承接前序文章《设计模式之“Observer”注疏#01》
装饰模式在某种意义上来说也是挺原始的,
- 它首先需要一个你被装饰的基础类,
- 再来是需要一个基于这个基础类的原始包装器,可以看作是其它包装器的基础类
- 进而通过继承这个包装器,来构建出多个具有各自功能的特定包装器
- 最后,是通过层层组合这些包装器(通过一层层地使用构造函数的方式),来构建出你最终需要的包含多个功能的产品类
一开始接触这一过程时,不免会感觉繁杂、手足无措。不就是一个不断装饰、不断包裹的过程么,为何需要这么多的步骤呢?
那么,我可以首先给出一个直观解释,并且通过这个直观解释,可以在一定程度上去解释这里所谓的装饰模式的“原始”表示什么意思。
我们可以把一般的抽象的“装饰过程”想象为:不断地往被装饰物上面添加装饰物件的过程。(特别地,我们在这里考察一个特殊的装饰过程:往墙壁上不断添加各色墙纸的过程,来作为例子。)
(一)
那么,从一般性的装饰过程来看,首先要解决的问题便是:到底为什么物件做装饰呢?你是为一盏台灯做装饰,还是为一张桌子做装饰,又或是为了一面墙做装饰?所以,我们需要一个基础类来决定,被装饰物到底是什么。(在这里,我们的被装饰物是一面墙,所以,我们需要声明“墙”这个类作为我们的基础类。)
(二)
有了装饰的目标之后,下一个要解决的问题是拿什么来做装饰呢?你是用一朵花来做装饰呢?还是用一个气球?又或是用一张海报来做装饰?并且,你选中的这个装饰物还和你的被装饰物牢牢相关。例如,装饰汽车的花朵和装饰卧室的花朵品种可能并不相同;装饰户外的气球和装饰办公室的气球风格,也可能不同。
所以,你不仅要决定做装饰器物(也即是装饰器decorator)的品种,还得要让这个装饰器牢牢地和你的被装饰物相关。(在这里,我们需要装饰墙壁的墙纸。所以你需要建立一个“墙纸”类,并且它是同你的被装饰物“墙”相关)
需要注意的是,这一步的装饰物是一个泛指。例如装饰汽车的花朵、装饰办公室的气球、装饰墙壁的墙纸。可是,这个花朵是什么颜色的、气球是什么形状的、墙纸是什么花纹的,我们并不知道。所以在这一步,声明的仅仅是一个具备功能性泛指的装饰器基础类。
(三)
自然地,下一步的工作便是细化你的装饰器。在这个一般性的装饰器下,例如墙纸,你的这个墙纸到底是什么花纹、什么颜色、有什么特殊功效。这就可以通过构建一个个具体的子类来实现。
(四)
最后,有了被装饰的物件、有了各种装饰器,那么,就可以通过原始的被装饰物,以及各个装饰器的层层嵌套构造函数,来构建出一个具备各种特定功能的产品。
所以,整体考察下来,这个过程还是挺直观的。但是,说其“原始”,是因为你的很多特殊装饰器的构造都必须要通过层层嵌套才能够创造。例如,我装饰汽车的花朵为什么不可以直接拿去装饰卧室?从现实直观上,我们是可以这样做的,但是它却不能在“类”的构建体系中实现一个方便的迁移,不得不从最开始被装饰物出发,一层层地添加装饰物来实现最后的你需要的产品。
// 1\. Basic class, the decorated thing
public class Wall {
private String _name;
public Wall(){};
public void set(String name) {
_name = name;
}
public String get() {
return _name;
}
}
// 2\. General decorator class
public class WallPaper extends Wall {
protected Wall _wall;
public WallPaper(Wall wall) {
_wall = wall;
}
public void set(String name) {
_wall.set(name);
}
public String get() {
return _wall.get();
}
}
// 3.1 Specific decorator: red wall paper
public class RedWallPaper extends WallPaper {
public RedWallPaper(Wall wall) {
super(wall);
}
// ... other specific features
}
// 3.2 Specific decorator: blue wall paper
public class BlueWallPaper extends WallPaper {
public BlueWallPaper(Wall wall) {
super(wall);
}
// ... other specific features
}
// 4\. Final decoration: colorful wall
public class Decoration {
public static void main(String[] args) {
BlueWallPaper ColorfulWall = new BlueWallPaper(new RedWallPaper(new Wall()));
}
}
虽然有了上面的铺垫,但上面这段代码还是存在一个不大容易让人理解的地方,也就是为什么墙纸类WallPaer
需要去继承Wall
呢?按照生活中的对应关系,这个继承明显是有问题的啊?!
这是Decorator模式中最为tricky的部分。严格说来,这个WallPaper
并不是单纯的墙纸,而是已经包装好墙纸的墙!
也就是说,这里不是把“被装饰物”和“装饰物”严格地分开,而是让它们直接合并为一个产品,让整个包装的过程(即增加功能的过程)在内部因为“继承”这个特性而自动实现。
由此,你不断地往一个Decorator的构造函数里面不断地套用Decorator,就能够像俄罗斯陶瓷娃娃那样,不断地加入新的功能,并直接成为一个最终产品。
再来看更为一般和抽象的来自Thinking in Java的装饰模式的代码示例:
// 1. Basic class, the decorated thing
class Basic {
private String value;
public void set(String val) {
value = val;
}
public String get() {
return value;
}
}
// 2. General decorator class
class Decorator extends Basic {
protected Basic basic;
public Decorator(Basic basic) {
this.basic = basic;
}
public void set(String val) {
basic.set(val);
}
public String get() {
return basic.get();
}
}
// 3.1 Specific decorator
class TimeStamped extends Decorator {
private final long timeStamp;
public TimeStamped(Basic basic) {
super(basic);
timeStamped = new Date().getTime();
}
public long getStamp() {
return timeStamp;
}
}
// 3.2 Specific decorator
class SerialNumbered extends Decorator {
private static long counter = 1;
private final long serialNumber = counter++;
public SerialNumbered(Basic basic) {
super(basic);
}
public getSerialNumber() { return serialNumber; }
}
// 4. Decoration
public class Decoration {
public static void main(String[] args) {
TimeStamped t = new TimeStamped(new Basic());
TimeStamped t2 = new TimeStamped(new SerialNumbered(new Basic()));
// ! t2.getSerialNumber(); // Not available
SerialNumbered s = new SerialNumbered(new Basic());
SerialNumbered s2 = new SerialNumbered(new TimeStamped(new Basic()));
// ! s2.getStamp(); // Not available
}
}
这样,装饰模式应该就可以理解了。
近期回顾
《由阅读源码想到 | 下篇》
《由阅读源码想到》
《志玲姐姐的《十三邀》》
如果你喜欢我的文章或分享,请长按下面的二维码关注我的微信公众号,谢谢!
VIP赞赏专区
PDF文档下载:《Decorator设计模式》