定义

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。

结构和说明

结构型模式-装饰模式 - 图1

示例代码

  1. public class DecoratorDemo {
  2. /**
  3. * 组件对象的接口,可以给这些对象动态添加职责
  4. */
  5. public static abstract class Compoent {
  6. /**
  7. * 示例方法
  8. */
  9. public abstract void operation();
  10. }
  11. /**
  12. * 具体实现组件对象接口的对象
  13. */
  14. public static class ConcreteCompoent extends Compoent {
  15. @Override
  16. public void operation() {
  17. // 相应功能处理
  18. }
  19. }
  20. /**
  21. * 装饰器接口,维持一个指向组件对象,并定义一个与组件接口一致的接口
  22. */
  23. public static abstract class Decorator extends Compoent {
  24. /**
  25. * 持有组件对象
  26. */
  27. protected Compoent compoent;
  28. public Decorator(Compoent compoent) {
  29. this.compoent = compoent;
  30. }
  31. @Override
  32. public void operation() {
  33. // 转发请求给组件对象,可以在转发前后执行一些附加工作
  34. compoent.operation();
  35. }
  36. }
  37. /**
  38. * 装饰器的具体实现对象,想组件负责添加职责
  39. */
  40. public static class ConcreteDecoratorA extends Decorator {
  41. /**
  42. * 添加的状态
  43. */
  44. @Getter
  45. @Setter
  46. private String addedState;
  47. public ConcreteDecoratorA(Compoent compoent) {
  48. super(compoent);
  49. }
  50. @Override
  51. public void operation() {
  52. // 调用父类的方法,可以在调用前后执行一些附加动作
  53. // 在这里进行处理的时候,可以使用添加的状态
  54. super.operation();
  55. }
  56. }
  57. /**
  58. * 装饰器的具体实现对象,想组件负责添加职责
  59. */
  60. public static class ConcreteDecoratorB extends Decorator {
  61. public ConcreteDecoratorB(Compoent compoent) {
  62. super(compoent);
  63. }
  64. @Override
  65. public void operation() {
  66. // 调用父类的方法,可以在调用前后执行一些附加动作
  67. super.operation();
  68. addedBehavior();
  69. }
  70. private void addedBehavior() {
  71. // 需要添加的职责实现
  72. }
  73. }
  74. }

优缺点

优点

  • 比继承更灵活

从为对象添加功能的角度来看,装饰模式比继承更灵活。继承是静态的,而且一旦继承所有子类都有一样的功能。而装饰模式采用把功能分离到每个装饰器当中,然后使用对象组合的方式,在运行时动态地组合功能,每个被装饰的对象最终有哪些功能,是由运行期到动态组合的功能来决定的。

  • 更容易复用功能

装饰模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,使实现装饰器变得简单,更重要的是这样有利于装饰器功能的复用,可以给一个对象增加多个同样的装饰器,也可以把一个装饰器用来装饰不同的对象,从而实现复用装饰器的功能。

  • 简化高层定义

装饰模式可以通过组合装饰器的方式,为对象添加任意多的功能。因此在进行高层定义的时候,不用把所有的功能都定义出来,而是定义最基本的就可以了,可以在需要使用的时候,组合相应的装饰器来完成所需的功能。

缺点

  • 会产生很多细粒度对象

前面说了,装饰模式是把一系列复杂的功能,分散到每个装饰器当中,一般一个装饰器只实现一个功能,这样会产生很多细粒度的对象,而且功能越复杂,需要的细粒度对象越多。

思考

本质

动态组合。

何时选用

  • 如果需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责,可以使用装饰模式,这几乎是装饰模式的主要功能。
  • 如果不适合使用子类进行扩展的时候,可以考虑使用装饰模式。因为装饰模式是使用的“对象组合”的方式。所谓不适合用子类扩展的方式,比如,扩展功能需要的子类太多,造成子类数目呈爆炸性增长。

相关模式

  • 装饰模式与适配器模式

这是两个没有什么关联的模式,放到一起来说,是因为他们有一个共同的别名:Wrapper。
这两个模式功能上是不一样的,适配器模式是用来改变接口的,而装饰模式是用来改变对象功能的。

  • 装饰模式与组合模式

这两个模式有相似之处,都涉及到对象的递归调用,从某个角度来说,可以把装饰看做是只有一个组件的组合。
但是它们的目的完全不一样,装饰模式是要动态地给对象增加功能;而组合模式是想要管理组合对象和叶子对象,为它们提供一个一致的操作接口给客户端,方便客户端的使用。

  • 装饰模式与策略模式

这两个模式可以组合使用。
策略模式也可以实现动态地改变对象的功能,但是策略模式只是一层选择,也就是根据策略选择一下具体实现类而已。而装饰模式不是一层,而是递归调用,无数层都可以,只要组合好装饰器的对象组合,那就可以依次调用下去。所以装饰模式更灵活。
而且策略模式改变的是原始对象的功能,不像装饰模式,后面一个装饰器,改变的是经过前一个装饰器装饰后的对象。也就是策略模式改变的是对象的内核,而装饰模式改变的是对象的外壳。
这两个模式可以组合使用,可以在一个具体的装饰器中使用策略模式来选择更具体的实现方式。

  • 装饰模式与模版方法模式

这是功能上有点相似的模式。
模版方法模式主要应用在算法骨架固定的情况,那么要是算法步骤不固定呢,也就是一个相对动态的算法步骤,就可以使用装饰模式了,因为在使用装饰模式的时候,进行装饰器组装,其实相当于是一个调用算法步骤的组装,相当于是一个动态的算法骨架。
既然装饰模式可以实现动态的算法步骤的组装和调用,那么把这些算法步骤固定下来,那就是模版方法模式实现的功能了,因此装饰模式可以模拟实现模版方法模式的功能。

但是请注意,仅仅只是可以模拟功能而已,两个模式的设计目的、原本的功能、本质思想等都是不一样的。