本篇介绍了装饰者模式及相关的面向对象设计原则。装饰者模式动态的将责任附加到对象身上,扩展功能时,装饰者模式相比于继承能为系统带来更好的弹性。
1 引例
一个咖啡饮料系统需要为所有类型的饮料定义各自的类,最原始的设计是这样:
在购买饮料时,顾客可以在某种饮料的基础上加入各种调料,比如奶泡、摩卡等,不同调料的价格也不同,组合而成的饮料价格也就不同,不同的调料搭配不同的饮料可能会出现无数种组合,如果一味的使用继承,将会使饮料子类的数量急剧膨胀,难以管理的同时还会出现大量的重复代码。因此我们希望能有一种设计模式来解决这种问题。
2 装饰者模式
设计原则:开放-关闭原则,对扩展开放,对修改关闭。
开放-关闭原则是指当一个类设计好之后,尽可能不对他进行修改,但又能允许该类进行扩展,也就是在不修改现有代码的前提下,扩展新的行为。装饰者模式就实现了这一目的。
以一杯加了奶泡和摩卡的深焙咖啡为例,构建这样一种饮品的流程是:
这就是一个装饰者模式的流程,装饰者模式的类图结构如下:
按着上面的类图,饮料就作为组件,具体的饮料类型对应于具体组件,而调料作为装饰器,具体的调料对应不同的具体装饰器,于是我们可以开始用装饰者模式实现上面的饮料系统。
首先是组件(饮料)类:
1 | class Component { |
装饰器(调料)要和组件是同一类,这样才能保证所有装饰器都能代替组件,调用同一个接口,因此需要继承于组件类:
1 | class Decorator : public Component { |
接下来实现一个具体组件类,比如浓缩咖啡(Espresso)类:
1 | class Espresso : public Component { |
然后实现一个具体装饰器,比如摩卡(Mocha)类:
1 | class Mocha : public Decorator { |
按照上面的方法可以构建不同的饮料和调料,之后就可以随意组合了,在调用的时候也会变得无比方便:
1 | int main() { |
总结来看,装饰者类是一个组件类,也就是和被装饰者是 is-a 的关系,但同时装饰者类中又包含组件类,对组件类进行扩展,因此二者之间还存在 has-a 的关系。装饰者模式利用组合解决了继承带来的子类膨胀和灵活性差的问题。