Head First设计模式学习笔记三

装饰者模式

Posted by Emo on June 25, 2022

设计模式

练习实例仓库

EMO’s Blog

定义与描述

装饰者模式动态的将责任附加到对象上。若要扩展功能,装饰者提供比继承更有弹性的替代方案。

装饰的作用

运行时扩展,使用装饰我们可以在不修改任何底层代码的情况下,给你的或者别人的对象添加新的职责。

组合,委托与继承

利用继承设计子类,子类的功能在编译初期就已经确定了,而且所有的子类都将继承到相同的功能,而利用组合去设计子类,我们就可以在运行的时候动态的扩展子类的功能。而且利用组合,我们在添加新功能的时候就可以避免在原有代码上做修改,这样产生bug的几率就大大减小了。同样的委托(delegation)也能够在运行时具有继承的效果。

新的原则

开放-关闭原则: 类应该对扩展开放,对修改关闭。

对现成代码的修改很容易引入bug,所以类应当对修改关闭,而使用扩展的方式去引入新功能。因此我们的目标是允许类的扩展,以便在不修改现有代码的情况下引入新的变化。

装饰者模式架构

整体结构由超类,被装饰者以及装饰者来构建。装饰者和被装饰者都是源于一个相同的超类。各种被装饰者直接继承于抽象的超类,而各种装饰者是直接继承于一个装饰类,然后装饰类直接继承于这个抽象的超类。

星巴兹实例

为星巴兹订单系统设计类。主要分为咖啡和配料,不同的咖啡可以选加各种配料,最后我们需要计算出总体的价格。

通过装饰者模式

  1. 创建一个咖啡实例对象
  2. 用配料实例对象修饰他
  3. 调用cost()方法,并通过委托的方式将配料的价钱加上去。

架构

java.io类的装饰结构

装饰者模式的弊端也在这里展现了出来

  • 我们可以看到,随着装饰者模式,带来的是构建大量的小类,这可能会在使用的时候带来一些困扰。但是即便如此,这些类之间的组织模式还是比较清晰的。
  • 除此之外,装饰者在实例化的时也会加深代码复杂度,因为需要将被装饰者实例不断地嵌入/包裹到一个个装饰者实例当中。
  • 还会导致类型转变问题,如果有代码依赖于特定的被装饰者类型,而包装会导致被装饰者的的类型发生转别,从而导致类型识别出现问题。用星巴兹咖啡举例,比如我们设计了一个针对HouseBlend的打折政策,需要识别到(实例类型 == HouseBlend),而如果我们将HouseBlend用Soy进行包装后,我们将无法将包装后的实例识别为HouseBlend。

{ % if page.mermaid % } { % endif % }