装饰器模式

Scroll Down

概述

不断地为对象添加装饰的设计模式被称为Decorator模式。
在实践生产中,新需求在软件的整个生命过程中总是不断出现的。当有新需求出现时,就需要为某些组件添加新的功能满足这些需求。添加新功能的方式有很多,我们可以直接修改已有组件的代码并添加相应的新功能,这显然会破坏已有组件的稳定性,修改完成后,整个组件需要重新进行测试,才能上线使用。这种方式显然违反了开放-封闭原则。
另一种方式是使用继承方式,我们可以创建子类并在子类中添加新功能实现扩展。这种方法是静态的,用户不能控制增加行为的方式和时机。而且有些情况下是不能继承的。另外,如果待添加的新功能存在多种组合。使用继承方式可能会导致大量子类的出现。
装饰器模式能够帮助我们解决上述问题,装饰器可以动态地为对象添加功能,它是基于组合的方式实现该功能的。在实践中,我们应该尽量使用组合的方式来扩展系统的功能,而非使用继承的方式。通过装饰器模式的介绍,可以帮助读者更好地理解设计模式中常见的一句话:组合由于继承。

结构

design-decorate.drawio

  • Component(组件):组件接口定义了全部组件实现类以及所有装饰器实现的行为。
  • ConcreteComponent(具体组件实现类):具体组件实现类实现了Component接口。通常情况下,具体组件实现类就是被装饰器装饰的原始对象,该类提供了Component接口中定义的最基本的功能,其他高级功能或后续添加的新功能,都是通过装饰器的方式添加到该类的对象之上的。
  • Decorator(装饰器):所有装饰器的父类,它是一个实现了Component接口的抽象类,并在其中封装了一个Component对象,也就是被装饰的对象。而这个被装饰的对象只要是Component类型即可,这就实现了装饰器的组合和复用。如下图所示,装饰器C(ConcreteDecorator1类型)修饰了装饰器B(ConcreteDecorator2类型)并为其添加功能W,而装饰器B(ConcreteDecorator2类型)又修饰了组件A(ConcreteComponent类型)并为其添加功能V。其中,组件对象A提供的是最基本的功能,装饰器B和装饰器C会为组件对象A添加新功能。

design-decorate-002.drawio

  • ConcreteDecorator:具体的装饰器实现类,该实现类要向被修饰对象添加某些功能,如上图所示,装饰器B、C就是该角色,被装饰的对象只要是Component类型即可。

优点

使用装饰器模式有两个明显的优点:

  • 相较于继承来说,装饰器模式的灵活性更强,可扩展性也强。正如前面所说,继承方式会导致大量子类的情况,而装饰器模式可以将复杂的功能一个个独立的装饰,通过多个独立的装饰器的动态组合,创建不同功能的组件,从而满足不同需求。
  • 当有新功能需要添加时,只需要添加新的装饰器实现类,然后通过组合方式添加这个新装饰器即可,无须修改已有类的代码,符合开闭原则。