装饰模式(装饰设计模式)详解

上班族大多都有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。

在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。

装饰模式的定义与特点

装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

装饰(Decorator)模式的主要优点有:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则

其主要缺点是:

​ 装饰模式会增加许多子类,过度使用会增加程序得复杂性。

装饰模式的结构与实现

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。下面来分析其基本结构和实现方法。

1. 模式的结构

装饰模式主要包含以下角色。

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

装饰模式的结构图如图 1 所示。


图1 装饰模式的结构图

2. 模式的实现

装饰模式的实现代码如下:

package decorator;public class DecoratorPattern {public static void main(String[] args) {Component p = new ConcreteComponent();p.operation();System.out.println("---------------------------------");Component d = new ConcreteDecorator(p);d.operation();}
}//抽象构件角色
interface Component {public void operation();
}//具体构件角色
class ConcreteComponent implements Component {public ConcreteComponent() {System.out.println("创建具体构件角色");}public void operation() {System.out.println("调用具体构件角色的方法operation()");}
}//抽象装饰角色
class Decorator implements Component {private Component component;public Decorator(Component component) {this.component = component;}public void operation() {component.operation();}
}//具体装饰角色
class ConcreteDecorator extends Decorator {public ConcreteDecorator(Component component) {super(component);}public void operation() {super.operation();addedFunction();}public void addedFunction() {System.out.println("为具体构件角色增加额外的功能addedFunction()");}
}

程序运行结果如下:

创建具体构件角色
调用具体构件角色的方法operation()
---------------------------------
调用具体构件角色的方法operation()
为具体构件角色增加额外的功能addedFunction()

装饰模式的应用实例

【例1】用装饰模式实现游戏角色“莫莉卡·安斯兰”的变身。

分析:在《恶魔战士》中,游戏角色“莫莉卡·安斯兰”的原身是一个可爱少女,但当她变身时,会变成头顶及背部延伸出蝙蝠状飞翼的女妖,当然她还可以变为穿着漂亮外衣的少女。这些都可用装饰模式来实现,在本实例中的“莫莉卡”原身有 setImage(String t) 方法决定其显示方式,而其 变身“蝙蝠状女妖”和“着装少女”可以用 setChanger() 方法来改变其外观,原身与变身后的效果用 display() 方法来显示,图 2 所示是其结构图。


图2 游戏角色“莫莉卡·安斯兰”的结构图

程序代码如下:

package decorator;import java.awt.*;
import javax.swing.*;public class MorriganAensland {public static void main(String[] args) {Morrigan m0 = new original();m0.display();Morrigan m1 = new Succubus(m0);m1.display();Morrigan m2 = new Girl(m0);m2.display();}
}//抽象构件角色:莫莉卡
interface Morrigan {public void display();
}//具体构件角色:原身
class original extends JFrame implements Morrigan {private static final long serialVersionUID = 1L;private String t = "Morrigan0.jpg";public original() {super("《恶魔战士》中的莫莉卡·安斯兰");}public void setImage(String t) {this.t = t;}public void display() {this.setLayout(new FlowLayout());JLabel l1 = new JLabel(new ImageIcon("src/decorator/" + t));this.add(l1);this.pack();this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}
}//抽象装饰角色:变形
class Changer implements Morrigan {Morrigan m;public Changer(Morrigan m) {this.m = m;}public void display() {m.display();}
}//具体装饰角色:女妖class Succubus extends Changer {public Succubus(Morrigan m) {super(m);}public void display() {setChanger();super.display();}public void setChanger() {((original) super.m).setImage("Morrigan1.jpg");}
}//具体装饰角色:少女
class Girl extends Changer {public Girl(Morrigan m) {super(m);}public void display() {setChanger();super.display();}public void setChanger() {((original) super.m).setImage("Morrigan2.jpg");}
}

程序运行结果如图 3 所示。


图3 游戏角色“莫莉卡·安斯兰”的变身

装饰模式的应用场景

前面讲解了关于装饰模式的结构与特点,下面介绍其适用的应用场景,装饰模式通常在以下几种情况使用。

  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

装饰模式在 Java 语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。

下面代码是为 FileReader 增加缓冲区而采用的装饰类 BufferedReader 的例子:

BufferedReader in=new BufferedReader(new FileReader("filename.txtn));String s=in.readLine();

装饰模式的扩展

装饰模式所包含的 4 个角色不是任何时候都要存在的,在有些应用环境下模式是可以简化的,如以下两种情况。

(1) 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件,其结构图如图 4 所示。


图4 只有一个具体构件的装饰模式

(2) 如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并,其结构图如图 5 所示。


所包含的 4 个角色不是任何时候都要存在的,在有些应用环境下模式是可以简化的,如以下两种情况。

(1) 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件,其结构图如图 4 所示。

[外链图片转存中…(img-QT2YoKXa-1606732760147)]
图4 只有一个具体构件的装饰模式

(2) 如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并,其结构图如图 5 所示。

[外链图片转存中…(img-8sultfh1-1606732760149)]
图5 只有一个具体装饰的装饰模式

装饰模式(装饰设计模式)详解——小马同学@Tian相关推荐

  1. 责任链模式(职责链模式)详解——小马同学@Tian

    责任链模式(职责链模式)详解 在现实生活中,一个事件需要经过多个对象处理是很常见的场景.例如,采购审批流程.请假流程等.公司员工请假,可批假的领导有部门负责人.副总经理.总经理等,但每个领导能批准的天 ...

  2. Python 装饰器详解(上)

    Python 装饰器详解(上) 转自:https://blog.csdn.net/qq_27825451/article/details/84396970,博主仅对其中 demo 实现中不适合pyth ...

  3. 《设计模式详解》结构型模式 - 外观模式

    外观模式 5.5 外观模式 5.5.1 概述 5.5.2 结构 5.5.3 案例 5.5.4 使用场景 5.5.5 Tomcat 源码 完整的笔记目录:<设计模式详解>笔记目录,欢迎指点! ...

  4. 《设计模式详解》设计模式概述、UML

    <设计模式详解> 1.设计模式概述 1.1 软件设计模式的产生背景 1.2 软件设计模式的概念 1.3 学习设计模式的必要性 1.4 设计模式分类 2.UML 2.1 类图概述 2.2 类 ...

  5. 23种设计模式详解(代码讲解、持续更新)

    目录 设计模式分类 设计模式的六大原则 创建型模式 1.工厂方法模式(Factory Method) 2.建造者模式(Builder Pattern(常用.常见)) 行为型模式 模板模式(Templa ...

  6. Javascript常用的设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  7. 【深度】从朴素贝叶斯到维特比算法:详解隐马尔科夫模型

    详解隐马尔科夫模型 作者:David S. Batista 选自:机器之心 本文首先简要介绍朴素贝叶斯,再将其扩展到隐马尔科夫模型.我们不仅会讨论隐马尔科夫模型的基本原理,同时还从朴素贝叶斯的角度讨论 ...

  8. 《设计模式详解》笔记目录

    <设计模式详解> 前言 后记 目录 设计模式概述.UML 软件设计原则 创建型模式 单例模式 工厂模式 原型模式 建造者模式 结构型模式 代理模式 适配器模式 装饰者模式 桥接模式 外观模 ...

  9. 《设计模式详解》手写简单的 Spring 框架

    自定义 Spring 框架 自定义 Spring 框架 Spring 使用回顾 Spring 核心功能结构 bean 概述 Spring IOC 相关接口 BeanFactory 接口 BeanDef ...

最新文章

  1. 第八周实践项目10 稀疏矩阵的十字链表表示
  2. win32汇编处理字符消息和给常量区标号赋值
  3. Silverlight如何与JS相互调用
  4. c++/cli中调用C#类库中的out参数
  5. python自动抠头像图_Python实现AI自动抠图实例解析
  6. tornado学习笔记day01-高并发性能web框架
  7. 简单的控制台五子小游戏棋程序(Java)
  8. ES6 深拷贝_JS基本数据类型和引用数据类型的区别及深浅拷贝
  9. http协议,postget请求
  10. 分享一种很好看的字体
  11. 菁搜FTP搜索引擎 photo2
  12. 学习计算机英语的重要性
  13. Elasticsearch版本客户端与服务端版本不一致问题
  14. 位掩码(BitMask)——介绍与使用
  15. 由三个点坐标判断三个点能否组成三角形模板(自用,客官可取)
  16. html中名词解释,HTML相关名词解释
  17. 论一个程序猿的自我修养!
  18. Win2016 安装及配置 + 存储卷管理
  19. HTML 6种空格nbsp;ensp;emsp;thinsp;zwnj;zwj;空白空格的区别
  20. mac安装MongoDB与启动

热门文章

  1. 时光轴三之 ExpandableListView版时光轴效果
  2. java集合类构造及原理
  3. Windows 安装Java (官网下载Java)
  4. 深度学习与自然语言处理教程(6) - 神经机器翻译、seq2seq与注意力机制(NLP通关指南·完结)
  5. 数据库查询优化的方式
  6. 非易失性存储器的分类和未来发展预测
  7. 通用人工智能时代即将来临?
  8. 在外部DDR3内存中运行的MicroBlaze程序的固化方法
  9. Day1 Java简介
  10. 数字身份 和 iot_数字身份的过去和现在