浅谈设计模式3——装饰模式


这个模式用做饭来说确实感觉不错,因为书上也是这么打比方的,就是用咖啡,还都是那么绕口的名字,感觉实在受不了。所以此处用面条来打比方,原理相同,但我觉得这个应该大家会更熟悉一些。

1、背景
有各式各样的面条,拉面,手擀面,宽条面,面片等等,当然,每种面条由于制作工艺不同,所以价钱也不一样。同时,面条还要配着卤汁,汤料,所以可以加猪骨高汤,鸡架高汤,蔬菜高汤,羊肉汤,或者炸好的甜面酱,西红柿鸡蛋酱等等。同样,加不同的酱料,价钱也不同。

那么首先想到的是,先做一个面条基类,然后从这个类中派生出各种面条类,比如鸡架高汤手擀面,西红柿鸡蛋宽条面。可是,一旦你这样做了,将会出现以下问题:
1)将会出现无穷多的子类,n种类型的面条×m中类型的卤汁=m×n种类型的产品(为了和不同形状的面条区分,凡是上桌的都叫做“产品”)
2)无法多种材料进行拼凑。比如你想吃拉面,配炸酱+蔬菜高汤,那这种设计方法做不到,除非再新建一个类,这样无疑又增大了工作量。
3)无法修改价钱。目前物价越来越高,可能半年后,西红柿从原来的2块一斤,涨到了4块一斤,那么你必须修改所有加了卤汁为西红柿炒鸡蛋的产品类——估计累晕。

所以经过上面问题的讨论,还是采用装饰模式比较好。那么装饰模式长啥样?不要走开,精彩继续。

2、装饰模式
顾名思义,装饰模式就是在原有的基础上,对原有的事物锦上添花。每次加一个装饰,一方面扩展了原有对象的特点,同时,原有对象的基本特质保持不变。即咖啡再怎么变,也还是咖啡,无论你加了糖、奶、豆浆。面条也是,你加什么卤汁,汤料,仍然还是面条。只不过面条的味道变了,口感变了,价钱也变了。上代码看下:


public class Test1 {

    public static void main(String args[])
    {
        /*宽条面西红柿鸡蛋面 2+2=4
         * 拉面羊肉汤面  3+3=6
         * 宽面条西红柿鸡蛋,羊肉汤面 2+2+3=7
         * */
        
            WideNoodles wideNoodles1=new WideNoodles();//2
            TomatoEgg tomatoEgg1=new TomatoEgg(wideNoodles1);//2
            
            WideNoodles wideNoodles2=new WideNoodles();//2
            TomatoEgg tomatoEgg2=new TomatoEgg(wideNoodles2);//2
            GoatSoup goatSoup2=new GoatSoup(tomatoEgg2);//3
            
            StretchNoodles stretchNoodles1=new StretchNoodles();//3
            GoatSoup goatSoup1=new GoatSoup(stretchNoodles1);//3
                        
        System.out.println(tomatoEgg1.getDes()+tomatoEgg1.price());
        System.out.println(goatSoup1.getDes()+goatSoup1.price());
        System.out.println(goatSoup2.getDes()+goatSoup2.price());
        
        
    }
}
abstract class Noodles //面条抽象类
{
    protected String description="noodles";
    public abstract double price();
    public String getDes(){ return description; }
}

//各种面条
class WideNoodles extends Noodles//宽条面
{
    public WideNoodles() {this.description="WideNoodles";}
    public double price() {return 2.0;}
}

class StretchNoodles extends Noodles//拉面
{
    public StretchNoodles() {this.description="StretchNoodles";}
    public double price(){ return 3.0;}
}

//卤汁类
class Bittern extends Noodles////基本卤汁
{
    protected Noodles noodles;//面条
    
    public Bittern(Noodles n){this.description="Bittern";  this.noodles=n;}//修改成卤汁类型
    public double price(){return this.noodles.price()+1.0;}//面条+1块钱卤汁   
    public String getDes() {return this.description+","+this.noodles.getDes();}//面条+卤汁
}

//具体卤汁,由于上面卤汁基类的方法都已经重新,所以只需要修改price即可
class TomatoEgg extends Bittern//西红柿炒鸡蛋卤汁
{
    public TomatoEgg(Noodles n) {super(n); this.description="TomatoEgg";}
    public double price(){return this.noodles.price()+2.0;}
}

class GoatSoup extends Bittern//羊肉汤卤汁
{
    public GoatSoup(Noodles n) {super(n);this.description="GoatSoup";}
    public double price() {return this.noodles.price()+3.0;}
}
class ChickSoup extends Bittern//鸡架卤汁
{
    public ChickSoup(Noodles n) {super(n);this.description="ChickSoup";}
    public double price() {return this.noodles.price()+4.0;}
}

运行结果截图

开始解析上面的代码。刚看到工作的同学准备招聘高级java程序员。读研的伤不起啊。。。
1)从程序中可以看到,所有的类都有一个共同的抽象基类,即Noodles,因为产品无论怎么变化,本质是面条,也应正了继承是Is—a的规律。面条都有描述和价钱。
2)每一种面条都作为独立的个体,而卤汁不一样,卤汁需要依附于面条,也就是有了面条,卤汁才能修饰作为装饰者,去修饰面条。所以卤汁中包含了面条主体。而卤汁+面条=面条,所以面条的本质没有变。
(注意修饰和被修饰的关系,这样才能确定组合的情况)

3、Java中的具体实现
在Java.io.*中,就是这样的道理。所有的IO类都有一个共同的基类,表明这些类都是I/O操作,本质没有变。但是,对于真正做读写操作的(就好像本文中的面条),作为一个独立实体,这些类只能一次读取一个字节,或者一行。而修饰这些进行实际读写操作的装饰者,则可能只是封装了一个缓存,或者将读取的字符转换成小写等等。

4、其他方面
其实说的再远一点。Unix中的管道也是这种思想,每个程序都提供了读取数据和写出数据的接口,然后通过管道,可以一次又一次的传递数据,比如:ls /root | more | grep "Noodles"。当然,more只是为了说明,实际没啥用。

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

推荐阅读更多精彩内容