每天一个模式 - Decorator Pattern

Waht?

Definition

在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象上,从而实现动态拓展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

满足设计原则

  • 多用组合,少用继承
  • 开放-关闭原则,类应该对拓展开放,对修改关闭

Usage

  • Component是基类。通常是一个抽象类或者一个接口,定义了属性或者方法,方法的实现可以由子类实现或者自己实现。通常不会直接使用该类,而是通过继承该类来实现特定的功能,它约束了整个继承树的行为。比如说,如果Component代表人,即使通过装饰也不会使人变成别的动物。

  • ConcreteComponent是Component的子类,实现了相应的方法,它充当了“被装饰者”的角色。

  • Decorator也是Component的子类,它是装饰者共同实现的抽象类(也可以是接口)。比如说,Decorator代表衣服这一类装饰者,那么它的子类应该是T恤、裙子这样的具体的装饰者。

  • ConcreteDecorator是Decorator的子类,是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰。


Example

人类吃肉方式变化过程:直接吃生肉 -> 煮熟以后再吃 -> 煮熟添加各种配菜和调料再吃 -> …….

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
/**
* 定义作为人类应有的一些规范
*/
public interface IHuman {
void eating();
}

/**
* 人类具体吃的行为
*/
public class Man implements IHuman {
@Override
public void eating() {
System.out.println("吃刚刚捕获的美味佳肴");
}
}

/**
* 抽象装饰者
*
* 在Decorator中并未真正实现eat()方法,而只是调用原有Human对象的eating()方法,
* 它没有真正实施装饰,而是提供一个统一的接口,将具体装饰过程交给子类完成。
*/
public class Decorator implements IHuman {
//维持对抽象构件的引用
private IHuman human;
//注入构件实例
public Decorator(IHuman human) {
this.human = human;
}

@Override
public void eating() {
human.eating();
}
}

public class ConcreteDecorator extends Decorator {

public ConcreteDecorator(IHuman human){
super(human);
}

@Override
public void eating() {
System.out.println("用火烤熟肉");
super.eating();
}
//需要增加的其他方法
}

JDK

  • Component:InputStream就是装饰者模式中的超类
  • ConcreteComponent:ByteArrayInputStream,FileInputStream相当于被装饰者
  • Decorator:FilterInputStream
  • ConcreteDecorator(继承FilterInputStream):BufferedInputStream,DataInputStream,PushbackInputStream

Code Example

1
2
3
File file = new File("hello.txt");
FileInputStream in = new FileInputStream(file);
BufferedInputStream inBuffered = new BufferedInputStream(in);


Conclusion

Advantage

  • 使用装饰者模式比使用继承更加灵活,因为它选择通过一种动态的方式来扩展一个对象的功能,在运行时可以选择不同的装饰器,从而实现不同的行为。

  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

  • 具体构件类与具体装饰类可以独立变化,他能是低耦合的。用户可以根据需要来增加新的具体构件类和具体装饰类,在使用时再对其进行各种组合,原有代码无须改变,符合“开闭原则”。

Disadvantage

  • 会产生很多的小对象,增加了系统的复杂性

  • 由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

Reference

https://blog.csdn.net/a553181867/article/details/52108423
https://zhuanlan.zhihu.com/p/44003220
设计模式–装饰者模式(在IO体系中的应用):https://blog.csdn.net/u013679744/article/details/77686646