初探装饰模式

代码链接

koonchen/design-patterns/decorator

要点

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

一个图形界面允许我们仅仅给某个窗口增加特性,比如窗口滚动。我们可以将窗口滚动的边框加入到一个组件中,这个对象被称为 装饰 。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。

假设现在有一个对象被称作 TextView ,它可以在窗口中显示正文,缺省的 TextView 没有滚动条,因为我们可能并不需要滚动条。当需要滚动条的时候,我们可以使用 ScrollDecorator 添加滚动条到 TextView 。如果我们还想在 TextView 周围增加一个粗黑边框,我们可以使用 BorderDecorator 添加滚动条。示意图如下:

cover

从上文,我们可以发现, ScrollDecoratorBorderDecorator 类是 Decorator 类的子类。 Decorator 类是一个可视化组件的抽象类,具体结构如下:

cover

VisualComponent 是一个描述可视对象的抽象类,它定义了绘制和事件处理的接口。 Decorator 的子类为特定功能增加一些操作。比如我现在想要一个滚动窗口,那么就可以使用 ScrollDecorator 对象的 ScrollTo 操作滚动这个界面,同时,我们不会感受到装饰和未装饰的差异。

在以下情况使用 Decorator 模式:

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象增加职责;
  • 处理那些可以撤销的职责;
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立地扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

参与者以下:

  • Component — 定义一个对象接口,可以给这些对象动态地增加职责。
  • ConcreteComponent — 定义一个对象,可以给这个对象添加一些职责。
  • Decorator — 维持一个指向 Component 对象的指针,并定义一个与 Component 接口一致地接口。
  • ConcreteDecorator — 向组件添加职责。

总体结构如下:

cover

decorator 模式的优点及缺点

  • 比静态继承更灵活:与对象的静态继承相比, Decorator 模式提供了更加灵活的对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。相比之下,继承机制要求为每个增加的职责创建一个新的子类,这会导致产生许多新的类,并且会增加系统复杂度。甚至可以对一个特定的 Component 类提供多个不同的 Decorator 类,这就使得你可以对一些职责进行混合和匹配。
  • 避免在层级结构高层的类有太多的特征: Decorator 模式是一种即用即付的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用 Decorator 类给他组件添加功能。
  • Decorator 与它的 Component 不一样: Decorator 是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
  • 有许多小对象:采用 Decorator 模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。

实现

在实现 Decorator 模式时有以下几点需要注意:

  • 接口一致性:装饰对象的接口必须与它所装饰的 Component 的接口是一致的。
  • 省略抽象的 Decorator 类:当仅需添加一个职责时,没有必要定义抽象 Decorator 类。
  • 保持 Component 类的简单性:为了保证接口的一致性,组件和装饰必须公用一个父类,保持简单性很重要。
  • 改变对象外壳与改变对象内核:可以将 Decorator 看做一个对象的外壳,他可以改变这个对象的行为。

java 实现

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Component {
void operation() {
System.out.println("Component instance has been created");
}
}

class ConcreteComponent extends Component {
@Override
public void operation() {
System.out.println("DecoratorClient instance has been created");
}
}

class Decorator extends Component {
Component component;

public Decorator(Component component) {
this.component = component;
}

@Override
public void operation() {
component.operation();
}
}

class ConcreteDecoratorA extends Decorator {

public ConcreteDecoratorA(Component component) {
super(component);
}

void addedState() {
System.out.println("Added state");
}
}

class ConcreteDecoratorB extends Decorator {

public ConcreteDecoratorB(Component component) {
super(component);
}

void addedBehavior() {
System.out.println("Added behavior");
}
}

public class DecoratorClient {
public static void main(String[] args) {
ConcreteComponent component = new ConcreteComponent();
ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(component);
ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(component);
component.operation();
concreteDecoratorA.addedState();
concreteDecoratorB.addedBehavior();
}
}

对原始的 ConcreteComponent 增加了 addedStateaddedBehavior 两个方法。

python 实现

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
27
28
29
30
class Component(object):
def operation(self):
print('Component instance has been created')

class ConcreteComponent(Component):
def operation(self):
print('ConcreteComponent instance has been created')

class Decorator(Component):
def __init__(self, component):
self._component = component

def operation(self):
print('Decorator instance has been created')

class ConcreteDecoratorA(Decorator):
def addedState(self):
print('Added state')

class ConcreteDecoratorB(Decorator):
def addedBehavior(self):
print('Added behavior')

if __name__ == '__main__':
component = ConcreteComponent()
componenta = ConcreteDecoratorA(component)
componentb = ConcreteDecoratorB(component)
component.operation()
componenta.addedState()
componentb.addedBehavior()

实现大同小异,不再赘述。

相关模式:

  • Adapter 模式: Decorator 模式不同于 Adapter 模式,因为装饰仅仅改变对象的职责而不是改变接口;适配器模式给出了一个全新的接口。
  • Composite 模式:可以将装饰视为一个退化的、仅有一个组件的组合模式。然而,装饰的目的是给对象增加职责,而不是将对象进行聚集。
  • Strategy 模式:用一个装饰可以改变对象的外表;而 Strategy 模式使得我们可以改变对象的内核。这是改变对象的两种途径。