前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >漫谈模式之装饰者模式

漫谈模式之装饰者模式

原创
作者头像
孟君
发布2023-03-15 06:11:08
4520
发布2023-03-15 06:11:08
举报
文章被收录于专栏:孟君的编程札记

今天,我们来分享结构型模式的另外一个成员:装饰者模式。

装饰者模式是一种对象结构型模式,它能动态地给一个对象添加一些额外的职责

在开始本文介绍之前,我们先来感受一下几个装饰者模式的场景。

场景1、孙悟空七十二般变化

小时候看西游记,有一幕是孙悟空和二郎神的追逐戏,两者展示了多种变化的本领,让人印象深刻。他们可以变成鸟在天上飞,可以变成鱼在水里游... .... 每次变化后,其就有一种新的本领。现在想想,这种场景不就是装饰者模式的一个例子吗

图片
图片

场景2、IO流

大家对文件流的操作很熟悉。比如,

代码语言:javascript
复制
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者调用的示例:

齐天大圣:

图片
图片

读文件:

代码语言:javascript
复制
new BufferedReader(new FileReader("F:\\test.txt"));

现在是不是更加有体感、更加熟悉?

优点

  • 装饰模式与继承关系的目的都是扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态地决定“贴上”一个需要的“装饰”。而继承关系则不同,继承关系是静态的,它在系统运行前就决定了,运行时是不能改变的。
  • 装饰者模式不试图在一个复杂的可定制的类中支持所有可预见的特性。相反你可以定义一个简单的类,并且使用Decorator类给它逐渐的添加功能。可以从一个简单的部件组合出复杂的功能。可以创造出出很多不同行为的组合。

缺点

使用装饰模式会产生比使用继承关系更多的对象,这些对象看上去类似。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

装饰者模式 vs. 继承

区别:

  • 装饰器模式可以在运行时动态地添加或修改对象的行为,而不需要修改原始对象或其类的代码。继承则需要在编译时确定,因为继承关系是在类定义时就确定的,是静态的。
  • 装饰器模式允许您将多个装饰器堆叠在一起,以添加多个不同的行为。继承则只允许您从一个父类继承行为。比如,
图片
图片
  • 使用装饰器模式可以避免创建许多子类来实现不同的变体,因为可以在运行时动态地添加或修改行为。相比之下,使用继承可能会导致类层次结构变得过于复杂,并且可能需要创建大量的子类来实现不同的变体

适合场景:

在需要在运行时动态添加或修改对象行为的情况下,或者需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实的情况下,装饰器模式可能更加适用。

而在需要创建具有类似但略有不同行为的多个类时,继承可能更加适用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 装饰者模式基本介绍
  • 装饰者模式示例
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档