-
2021-10-17 11:05:08
设计模式之装饰器模式详解
一、什么是装饰器模式
装饰器模式(Decorator Pattern) 也称为包装模式(Wrapper Pattern) 是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。
二、装饰器模式的角色组成
我们先来看下装饰器模式的通用类图:
- 抽象组件(Component): 可以是一个接口或者抽象类,其充当被装饰类的原始对象,规定了被装饰对象的行为;
- 具体组件(ConcreteComponent): 实现/继承Component的一个具体对象,也即被装饰对象;
- 抽象装饰器(Decorator): 通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个Component抽象组件,这是强制的通用行为(当然,如果系统中逻辑单一,并不需要实现许多装饰器,那么我们可以直接省略该类,而直接实现一个具体装饰器(ComcreteDecorator)即可);
- 具体装饰器(ConcreteDecorator): Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能;
三、装饰器模式示例
我们先创建一个抽象组件Component来规定被装饰对象的行为:
/** * 抽象组件 */ public abstract class Component { /** * 操作方法 */ public abstract void operation(); }
创建具体组件ConcreteComponent:
/** * 具体组件 */ public class ConcreteComponent extends Component { public void operation() { System.out.println("具体组件处理业务逻辑"); } }
创建一个抽象装饰器Decorator:
public abstract class Decorator extends Component { /** * 持有组件对象 */ protected Component component; /** * @param component 组件对象 */ public Decorator(Component component) { this.component = component; } public void operation() { //转发请求给组件对象,可以在转发前后执行一些附加动作 component.operation(); } }
创建两个具体装饰器ConcreteDecoratorA、ConcreteDecoratorB:
public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } //在调用父类的operation方法之前需要执行的操作 private void operationFirst() { System.out.println("ConcreteDecoratorA前置操作...."); } //在调用父类的operation方法之后需要执行的操作 private void operationLast() { System.out.println("ConcreteDecoratorA后置操作...."); } public void operation() { //调用父类的方法,可以在调用前后执行一些附加动作 operationFirst(); //添加的功能 //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能 super.operation(); operationLast(); //添加的功能 } }
public class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } //在调用父类的operation方法之前需要执行的操作 private void operationFirst() { System.out.println("ConcreteDecoratorB前置操作...."); } //在调用父类的operation方法之后需要执行的操作 private void operationLast() { System.out.println("ConcreteDecoratorB后置操作...."); } public void operation() { //调用父类的方法,可以在调用前后执行一些附加动作 operationFirst(); //添加的功能 //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能 super.operation(); operationLast(); //添加的功能 } }
编写测试类:
public class Client{ public static void main(String[] args){ //首先创建需要被装饰的原始对象(即要被装饰的对象) Component component = new ConcreteComponent (); //给对象透明的增加功能A并调用 Decorator decoratorA = new ConcreteDecoratorA(component); decoratorA.operation(); System.out.println("================================================"); //给对象透明的增加功能B并调用 Decorator decoratorB = new ConcreteDecoratorB(component); decoratorB.operation(); System.out.println("================================================"); //装饰器也可以装饰具体的装饰对象,此时相当于给对象在增加A的功能基础上在添加功能B Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA); decoratorBandA.operation(); } }
代码测试,控制台输出:
四、装饰器模式优缺点
- 优点
- 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用
- 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
- 装饰器完全遵守开闭原则
- 缺点
- 从代码层面来看,使用装饰器模式会出现更多的代码,更多的类,增加程序复杂性
- 动态装饰时,多层装饰时会更复杂
更多相关内容 -
java 中设计模式(装饰设计模式)的实例详解
2020-08-29 08:11:16主要介绍了java 中设计模式(装饰设计模式)的实例详解的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下 -
js装饰设计模式学习心得
2020-11-30 00:14:36装饰设计模式 每种设都有其独特的应用场景和解决问题的方式, 装饰设计模式是动态的为对象添加新的功能, 是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,... -
黑马程序员:java基础之装饰设计模式
2020-12-22 23:26:58一、什么是装饰设计模式 当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的类成为装饰类。 装饰类通常会通过构造方法,传递进来一个需要装饰的... -
装饰模式(装饰设计模式)详解——小马同学@Tian
2020-11-30 18:39:47装饰模式(装饰设计模式)详解 上班族大多都有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是...装饰模式(装饰设计模式)详解
上班族大多都有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。
在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。
装饰模式的定义与特点
装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
装饰(Decorator)模式的主要优点有:
- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
其主要缺点是:
装饰模式会增加许多子类,过度使用会增加程序得复杂性。
装饰模式的结构与实现
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。下面来分析其基本结构和实现方法。
1. 模式的结构
装饰模式主要包含以下角色。
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(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 只有一个具体装饰的装饰模式 -
javascript设计模式之装饰者模式
2020-10-15 17:53:24主要为大家详细介绍了javascript设计模式之装饰者模式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
装饰设计模式——RecyclerView添加头部和底部-附件资源
2021-03-02 15:09:13装饰设计模式——RecyclerView添加头部和底部-附件资源 -
C#设计模式_设计模式_C#_
2021-10-01 14:40:42装饰模式(Decorator Pattern) 9. 组合模式(Composite Pattern) 10. 外观模式(Facade Pattern) 11. 享元模式(Flyweight Pattern) 12. 代理模式(Proxy Pattern) 行为型: 13. 模板方法(Template Method) 14. 命令... -
Java设计模式-装饰者模式
2020-12-21 13:08:08Java设计模式-装饰者模式 装饰者模式是什么 装饰者模式是23中设计模式的一种。 从这个模式的名字“装饰者”可以看出,这个设计模式是基于已有的类进行设计的一种模式。 装饰者模式他是为了让已经存在的类的功能更多... -
Java设计模式之装饰模式原理与用法实例详解
2020-08-27 15:21:30主要介绍了Java设计模式之装饰模式原理与用法,结合实例形式详细分析了装饰模式的概念、原理、定义与使用方法,并总结分析了装饰模式的优缺点,具有一定参考借鉴价值,需要的朋友可以参考下 -
javascript设计模式 – 装饰模式原理与应用实例分析
2021-01-19 14:47:20本文实例讲述了javascript设计模式 – 装饰模式原理与应用。分享给大家供大家参考,具体如下: 介绍:装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为。在现实生活中,这种情况也到处存在,... -
设计模式课程设计(基于设计模式的绘图程序设计).zip
2021-07-27 09:12:53该系统是一个画图程序,我们要用设计模式的思想来设计系统结构,然后实现基本图形的绘制功能。 -
设计模式PPT
2019-08-08 16:35:04创建型模式用来处理对象的创建过程,主要包含以下5种设计模式: 工厂方法模式(Factory Method Pattern) 抽象工厂模式(Abstract Factory Pattern) 建造者模式(Builder Pattern) 原型模式... -
NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】
2020-10-19 06:47:41主要介绍了NodeJS设计模式,结合实例形式总结分析了nodejs单例模式,适配器模式,装饰模式,观察者模式的概念、原理与具体实现技巧,需要的朋友可以参考下 -
设计模式专题之(七)装饰模式---设计模式装饰模式示例代码(python--c++)
2018-09-15 16:36:59设计模式专题之(七)装饰模式---设计模式装饰模式示例代码(python--c++) -
设计模式--装饰者模式java例子
2017-02-06 17:56:45设计模式--装饰者模式java例子 -
【设计模式】装饰模式
2022-03-23 09:55:11学习于《大话设计模式》,本文讲解了装饰模式的概念,使用场景以及装饰模式的优点和缺点,并给出了一个实例和自己的想法装饰模式概念
属于结构型模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活
装饰模式是利用setComponent来对对象进行包装,这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰独享只关心自己的功能,不需要关心如何被添加到对象链当中。
如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类
装饰模式是为已有功能动态地添加更多功能的一种方式。装饰模式把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象。因此,当需要执行特殊行为时,客户端代码就可以在运行时根据需要有选择地按顺序地使用装饰功能包装对象。
优点
1.把类中的装饰功能从类中去除,简化原有的类
2.有效地把类的核心职责和装饰功能分开,还可去除相关类中重复的装饰逻辑缺点
1.多重装饰比较复杂
实例
Component
ConcreteComponent
Decorator
装饰抽象类中的操作方法中被调用的对象是我们setComponent方法传入的对象,在客户端我们通过setComponent方法传入一个正在被装饰的对象,当对上一个对象装饰完毕后,上一个对象便作为下一个装饰类setComponent的参数传入进行装饰,从而形成一条装饰对象链。
ConcreteDecoratorA
ConcreteDecoratorB
Client
首先用ConcreteComponent实例化对象,然后用A对象包装component对象,B对象再包装A对象,最后调用B对象的Operation方法。
结果
我的想法
其实我们可以把装饰者类和被装饰者类看成同一种类型的类,因为装饰者和被装饰者的实例都继承了Component,但是它们之间承担了不一样的任务,被装饰者类在代码中只能够被装饰,而装饰者类既可以装饰别人也可以被继续装饰。
从上面的例子我们可以看出装饰者中的Operation方法会调用父类的Operation方法,而父类的Operation方法则是调用setComponent传入的对象的Operation方法,也就是被装饰者中的Operation方法。依靠这条链式调用来不停装饰。
-
PHP设计模式之装饰器模式定义与用法详解
2021-01-20 01:30:10本文实例讲述了PHP设计模式之...有些设计设计模式包含一个抽象类,而且该抽象类还继承了另一个抽象类,这种设计模式为数不多,而装饰器就是其中之一. 什么时候使用装饰器模式 基本说来, 如果想为现有对象增加新功能而不 -
东北大学四种设计模式实验
2022-03-16 16:52:44实现单例模式,组合模式,Builder模式,装饰器模式,并使用设计模式实现赌场 -
装饰设计模式
2008-01-11 14:59:46java中的装饰设计模式 -
php设计模式之装饰模式应用案例详解
2021-01-20 07:57:40本文实例讲述了php设计模式之装饰模式。分享给大家供大家参考,具体如下: 介绍 装饰者模式(Decorator Pattern)允许你向一个现有的对象添加新的功能,同时又不改变其结构。 这种类型的设计模式属于结构型模式,它... -
C#面向对象设计模式纵横谈(视频与源码)
2021-05-08 14:16:38C#面向对象设计模式纵横谈(10):Decorator 装饰模式(结构型模式) C#面向对象设计模式纵横谈(11):Facade 外观模式(结构型模式) C#面向对象设计模式纵横谈(12):Flyweight 享元模式(结构型模式) C#面向对象设计... -
Java设计模式之装饰者模式详解和代码实例
2020-09-04 04:01:02主要介绍了Java设计模式之装饰者模式详解和代码实例,Decorator模式(别名Wrapper):动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案,需要的朋友可以参考下 -
Learning+PHP设计模式
2018-08-19 17:20:03第8章 装饰器设计模式 第4部分 行为型设计模式 第9章 模板方法模式 第10章 状态设计模式 第5部分 MySQL和PHP设计模式 第11章 通用类负责连接,代理模式保证安全 第12章 策略设计模式的灵活性 第13章 职责链... -
.NET设计模式(10):装饰模式(DecoratorPattern)
2021-03-03 21:42:29在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式...[GOF《设计模式》]图1Decorator模式结构图装饰模式动态地给一个对象添加额外的职责。不论一幅画有没有画 -
23种设计模式详细介绍与区别
2021-03-25 11:43:13主要介绍23种设计模式,以及个别设计模式的区别,文章查看地址:https://blog.csdn.net/wangjz2008/article/details/115203884