设计模式之“Decorator”注疏#02

承接前序文章《设计模式之“Observer”注疏#01

装饰模式在某种意义上来说也是挺原始的,

  1. 它首先需要一个你被装饰的基础类
  2. 再来是需要一个基于这个基础类的原始包装器,可以看作是其它包装器的基础类
  3. 进而通过继承这个包装器,来构建出多个具有各自功能的特定包装器
  4. 最后,是通过层层组合这些包装器(通过一层层地使用构造函数的方式),来构建出你最终需要的包含多个功能的产品类

一开始接触这一过程时,不免会感觉繁杂、手足无措。不就是一个不断装饰、不断包裹的过程么,为何需要这么多的步骤呢?

那么,我可以首先给出一个直观解释,并且通过这个直观解释,可以在一定程度上去解释这里所谓的装饰模式的“原始”表示什么意思。

我们可以把一般的抽象的“装饰过程”想象为:不断地往被装饰物上面添加装饰物件的过程。(特别地,我们在这里考察一个特殊的装饰过程:往墙壁上不断添加各色墙纸的过程,来作为例子。)

(一)

那么,从一般性的装饰过程来看,首先要解决的问题便是:到底为什么物件做装饰呢?你是为一盏台灯做装饰,还是为一张桌子做装饰,又或是为了一面墙做装饰?所以,我们需要一个基础类来决定,被装饰物到底是什么。(在这里,我们的被装饰物是一面墙,所以,我们需要声明“墙”这个类作为我们的基础类。)

(二)

有了装饰的目标之后,下一个要解决的问题是拿什么来做装饰呢?你是用一朵花来做装饰呢?还是用一个气球?又或是用一张海报来做装饰?并且,你选中的这个装饰物还和你的被装饰物牢牢相关。例如,装饰汽车的花朵和装饰卧室的花朵品种可能并不相同;装饰户外的气球和装饰办公室的气球风格,也可能不同。

所以,你不仅要决定做装饰器物(也即是装饰器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
    }
}

这样,装饰模式应该就可以理解了。

近期回顾

由阅读源码想到 | 下篇
由阅读源码想到
志玲姐姐的《十三邀》

如果你喜欢我的文章或分享,请长按下面的二维码关注我的微信公众号,谢谢!

image

VIP赞赏专区

image

PDF文档下载:《Decorator设计模式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Iterator模式 (迭代器) 一个一个遍历 一个集合类可以遵守 Iterator 协议,并实现一个 Itera...
    SSBun阅读 1,788评论 0 15
  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,710评论 0 14
  • 平时特别喜欢在手机上面看文章,看到好的就喜欢收藏,最终结果就是很多app上面收藏了很多,但是很凌乱又不能及时更新整...
    苑常生阅读 2,094评论 0 11
  • 日子还要继续,虽然得到批准可以休假,但毕竟还有一个月的时间,第一次感觉时间难熬。 靠日本码头的时候,第一次在东京下...
    海豚儿儿阅读 167评论 0 0
  • 从小就梦想可以吃遍天下的水果,所以我大学报到了南方。 到了大二我从新审视了我的目标,我发誓我一定要吃遍天下所有口味...
    怪怪的人说怪怪的话阅读 92评论 1 0