今天,我们来分享结构型模式的另外一个成员:装饰者模式。
装饰者模式是一种对象结构型模式,它能动态地给一个对象添加一些额外的职责。
在开始本文介绍之前,我们先来感受一下几个装饰者模式的场景。
场景1、孙悟空七十二般变化
小时候看西游记,有一幕是孙悟空和二郎神的追逐戏,两者展示了多种变化的本领,让人印象深刻。他们可以变成鸟在天上飞,可以变成鱼在水里游... .... 每次变化后,其就有一种新的本领。现在想想,这种场景不就是装饰者模式的一个例子吗
。
场景2、IO流
大家对文件流的操作很熟悉。比如,
new BufferedReader(new FileReader("F:\\test.txt"));
I/O流的结构其实也是装饰者的场景。
我们看到在场景1和场景2中,我们发现一些共同点,要产生新特点的时候,采用将原来对象包起来。所以,装饰者模式也称包装模式。
接下来,我们就来看下装饰者模式的组成和示例吧,一起学习一下。
意图
动态地给一个对象添加额外的职责。
结构
装饰者模式的基本结构如下:
Component
定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent
定义一个对象,可以给这个对象添加一些职责
Decorator
装饰角色,持有一个Component对象的实例,并定义一个与Componnet接口一致的接口。
ConcreteDecorator
具体装饰角色,负责给组件添加职责。
在本示例中,我们先给出一个基本的最近最少使用的Cache实现,包含基本的get / put 以及remove等操作。然后,使用装饰者模式对其功能增强,例如添加过期时间、获取缓存命中率等等。
Cache接口
定义get / put / remove以及默认获取命中率的接口方法。
LRUCache实现
使用LinkedHashMap来实现一个最近最少使用的Cache存储实现。
抽象装饰者
定义一个抽象的缓存包装类,它实现了缓存接口并维护一个被包装对象的应用。
具体装饰者(过期时间)
一个过期时间的具体装饰。为了测试方便,过期时间的阈值设置为3000毫秒,即3秒。装饰器维护一个map,在put设值的时候,将当前key和时间戳存入map。在get获取缓存的时候,判断距离现在是否超过3秒,如果是清空缓存的值和时间戳,同时返回null值。这是一个很简单的实现。
具体装饰者(缓存命中率)
当然,也可以增加其它诸如同步缓存操作,缓存大小限制等装饰,如:
这里就不一一列举了。
测试一下
输出结果
如上图所示,程序先打印背景色标注部分的内容。命中3次,未命中1次,所以命中率为0.75。3100毫秒后,由于过期时间为3秒,所以获取key为user:1001的缓存信息时,比对时间超过3秒,删除缓存信息并返回null,也就是未命中。
所以结合前面命中情况,相当于5次命中3次,未命中2次,也就是输出0.6的命中率。
这样,一个对缓存进行功能增强的装饰器模式示例就完成了。
再回顾一下本文开头的2个场景的结构:
场景1、孙悟空变化
场景2、IO层次结构
再看下2者调用的示例:
齐天大圣:
读文件:
new BufferedReader(new FileReader("F:\\test.txt"));
现在是不是更加有体感、更加熟悉?
优点
缺点
使用装饰模式会产生比使用继承关系更多的对象,这些对象看上去类似。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
装饰者模式 vs. 继承
区别:
适合场景:
在需要在运行时动态添加或修改对象行为的情况下,或者需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实的情况下,装饰器模式可能更加适用。
而在需要创建具有类似但略有不同行为的多个类时,继承可能更加适用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。