1、意图
动态给一个对象添加一些额外的职责。比如增加功能,装饰模式相比生成子类更为灵活。该模式以对客户端透明的方式扩展对象的功能。
2、适用场景
(1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;
(2)当不能采用继承方式对系统进行扩展或者采用继承不利于系统的扩展和维护时,第一种情况,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈现爆炸式增长;另外一种情况可能是因为类的定义被隐藏,如@Hide注释的类,或者定义的类不能生成子类,如final修饰的类;
(3)需要动态地给某个对象添加功能,这些功能也可以动态地撤销。
3、类图分析
- Component:定义一个对象接口,可以给这些对象动态地添加职责。
- ConcreteComponent:定义一个对象,可以给这个对象添加一下职责。
- Decorator:维持一个指向Component对象的引用。一般也会直接实现Component接口,以达到相同父类型的目的。
- ConcreteDecorator:向组件添加职责。
4、代码示例
1 /** 2 * @author it-小林 3 * @desc 被修饰的类的抽象接口 4 * @date 2021年07月28日 11:28 5 */ 6 public interface People { 7 public void run(); 8 }
/** * @author it-小林 * @desc 被修饰的类 * @date 2021年07月28日 11:28 */ public class Man implements People{ @Override public void run() { System.out.println("人会跑步"); } }
1 /** 2 * @author it-小林 3 * @desc 装饰的抽象类 4 * @date 2021年07月28日 11:29 5 */ 6 public class AbstractDecorator implements People{ 7 //持有被装饰类的引用 8 private People people; 9 10 //构造函数注入被修饰者 11 12 13 public AbstractDecorator(People people) { 14 this.people = people; 15 } 16 17 @Override 18 public void run() { 19 people.run(); 20 } 21 }
1 /** 2 * @author it-小林 3 * @desc 装饰类 4 * @date 2021年07月28日 11:33 5 */ 6 public class ManDecorator extends AbstractDecorator{ 7 8 public ManDecorator(People people) { 9 super(people); 10 } 11 12 //装饰类增加的功能 13 private void sing(){ 14 System.out.println("人在唱歌"); 15 } 16 17 @Override 18 public void run() { 19 super.run(); 20 sing(); 21 } 22 }
1 /** 2 * @author it-小林 3 * @desc 4 * @date 2021年07月28日 11:35 5 */ 6 public class Client { 7 public static void main(String[] args) { 8 //创建被修饰的类 9 People man = new Man(); 10 11 //创建修饰的类,并添加被修饰类的引用 12 People superMan = new ManDecorator(man); 13 14 //执行增强后的run方法 15 superMan.run(); 16 17 } 18 }
5、优缺点
优点:
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合;
- Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
缺点:
- 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择;
- 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂;
- 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
6、总结
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案;
- 组合和委托可用于在运行时动态地加上新的行为;
- 在我们的设计中,应该允许行为可以被扩展,而不须修改现有的代码;
- 装饰者模式意味着一群装饰者类, 这些类用来包装具体组件;
- 装饰者一般对组建的客户是透明的,除非客户程序依赖于组件的具体类型;
- 可以有无数个装饰者包装一个组件;
- 装饰者类反映出被装饰的组件类型(实际上,他们具有相同的类型,都经过接口或继承实现);
- 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
7、模式对比
- 适配器(Adapter)模式:装饰模式不同于适配器模式,适配器模式会给对象一个全新的接口,而装饰模式是改变对象的职责,并不会改变对象接口;
- 组合(Composite)模式:可以将装饰装饰模式视为一个退化的、仅有一个组件的组合(示例中调料类保存了一个咖啡的引用)。然而,装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集;
- 策略(Strategy)模式:装饰模式主要改变的对象的外表;而策略模式重点在于改变对象的内核。它们是改变对象的两种途径。