裝飾者模式

裝飾模式是一種用于替代繼承的技術(shù)粥鞋,它通過一種無須定義子類的方式來給對象動態(tài)增加職責(zé),使用對象之間的關(guān)聯(lián)關(guān)系取代類之間的繼承關(guān)系裕膀。

概覽

定義

Decorator Pattern: Attaches additional responsibility to anobject dynamically. Decorators provide a flexible alternative to subclassingfor extending functionality.

裝飾模式(DecoratorPattern)在不改變原類文件以及不使用繼承的情況下员串,動態(tài)地將責(zé)任附加到對象上,從而實(shí)現(xiàn)動態(tài)拓展一個對象的功能昼扛。

UML類圖

參與者

參與者

Component(抽象構(gòu)件)

  • 是一個接口或者抽象類寸齐,用來定義基本行為
  • 是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實(shí)現(xiàn)的業(yè)務(wù)方法抄谐,它的引入可以使客戶端以一致的方式處理未被裝飾的對象以及裝飾之后的對象渺鹦,實(shí)現(xiàn)客戶端的透明操作

ConcreteComponent(具體構(gòu)件)

  • 抽象構(gòu)件類的子類蛹含,用于定義具體的構(gòu)件對象(即被裝飾者)
  • 實(shí)現(xiàn)了在抽象構(gòu)件中聲明的方法毅厚,裝飾器可以給它增加額外的職責(zé)(方法)

Decorator(抽象裝飾類)

  • 一個接口或抽象類,也是抽象構(gòu)件類的子類浦箱,用于給具體構(gòu)件增加職責(zé)吸耿,但是具體職責(zé)在其子類中實(shí)現(xiàn)
  • 對于ConcreteComponent來說,不需要知道Decorator的存在酷窥,
  • 維護(hù)一個指向抽象構(gòu)件對象的引用咽安,通過該引用可以調(diào)用裝飾之前構(gòu)件對象的方法,并通過其子類擴(kuò)展該方法蓬推,以達(dá)到裝飾的目的

ConcreteDecorator(具體裝飾類)

  • 抽象裝飾類的子類妆棒,負(fù)責(zé)向構(gòu)件添加新的職責(zé)
  • 每一個具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法沸伏,并可以增加新的方法用以擴(kuò)充對象的行為糕珊。
  • 每個裝飾者都應(yīng)該有一個實(shí)例變量用以保存某個Component的引用,持有Component的引用后毅糟,由于其自身也是Component的子類放接,相當(dāng)于ConcreteDecorator包裹了Component,不但有Component的特性留特,同時自身也可以有別的特性纠脾,也就是所謂的裝飾。

設(shè)計的原則

裝飾者和被裝飾者有相同超類

這里利用繼承是為了達(dá)到類型匹配蜕青,而不是利用繼承獲得行為
因?yàn)檠b飾者和被裝飾者是同一個類型,因此裝飾者可以取代被裝飾者,這樣就使被裝飾者擁有了裝飾者獨(dú)有的行為苟蹈。

用組合而不是繼承

利用繼承設(shè)計子類,只能在編譯時靜態(tài)決定右核,并且所有子類都會繼承相同的行為慧脱;利用組合的做法擴(kuò)展對象,就可以在運(yùn)行時動態(tài)的進(jìn)行擴(kuò)展贺喝。
利用裝飾者菱鸥,我們可以實(shí)現(xiàn)新的裝飾者增加新的行為宗兼,而不用修改現(xiàn)有代碼,而如果單純依賴?yán)^承氮采,每當(dāng)需要新行為時殷绍,還得修改現(xiàn)有的代碼。遵循了開放-關(guān)閉原則:類應(yīng)該對擴(kuò)展開放鹊漠,對修改關(guān)閉主到。

調(diào)用方式

  1. 包裹
    它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實(shí)的對象
  2. 保存被裝飾者的引用
    在裝飾模式中引入了裝飾類躯概,在裝飾類中既可以調(diào)用待裝飾的原有類的方法登钥,還可以增加新的方法,以擴(kuò)充原有類的功能(在裝飾者類中調(diào)用被裝飾者類的方法娶靡,封裝成新的功能方法)
  3. 裝飾者類擁有被裝飾者類的對象牧牢,一般是當(dāng)構(gòu)造參數(shù)傳入

一個例子

分析

一個小攤賣手抓餅和烤冷面(都是小吃)。
點(diǎn)了手抓餅和烤冷面之后還可以在這個基礎(chǔ)之上增加一些配料姿锭,例如煎蛋塔鳍,火腿片等,每個配料的價格都不一樣艾凯。
隨意搭配配料献幔,最終價格是手抓餅和烤冷面基礎(chǔ)價+每一種所選配料價格的總和懂傀。小攤的價格單如下:

小吃(Snack) 價格
手抓餅(Handcake) 5
烤冷面(Noodles) 7
配料(Condiment ) 價格
煎蛋(Friedegg) 1
火腿(Ham) 2
培根(Bacon) 3

UML類圖

用裝飾者模式處理趾诗,主體是被裝飾者,而配料則是裝飾者蹬蚁,UML類圖:

UML類圖

代碼實(shí)現(xiàn):

結(jié)構(gòu)

Snack

public abstract class Snack {
    public String desc = "我不是一個具體的食物";
    public String getDesc() {
        return desc;
    }
    public abstract double cost();
}

Handcake

public class Handcake extends Snack {
    public Handcake() {
        desc = "手抓餅";
    }
    @Override
    public double cost() {
        return 5;
    }
}

Noodles

public class Noodles extends Snack {
    public Noodles() {
        desc = "烤冷面";
    }
    @Override
    public double cost() {
        return 7;
    }
}

Condiment

public abstract class Condiment extends Snack {
    public abstract String getDesc();
}

FiredEgg

public class FiredEgg extends Condiment {
    private Snack snack;// 持有Component的引用
    public FiredEgg(Snack snack) {
        this.snack = snack;
    }
    @Override
    public String getDesc() {
        return snack.getDesc() + ", 煎蛋";
    }
    @Override
    public double cost() {
        return snack.cost() + 1;
    }
}

Ham

public class Ham extends Condiment {
    private Snack snack;// 持有Component的引用
    public Ham(Snack snack) {
        this.snack = snack;
    }
    @Override
    public String getDesc() {
        return snack.getDesc() + ", 火腿";
    }
    @Override
    public double cost() {
        return snack.cost() + 2;
    }
}

Bacon

public class Bacon extends Condiment {
    private Snack snack;// 持有Component的引用
    public Bacon(Snack snack) {
        this.snack = snack;
    }
    @Override
    public String getDesc() {
        return snack.getDesc() + ", 培根";
    }
    @Override
    public double cost() {
        return snack.cost() + 3;
    }
}

測試類:TestDecorate

public class TestDecorate {
    public static void main(String[] args) {
        Snack snack1 = new Handcake();
        System.out.println(snack1.getDesc() + "純手抓餅花費(fèi)" + snack1.cost());
        snack1 = new FiredEgg(snack1);
        System.out.println(snack1.getDesc() + "手抓餅+煎蛋總共花費(fèi)" + snack1.cost());

        Snack snack2 = new Noodles();
        System.out.println(snack2.getDesc() + "純烤冷面花費(fèi)" + snack2.cost());
        snack2 = new FiredEgg(snack2);
        snack2 = new Ham(snack2);
        snack2 = new Bacon(snack2);
        System.out.println(snack2.getDesc() + "烤冷面+煎蛋+火腿+培根總共花費(fèi)" + snack2.cost());
    }
}

輸出

輸出

小結(jié)分析

Noodles恃泪、FiredEgg、Ham犀斋、Bacon都是繼承自Snack 基類贝乎,但是具體實(shí)現(xiàn)不同:

  • Noodles是Snack的直接子類,是被裝飾者
  • FiredEgg叽粹、Ham览效、Bacon是裝飾者,保存了Snack的引用虫几,實(shí)現(xiàn)了cost()方法锤灿,并且在cost()方法內(nèi)部,不但實(shí)現(xiàn)了自己的邏輯辆脸,同時也調(diào)用了Snack引用的cost()方法但校,即獲取了被裝飾者的信息,這是裝飾者的一個特點(diǎn)啡氢,保存引用的目的就是為了獲取被裝飾者的狀態(tài)信息状囱,以便將自身的特性加以組合术裸。

總結(jié)

1、裝飾者和被裝飾者對象有相同的超類型亭枷,所以在任何需要原始對象(被裝飾者)的場合袭艺,都可以用裝飾過得對象代替原始對象
2、可以用一個或多個裝飾者包裝一個對象(被裝飾者)
3奶栖、裝飾者可以在所委托的裝飾者行為之前或之后加上自己的行為匹表,以達(dá)到特定的目的
4、被裝飾者可以在任何時候被裝飾宣鄙,所以可以在運(yùn)行時動態(tài)地袍镀、不限量地用你喜歡的裝飾者來裝飾對象
5、裝飾者會導(dǎo)致出現(xiàn)很多小對象冻晤,如果過度使用苇羡,會讓程序變得復(fù)雜

Java I/O是裝飾者模式的典型應(yīng)用

參考文章
Java設(shè)計模式之裝飾者模式(Decorator pattern)
學(xué)習(xí)、探究Java設(shè)計模式——裝飾者模式
設(shè)計模式學(xué)習(xí)筆記之三:裝飾者模式
設(shè)計模式(結(jié)構(gòu)型)之裝飾者模式(Decorator Pattern)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鼻弧,一起剝皮案震驚了整個濱河市设江,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌攘轩,老刑警劉巖叉存,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異度帮,居然都是意外死亡歼捏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門笨篷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞳秽,“玉大人,你說我怎么就攤上這事率翅×防” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵冕臭,是天一觀的道長腺晾。 經(jīng)常有香客問我,道長辜贵,這世上最難降的妖魔是什么悯蝉? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮念颈,結(jié)果婚禮上泉粉,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好嗡靡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布跺撼。 她就那樣靜靜地躺著,像睡著了一般讨彼。 火紅的嫁衣襯著肌膚如雪叹话。 梳的紋絲不亂的頭發(fā)上蕴忆,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天苫昌,我揣著相機(jī)與錄音惕耕,去河邊找鬼。 笑死蜜自,一個胖子當(dāng)著我的面吹牛菩貌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播重荠,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼箭阶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了戈鲁?” 一聲冷哼從身側(cè)響起仇参,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婆殿,沒想到半個月后诈乒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡婆芦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年怕磨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寞缝。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡癌压,死狀恐怖仰泻,靈堂內(nèi)的尸體忽然破棺而出荆陆,到底是詐尸還是另有隱情,我是刑警寧澤集侯,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布被啼,位于F島的核電站,受9級特大地震影響棠枉,放射性物質(zhì)發(fā)生泄漏浓体。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一辈讶、第九天 我趴在偏房一處隱蔽的房頂上張望命浴。 院中可真熱鬧,春花似錦、人聲如沸生闲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碍讯。三九已至悬蔽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捉兴,已是汗流浹背蝎困。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留倍啥,地道東北人禾乘。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像虽缕,于是被迫代替她去往敵國和親盖袭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容