Design Pattern
1、概述
2、创建型模式
2.1 单例模式
2.2 工厂模式
3、结构型模式
3.1 代理模式
3.2 装饰器模式
4、行为型模式
4.1 策略模式
-
+
游客
注册
登录
装饰器模式
## 1 含义 1. 装饰器模式又称为**包装模式**,**主要以对客户端透明的方式**,**在不改变对象结构的情况下**,**可以动态地扩展器功能**。 > 不同于继承,组合可以在运行时进行,所以称之为「动态扩展」,比如在按钮点击时进行一些 log 日志的打印,在绘制 text 文本框时,额外绘制一个滚动条和边框等。 > 2. 装饰器模式**是继承关系的一个替代方案**,**可以在不使用创造更多子类的情况下**,**扩展对象的功能**,**就增加功能来说**,**相比生成子类更加灵活**。 > 组合比继承更加灵活是因为当可拓展的功能很多时,继承方案会产生大量的子类,而组合可以提前写好处理函数,在需要时动态构造,显然更加灵活。 > 3. 具体的示例如下: 1. **相框**: 1. 照片 + 相框 = 带相框的照片,这背后就是一种装饰器模式,**照片具有看的功能**,**相框具有装饰功能**,**在我们看照片的基础上**,**还能看到精心设计的相框**,**增加了美感**,**同时相框还可以增加照片的保存时间与安全性**。 2. **相框与照片是一种组合关系**,**任何照片都可以放到相框中**,**而不是每个照片生成一个特定的相框**,显然,**组合的方式更加灵活**。 2. **带有缓存的文件读写**: 1. 假如我们**有一个类 `FileIO` 用来读写文件**,但是**没有缓存能力**,一眼看上去好像新建一个`CachedFileIO` 用起来更加方便,而`CachedIO` 的用法是`new CachedIO(new FileIO())` 稍微麻烦些。 2. 但如果我们增加一个网络读写类`NetworkIO`,一个数据库读写类`DBIO`,此时,**继承的方式会使子类数量急速膨胀** ,而**组合的方式则非常灵活**,**生成一个支持缓存的网络读写器**,**只需要 `new CachedIO(new NetworkIO())` 即可**,**这就是组合灵活的地方**。 3. 当然,为了实现这个能力,`CachedIO`**需要与 `FileIO`、`CachedFileIO`、`CachedIO` 继承自同一个类**,**具备相同的接口**。 3. **搭建平台的组件 `wrapper`**: 1. 装饰器模式别名也叫`wrapper`,`wrapper` 也经常在前端搭建场景中遇到,当**搭建平台加载一个组件**时,**希望拓展其基础能力**,一般**会使用 `wrapper` 层对组件进行嵌套**,`wrapper` 层就是**在不改变 API 的基础上**,**对第三方组件进行增强**。 ## 2 结构 ![](/media/202108/2021-08-28_1117090.9232866764719614.png) 1. **抽象构件**(Component)**角色**:**定义一个抽象接口以规范准备接收扩展功能的目标对象**。 2. **具体构件**(ConrecteComponent)**角色**:**实现抽象构件**,**负责具体的行为实现**。 3. **抽象装饰**(Decorator)**角色**:**实现抽象构件**,**并包含具体构件的实例**,**可以通过其子类扩展具体构件的功能**。 4. **具体装饰**(ConcreteDecoratorA 和 ConcreteDecoratorB)**角色**:**继承抽象装饰角色**,**传入具体构件对象给到父类**,**重写抽象构件的方法**,**在重写的方法里面给具体构件对象添加附加的责任**。 ## 3 示例 1. 现在有这么一个场景: 1. 有一批厨师,全是中国厨师,他们有一个共同的动作是做完饭。 2. 这批厨师做晚饭前的习惯不同,有些人喜欢做晚饭前洗手,有些人喜欢做晚饭前洗头。 2. 那么,按照装饰器模式,先**定义抽象构件角色** `Cook`: ```java public interface Cook { /** * 做晚饭 */ public void cookDinner(); } ``` 3. 然后**创建具体构件角色 `ChineseCook`**: ```java public class ChineseCook implements Cook{ /** * 做晚饭 */ @Override public void cookDinner() { System.out.println("中国人做晚饭"); } } ``` 4. **定义一个抽象装饰角色 `FilterCook`**,**实现 `Cook` 接口并持有 `Cook` 引用**: ```java public abstract class FilterCook implements Cook{ /** * 厨师 */ protected Cook cook; } ``` 5. **定义具体装饰角色 `WashHandsCook` 和 `WashHearCook`:** ```java public class WashHandsCook extends FilterCook{ public WashHandsCook(Cook cook) { this.cook = cook; } /** * 做晚饭 */ @Override public void cookDinner() { washHand(); this.cook.cookDinner(); } /** * 洗手「方法拓展」 */ private void washHand() { System.out.println("洗手"); } } ``` ```java public class WashHearCook extends FilterCook{ public WashHearCook(Cook cook) { this.cook = cook; } /** * 做晚饭 */ @Override public void cookDinner() { washHear(); this.cook.cookDinner(); } /** * 洗头「方法拓展」 */ private void washHear() { System.out.println("洗头"); } } ``` 6. **定义一个客户端** `Client`,**对装饰器进行调用**: ```java public class Client { public static void main(String[] args) { Cook cook1 = new WashHandsCook(new ChineseCook()); Cook cook2 = new WashHearCook(new ChineseCook()); cook1.cookDinner(); cook2.cookDinner(); } } ``` ## 4 优缺点 ### 4.1 优点 1. 装饰器模式与继承关系的目的都是要**扩展对象的功能**,但是**装饰器模式可以提供比继承更多的灵活性**,**允许系统动态决定贴上一个需要的装饰**,**或者除掉一个不需要的装饰**,**继承关系是静态的**,**在系统运行前就确定了**。 2. **通过使用装饰类及这些装饰类的排列组合**,**可以实现不同的效果**,排列组合是指**在有多个装饰器时**,**按排列组合**,**将具体装饰器作为其他装饰器的构造方法入参**(当做具体部件),**组成装饰器嵌套**,**实现复杂的附加功能**。 ### 4.2 缺点 1. 装饰器的问题也是组合的问题,过多的组合会导致: 1. **组合过程的复杂**,**要生成过多的对象**。 2. **包装器层次增多**,**会增加调试成本**,**我们比较难追溯到一个 `bug` 是在哪一层包装导致**。 ## 5 适用场景 1. **需要扩展一个现有类的功能**,**或给一个类增加附加责任**,**而又不能采用生成子类的方法进行扩充时**,例如,该类被隐藏,或者该类是终极类,或者采用继承方式会产生大量子类。 2. **需要通过对现有的一组基本功能进行排列组合而产生非常大量的功能**,**采用继承关系很难实现**,**而采用装饰器模式又很好实现**。 3. **需要动态地给一个对象增加功能**,**可以再动态地撤销这些功能**。 ## 6 典型应用 ### 6.1 在 JDK 中的应用 1. 装饰器模式**在 Java I/O 标准库**(`java.io`)**包中广泛应用**,例如,**基于字节流的 `InputStream/OutputStream` 和基于字符的 `Reader/Writer` 体系**: 1. `InputStream` 的子类`FilterInputStream`。 2. `OutputStream` 的子类`FilterOutputStream`。 3. `Reader` 的子类`BufferedReader` 以及`FilterReader`。 4. `Writer` 的子类`BufferedWriter`、`FilterWriter` 以及`PrintWriter`。 2. 以 `InputStream` 为例,`InputStream`**是所有字节输入流的基类**,**其下有众多的子类**,如基于文件的 `FileInputStream`、基于对象的 `ObjectInputStream`、基于字节数组的 `ByteArrayInputStream` 等,**有些时候**,**需要为这些流加一些其他的小特性**,**如缓冲**、**压缩**,**用装饰器模式就十分方便**,相关的部分类图如下所示: ![](/media/202108/2021-08-28_154001_076483.png) 1. **抽象构件**:`InputStream`。 2. **具体构件**:`FileInputStream`、`ObjectInputStream`。 3. **抽象装饰器**:`FilterInputStream`,**继承抽象构件 `InputTsream`**,**构造方法传入具体构件对象**。 4. **具体装饰器**:`FilterInputStream`**的所有子类**,如`BufferedInputStream`。 ## 参考文献 1. [Java 设计模式 12:装饰器模式](https://www.cnblogs.com/xrq730/p/4908940.html)。 2. [Decorator(装饰器模式)](https://github.com/ascoders/weekly/blob/master/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/175.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Decorator%20%E8%A3%85%E9%A5%B0%E5%99%A8%E6%A8%A1%E5%BC%8F%E3%80%8B.md)。 3. [Java 设计模式----装饰器模式](https://codingdict.com/blog/309)。 4. [浅谈设计模式 - 装饰器模式(五)](https://segmentfault.com/a/1190000039193894)。 5. [设计模式之装饰器模式](https://zhuanlan.zhihu.com/p/25003369)。 6. [设计模式(十六):装饰器模式(Decorator)](http://www.gxitsky.com/article/1614074479186056)。
ricear
2021年8月28日 15:48
©
BY-NC-ND(4.0)
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码