系列文章目录

肝一肝设计模式【一】-- 单例模式 传送门
肝一肝设计模式【二】-- 工厂模式 传送门
肝一肝设计模式【三】-- 原型模式 传送门
肝一肝设计模式【四】-- 建造者模式 传送门
肝一肝设计模式【五】-- 适配器模式 传送门
肝一肝设计模式【六】-- 装饰器模式 传送门
肝一肝设计模式【七】-- 代理模式 传送门


文章目录

  • 系列文章目录
  • 前言
  • 一、什么是装饰器模式
  • 二、装饰器模式中的角色
  • 三、举个栗子
  • 四、在JDK中的应用
  • 写在最后

前言

我们知道设计模式可以分为三大类:创建型模式、结构型模式、行为型模式。上一节我们分析了结构型模式中的适配器模式,这一节我们继续分析另外一种结构型模式——装饰器模式


一、什么是装饰器模式

装饰器模式(Decorator Pattern),它允许在不改变已有对象的基础上动态地扩展其功能。通过将对象包装在装饰器对象中,可以在运行时为对象添加额外的行为,就增加功能来说,装饰器模式比生成子类更灵活。

装饰器模式的关键是使用组合而非继承来实现功能的扩展。通过将对象包装在装饰器对象中,可以按需添加或修改对象的行为,而无需改变原始对象或其他代码。

二、装饰器模式中的角色

在装饰器模式中,有一个核心的接口或抽象类,代表要被装饰的对象。

然后,创建一个装饰器类,它实现了相同的接口或继承了相同的抽象类,并在构造函数中接受被装饰的对象作为参数。

装饰器类中包含一个与被装饰对象相同的成员变量,用于保存被装饰的对象。

在装饰器类中,可以调用被装饰对象的方法,并可以在方法调用前后添加额外的逻辑。

以下是装饰器模式的几个关键角色:

  • Component(抽象组件):定义了被装饰对象的接口或抽象类。
  • ConcreteComponent(具体组件):实现了组件接口或抽象类,是被装饰的对象。
  • Decorator(装饰器):实现了组件接口或抽象类,并在构造函数中接受被装饰对象。装饰器可以调用被装饰对象的方法,并可以添加额外的功能。
  • ConcreteDecorator(具体装饰器):扩展了装饰器类,实现了额外的功能。

三、举个栗子

说了这么多的概念,感觉懂了又好像没懂,那就必须举个栗子了,我在第一次了解装饰器模式的时候,第一时间就想到了玩一款RPG游戏,你可以为你的角色穿上装备,也可以不穿装备硬刚ヽ( ̄▽ ̄)ノ

首先创建一个基础的角色类(Character)作为抽象组件,并定义一个角色的基本操作:

public interface Character {void attack();
}

然后我们再建一个具体的角色类,比如新建一个战士(Warrior),实现角色接口:

public class Warrior implements Character {@Overridepublic void attack() {System.out.println("赤手空拳的攻击!");}
}

现在我们准备给我们的战士加点装备,比如使用武器或者戴上防具,这时就可以使用装饰器模式了。

首先先定义一个装饰器抽象类(Decorator),也实现了角色接口:

public abstract class Decorator implements Character {protected Character character;public Decorator(Character character) {this.character = character;}@Overridepublic void attack() {character.attack();}
}

然后,创建具体的装饰器类来实现附加功能。
比如,创建一个武器装饰器类(WeaponDecorator):

public class WeaponDecorator extends Decorator {public WeaponDecorator(Character character) {super(character);}@Overridepublic void attack() {super.attack();addWeapon();}private void addWeapon() {System.out.println("装上利维坦之斧削你!");}
}

再创建一个防具装饰器类(ArmorDecorator):守护者之盾

public class ArmorDecorator extends Decorator {public ArmorDecorator(Character character) {super(character);}@Overridepublic void attack() {super.attack();addArmor();}private void addArmor() {System.out.println("戴上守护者之盾我防!");}
}

测试一下

public class DecoratorClient {public static void main(String[] args) {Character warrior = new Warrior();warrior.attack();  // 输出:赤手空拳的攻击!Character warriorWithWeapon = new WeaponDecorator(warrior);warriorWithWeapon.attack();  // 输出:赤手空拳的攻击!装上利维坦之斧削你!Character warriorWithWeaponAndArmor = new ArmorDecorator(warriorWithWeapon);warriorWithWeaponAndArmor.attack();  // 输出:赤手空拳的攻击!装上利维坦之斧削你!戴上守护者之盾我防!}
}

通过装饰器模式,我们可以动态地为玩具对象添加不同的装饰器,以实现不同的附加功能,而无需修改原有的玩具类或客户端代码。这种方式可以灵活地组合和扩展功能,同时保持代码的清晰

四、在JDK中的应用

JDK中使用装饰器模式的地方,一个典型的例子就是Java I/O库中使用 BufferedInputStreamBufferedOutputStream 类对输入流和输出流进行包装和装饰。

简单示例:

public class DecoratorClient {public static void main(String[] args) {try {FileInputStream fileInputStream = new FileInputStream("input.txt");FileOutputStream fileOutputStream = new FileOutputStream("output.txt");// 使用装饰器对输入流进行包装和装饰BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);// 使用装饰器对输出流进行包装和装饰BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);// 读取和写入数据int data;while ((data = bufferedInputStream.read()) != -1) {bufferedOutputStream.write(data);}// 关闭流bufferedInputStream.close();bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}
}

在示例当中,我们使用了 FileInputStreamFileOutputStream 来创建原始的输入流和输出流。然后,我们分别使用 BufferedInputStreamBufferedOutputStream 对这些原始流进行装饰。

BufferedInputStream为例,看一下源码摘要(省略了一些方法):

public class BufferedInputStream extends FilterInputStream {// ...public BufferedInputStream(InputStream in) {super(in);// ...}// ...
}

在上述代码中,BufferedInputStream 继承了 FilterInputStream 类,FilterInputStream 本身是一个抽象类,也是装饰器类。FilterInputStream 继承了 InputStream 抽象类,从而与原始的输入流进行连接。

构造函数 BufferedInputStream(InputStream in) 接受一个原始的输入流对象作为参数,并通过调用 super(in) 将其传递给父类 FilterInputStream 的构造函数。

通过这样的继承关系和构造函数的调用,BufferedInputStream 实际上是一个装饰器类,它可以包装和装饰任何实现了 InputStream 接口的输入流对象。

BufferedInputStream 通过添加缓冲功能来装饰输入流。它维护了一个内部缓冲区,通过调用原始输入流的 read() 方法将数据从原始流读入到缓冲区中,并通过提供额外的方法(如 read(byte[] b, int off, int len))来从缓冲区中读取数据,以提高读取的性能。


写在最后

装饰器模式的优点:

  • 可以在不修改已有代码的情况下扩展对象的功能。
  • 允许多个装饰器按照一定顺序进行组合,以实现复杂的功能组合。
  • 装饰器与被装饰对象之间是松耦合的,可以独立地进行扩展和修改。

装饰器模式的缺点:

  • 会导致类的增多,增加了代码复杂性,过度使用装饰器模式也可能使代码难以理解和维护。

肝一肝设计模式【六】-- 装饰器模式相关推荐

  1. 设计模式学习----装饰器模式

    这两天本来是自在学习java collection Framework的Fail Fast底层机制,看到核心的部分时,突然意识到设计模式的问题,上大学到现在我还没有真正理解过设计模式的概念,于是用了大 ...

  2. 【设计模式】装饰器模式的使用

    问题来源 我们在进行软件系统设计的时候,有一些业务(如下图,一些通用的非功能性需求)是多个模块都需要的,是跨越模块的.把它们放到什么地方呢? 最简单的办法就是把这些通用模块的接口写好,让程序员在实现业 ...

  3. go设计模式之装饰器模式

    go设计模式之装饰器模式 再写这篇文章时,我已经看了很多其他人发表的类似文章,大概看了这么多吧. 亓斌的设计模式-装饰者模式(Go语言描述) jeanphorn的Golang设计模式之装饰模式 七八月 ...

  4. python中的装饰器、装饰器模式_python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

  5. python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

  6. 零基础学习设计模式之装饰器模式(配套视频)

    零基础学习设计模式之装饰器模式 定义 在不改变目标结构的情况下,动态的给对象增加功能 举例 如房子装修.相片加相框等,都是装饰器模式. 基本组件 抽象构件(Component)角色:定义一个抽象接口以 ...

  7. 详解设计模式:装饰器模式

    装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),是 GoF 的 23 种设计模式中的一种结构型设计模式.装饰器模式 是指在不改变原有对象的基础之上,将 ...

  8. javascript设计模式之装饰器模式(结构型模式)

    javascript设计模式之装饰器模式 js的设计模式分为创建型模式,结构型模式和行为模式 结构模式描述了如何组合对象以提供新的功能. 装饰器模式是一种常见的结构型模式,我们可以以一个基础对象为基础 ...

  9. 设计模式之装饰器模式详解

    设计模式之装饰器模式详解 文章目录 设计模式之装饰器模式详解 一.什么是装饰器模式 二.装饰器模式的角色组成 三.装饰器模式通用写法示例 四.装饰器模式业务中的应用举例 五.装饰器模式优缺点 一.什么 ...

  10. 【张六儿大话设计模式】——装饰器模式

    大话设计模式断了好久,正好最近在研究无埋点技术接触到了ASM操作字节码,也就随之接触到了装饰器模式,于是仔细的学习了一下. 张六儿是一个网瘾少年,最喜欢玩的游戏就是Dota2了,众所周知Dota2是一 ...

最新文章

  1. Java Web整合开发读书笔记
  2. linux shell 字符串操作(长度,查找,替换)详解
  3. 求助大神!怎样除去XML节点反复的值的数据
  4. 为什么说 TCP/IP 是一个不确定性网络
  5. python用哪个软件好-4个备受欢迎的Python程序库 你用哪个?
  6. 什么是PyTorch?
  7. left edge algorithm.
  8. 关于sha1加密的一个问题。。。。
  9. android item 点击 获取position,Android ListView 子控件onClick正确获取position的方法
  10. 过渡效果_(新)61种数字胶动态过渡延时摄影效果转场 WIPE amp; LIGHT TRANSITIONS(3462)...
  11. DingTalk机器人C#代码
  12. mysql视图高峰事务_MySQL-视图与事务「程序员培养之路第二十六天」
  13. linux系统导航怎么刷安卓系统升级,4s送的10.2寸安卓导航刷机教程1
  14. Ubuntu搭建FTP服务器
  15. Sublime中文显示乱码
  16. 批处理 获取计算机硬件信息,检测硬件的批处理命令,检测硬件bat,一键获取电脑硬件信息...
  17. 关于实习、校招的科普
  18. Django建立一个音乐网站(一)
  19. FACIAL: Synthesizing Dynamic Talking Face with Implicit Attribute Learning(论文翻译)
  20. Raft 一致性算法论文

热门文章

  1. codeforces Round 314 div.2
  2. Oracle报错:ORA-01722-无效数字
  3. HCIA学习笔记day2---交换机入门知识1
  4. 2021年中国苹果行业产业链分析:上下游市场稳定,苹果行业市场运行情况平稳增长 [图]
  5. Windows 2000/XP 系统对U盘的处理过程入手,逆向彻底消除U盘使用痕迹
  6. 学计算机用微软笔记本可以吗,想选个能陪伴大学四年的笔记本?Surface Laptop 3别错过...
  7. Node 最老 npm 包 request 将废弃;中国首条 3D 刷脸乘车地铁开通
  8. 浪潮之巅第三章 — “水果”公司的复兴 (乔布斯和苹果公司)
  9. 自然几何之分形(1)
  10. oracle定时任务中的时间设置