Fork me on GitHub

【趣味设计模式系列】之【装饰器模式】

 


1. 简介

装饰器模式(Decorator Pattern):动态地给一个对象添加职责,就增加功能来说,装饰器比生成子类更灵活。

2. 示例

水果店需要给网上客户发货,除了包装之外,需要对特定水果包装加额外装饰,比如加防伪标志、加固、加急等额外功能,但在外部看来还是打包组件。

类图设计

水果包装接口类Bag,接口方法pack,完成水果打包。

package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:13 * @Desc: 包装接口 */ public interface Bag { void pack(); }

苹果、橘子、香蕉各自实现包装接口,用自己的特定的外包装。

package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:15 * @Desc: 苹果包装类 */ public class AppleBag implements Bag { @Override public void pack() { System.out.println("苹果使用纸箱包装"); } }
package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:15 * @Desc: 橘子包装类 */ public class OrangeBag implements Bag { @Override public void pack() { System.out.println("橘子使用网兜包装"); } }
package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:15 * @Desc: 香蕉包装类 */ public class BananaBag implements Bag { @Override public void pack() { System.out.println("香蕉使用竹箩包装"); } }

装饰器类BagDecorator,实现包装类Bag接口,同时拥有包装接口的引用,为了组装更多具体装饰器加入进来,增加包装类的装饰功能。

package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:23 * @Desc: 装饰器类 */ public class BagDecorator implements Bag { private Bag bag; //维持一个对抽象构件对象的引用 public BagDecorator(Bag bag) //注入一个抽象构件类型的对象 { this.bag = bag; } public void pack() { bag.pack(); } }

防伪装饰器CheckedBagDecorator,继承BagDecorator类,并增加自己的防伪标识方法checked。

package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:32 * @Desc: 防伪装饰器 */ public class CheckedBagDecorator extends BagDecorator { public CheckedBagDecorator(Bag bag) { super(bag); } @Override public void pack() { super.pack(); checked(); //打印防伪标识 } //增加防伪标识 public void checked() { System.out.println("==============="); System.out.println("打印上防伪标识"); } }

加固装饰器ReinforceBagDecorator,继承BagDecorator类,并增加自己的加固方法reinforce。

package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:34 * @Desc: 加固装饰器 */ public class ReinforceBagDecorator extends BagDecorator{ public ReinforceBagDecorator(Bag bag) { super(bag); } public void pack() { super.pack(); //调用原有业务方法 reinforce(); } //加固包装 public void reinforce() { System.out.println("==============="); System.out.println("加固了包装"); } }

加急装饰器SpeedBagDecorator,继承BagDecorator类,并增加自己的加急方法speedy。

package com.wzj.decorator; /** * @Author: wzj * @Date: 2020/9/8 10:34 * @Desc: 加急装饰器 */ public class SpeedBagDecorator extends BagDecorator { public SpeedBagDecorator(Bag bag) { super(bag); } public void pack() { super.pack(); //调用原有业务方法 speedy(); } //快件加急 public void speedy() { System.out.println("==============="); System.out.println("打上加急标识"); } }

客户端类

package com.wzj.decorator; import org.aspectj.weaver.ast.Or; /** * @Author: wzj * @Date: 2020/9/8 10:40 * @Desc: */ public class Client { public static void main(String[] args) { AppleBag appleBag = new AppleBag(); OrangeBag orangeBag = new OrangeBag(); BananaBag bananaBag = new BananaBag(); // 苹果纸箱包装后,外加防伪标识、加固包装 new ReinforceBagDecorator(new CheckedBagDecorator(appleBag)).pack(); System.out.println("*********************************"); // 橘子网兜包装后,外加防伪标识、加固、加急包装 new SpeedBagDecorator(new ReinforceBagDecorator(new CheckedBagDecorator(orangeBag))).pack(); } }

结果

苹果使用纸箱包装 =============== 打印上防伪标识 =============== 加固了包装 ********************************* 橘子使用网兜包装 =============== 打印上防伪标识 =============== 加固了包装 =============== 打上加急标识

从上述例子可以看出,装饰器的好处,不仅可以对具体的水果包装类进行装饰,多个装饰器还可以嵌套装饰,非常灵活,这也是为什么,装饰器中需要引用Bag类,就是方便嵌套,因为每个具体的装饰器,本身也是Bag的子类。

3. 源码分析

Java IO类库非常庞大,从大类分,如果从按流的方向来分的话,分为输入流InputStream,输出流OutputStream,如果按照读取的方式分的话,分为字节流与字符流,具体如下图

针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类,如下图

OutputStream 是一个抽象类,FileOutputStream 是专门用来写文件流的子类,FilterOutputStream很特殊,它实现了OutputStream,同时持有OutputStream的引用,部分源码如下图:

public class FilterInputStream extends InputStream { /** * The input stream to be filtered. */ protected volatile InputStream in; ...

所以FilterOutputStream在设计的时候本质就是一个装饰器,其子类BufferedOutputStream,DataOutputStream,PrintStream都是具体的装饰器,实现额外的功能。
同样FilterInputStream、InputStreamReader、OutputStreamWriter都是装饰器类,其子类充当具体装饰的功能。
在平时写代码的时候都有如下几行嵌套的写法:

File f = new File("c:/work/test.data"); FileOutputStream fos = new FileOutputStream(f); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(osw); bw.write("https://githup.com"); bw.flush(); bw.close();

本质上,OutputStream的实现类可以与具体装饰器实现相互嵌套,具体的装饰器之间也可以相互嵌套,非常灵活,避免了独立为每个类创建子类而产生累爆炸的不合理设计。

4. 总结

4.1 优点

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 处理可以撤销的职责。
  • 扩展子类灵活,避免产生累爆炸。

4.2 缺点

  • 如果最里面的装饰器出错,需要从外面一层一层往里面去查看,多层嵌套导致增加复杂性。

__EOF__

本文作者小猪爸爸
本文链接https://www.cnblogs.com/father-of-little-pig/p/13622216.html
关于博主:不要为了技术而技术,总结分享技术,感恩点滴生活!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   小猪爸爸  阅读(358)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示