摘要生成中...
AI 摘要
Hunyuan-lite

image.png

装饰器模式通过组合替代继承的方式在不改变原始类的情况下添加增强功能,主要解决继承关系过于复杂的问题。

image.png

登场角色:

  • Component(部件):增加功能时的核心角色。Component 角色只是定义了被装饰物接口(API)。
  • ConcreteComponent(具体部件):该角色是实现了 Component 角色所定义的接口(API)的具体被装饰物。
  • Decorator(装饰物):该角色具有与 Component 角色相同的接口(API)。在它内部保存了被装饰对象——Component 角色。Decorator 角色知道自己要装饰的对象。
  • ConcreteDecorator(具体的装饰物):该角色是具体的 Decorator 角色,它在实现方法时,会通过委托,调用 component,从而实现方法增强。

Main 中的嵌套装饰:

1
2
3
4
5
6
7
8
Component c = 
new ConcreteDecorator1(
new ConcreteDecorator2(
new ConcreteDecorator3(
new ConcreteComponent(); // 被装饰物
)
)
)
优点 缺点
无需创建新子类即可扩展对象的行为。 在封装器栈中删除特定封装器比较困难。
可以在运行时添加或删除对象的功能。 实现行为不受装饰栈顺序影响的装饰比较困难。
可以用多个装饰封装对象来组合几种行为。 各层的初始化配置代码看上去可能会很糟糕。
单一职责原则。可以将实现了许多不同行为的一个大类拆分为多个较小的类。

拓展思路:

  • 接口(API)的透明性:装饰边框与被装饰物具有一致性。Decorator 是被装饰物 Component 的子类就体现了它们的一致性,它们具有相同的接口(API)。即使被装饰物被装饰起来了,接口(API)也不会隐藏起来,依然可以调用,这就是接口的透明性。
  • 在不改变被装饰物的前提下增加功能。
  • 可以动态地添加功能。装饰器模式中使用委托,它使类之间形成了弱关联关系。因此,不用改变框架代码,就可以生成一个与其他对象具有不同关系的新对象。
  • 只需要添加一些装饰物即可添加许多功能。这些功能可以自由组合。
  • 缺点:导致增加许多很小的类

相关设计模式

image.png

相似的设计模式

因为透明性,被装饰物实际上也是别的物体的「装饰边框」,形成递归结构。通过下图和 站内文章组合模式UML 对比即可发现,两者近乎相似,因为两者都依赖递归组合来组织无限数量的对象。

  • 装饰器模式的目的是通过增加装饰物来增加对象的功能,组合仅对子节点的结果进行求和。
  • 装饰器只有一个子组件。
  • 两种模式可以相互合作,可以使用装饰来扩展组合树中特定对象的行为。
  • 可以用 站内文章原型模式 复制复杂结构。

在类功能改变方面:

  • 装饰器模式可以像改变被装饰物的边框或是为被装饰物添加多重边框那样,来增加类的功能,改变的是对象的外表。
  • 站内文章策略模式 通过整体地替换算法来改变类的功能,改变本质。

站内文章责任链模式 和装饰模式的类结构非常相似。两者都依赖递归组合将需要执行的操作传递给一系列对象。但是,两者有几点重要的不同之处。

  • 责任链的管理者可以相互独立地执行一切操作,还可以随时停止传递请求。
  • 各种装饰可以在遵循基本接口的情况下扩展对象的行为。
  • 装饰无法中断请求的传递。

装饰器模式和 站内文章代理模式 有着相似的结构,但是其意图却非常不同。关于 Wrapper 模式的辨析,详看 站内文章这篇文章

关于组合与继承的讨论

组合优于继承。对于增加类的功能方面,相比于通过继承生成子类的方法,装饰器更为灵活。

装饰器模式相对于简单的组合关系,还有两个比较特殊的地方:

  • 装饰器类和原始类继承同样的父类,这样我们可以对原始类「嵌套」多个装饰器类。在装饰器模式中,使用继承的主要目的是让装饰器和抽象组件是一样的类型,也就是要有共同的超类,也就是使用继承达到「类型匹配」,而不是利用继承获得「行为」。
  • 装饰器类是对功能的增强。代理模式、桥接模式都使用了组合,尽管它们的代码结构很相似,但是不同设计模式的设计意图是不一样的。具体区别详看 站内文章这篇文章 相关章节的辨析。

装饰器模式中,装饰边框与被装饰物具有一致性;装饰器使用了继承和委托,这两种方式也内含一种「一致性」,具体详看 站内文章这篇文章 的相应章节。

Java I/O 中的装饰者模式

Java IO 的类库十分庞大,有 40 多个类,负责 IO 数据的读取和写入。我们可以从以下角度将其划分为四类,具体如下:

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

针对不同的读取和写入场景,Java IO 又在四个父类基础上,扩展了很多子类:

image.png

一个输入流的装饰器模式结构:

image.png

FilterInputStream 是一个抽象的装饰者,构造函数声明为 protected,表明用户不能直接构造该类的对象,只能构造该类的子类对象。对于一些不需要装饰的方法,如 read()FilterInputStream 只是简单交由被装饰对象 InputStream 完成。

本文 PlantUML 存档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
abstract class Component{
{method} {abstract} method1
{method} {abstract} method2
{method} {abstract} method3
}

class ConcreteComponent{
{method} method1
{method} method2
{method} method3
}

abstract class Decorator{
component
}

class ConcreteDecorator{
{method} method1
{method} method2
{method} method3
}

Component <|-- ConcreteComponent
Component <|- Decorator
Component -o Decorator
Decorator <|-- ConcreteDecorator

本文参考