設(shè)計(jì)模式之裝飾器模式

0x01 前言


在編碼的時(shí)候倡勇,我們?yōu)榱藬U(kuò)展一個(gè)類常用的方法就是繼承山害,隨著擴(kuò)展功能越來越多如叼,子類會(huì)變得越來越膨脹冰木,
使系統(tǒng)變得不靈活。

裝飾器模式(Decorator pattern)允許像一個(gè)現(xiàn)有的對(duì)象添加新功能,同時(shí)又不改變其結(jié)構(gòu)踊沸,他能讓我們?cè)跀U(kuò)展類的同時(shí)歇终,系統(tǒng)保持較好的靈活性。

0x02 廢話不多說雕沿,進(jìn)入正題练湿,從一個(gè)場(chǎng)景開始


假設(shè)我們?cè)诒本┯幸粔K地,在這塊地上我們要蓋有好幾間房間的別墅审轮,每個(gè)房間的裝修費(fèi)用都不同肥哎,現(xiàn)在呢,我們需要對(duì)每個(gè)房間的費(fèi)用進(jìn)行計(jì)算并計(jì)算出別墅的價(jià)格疾渣。

//定義一個(gè)land表示這塊地
public interface Land {
     // 定義蓋別墅需要花錢的規(guī)則
    Integer cost();
}

那么land定義好了在這塊地上花錢的規(guī)則篡诽,但是蓋一間房需要多少錢呢?

此時(shí)我們定義一個(gè)Room類榴捡,這個(gè)類定義了一間房間需要建造的基本費(fèi)用杈女。

public class Room implements Land{
    
    protected final int money = 1000 ;

    @Override
    public Integer cost() {
        return money ;
    }

}

開始建造房間,房間建造了兩間吊圾,一間客廳(LivingRoom)达椰,一間餐廳(DiningRoom),建造完之后發(fā)現(xiàn)偌大房間連件家具都沒有项乒,這時(shí)候啰劲,我們計(jì)算一間房成本的時(shí)候需要加上家具的價(jià)格,那么檀何,上代碼...

public class LivingRoom extends Room{
    // 客廳
    @Override
    public Integer cost() {
        return this.land.cost()+200;
    }
}

public class DiningRoom extends Room{
    // 餐廳
    @Override
    public void cost() {
        return this.land.cost()+100;
    }
}

所以機(jī)智如你很容易就得到了建造一間客廳和一間餐廳的花費(fèi)..

public static void main(String[] args) {
        LivingRoom livingRoom = new LivingRoom();
        System.out.println("建造一間客廳需要花費(fèi)"+livingRoom.cost());
        DiningRoom diningRoom = new DiningRoom();
        System.out.println("建造一間餐廳需要花費(fèi)"+diningRoom.cost());
    }
// 得到結(jié)果
建造一間客廳需要花費(fèi)1200
建造一間餐廳需要花費(fèi)1100

那么蝇裤,問題來了:挖掘機(jī)...(啪,好好說啊频鉴,挖掘機(jī)是什么鬼啊栓辜,喂)emmmmm,好接著說:

0x03 問題的產(chǎn)生


這么做實(shí)現(xiàn)確實(shí)很容易垛孔,雖然我們很容易就得到了建造一間客廳和一間餐廳需要的花費(fèi)藕甩,但是這樣的結(jié)構(gòu)不具備靈活性,如果....我是說如果我的地比較小不能同時(shí)建造兩間房子似炎,只能把客廳和餐廳建在同一個(gè)屋子里辛萍,那么該去怎么計(jì)算費(fèi)用?難道羡藐,還要很麻煩得去創(chuàng)建一個(gè)包含LivingDiningRoom類嗎贩毕?這樣做除了麻煩,而且還會(huì)使代碼重復(fù)仆嗦。

既然提出了問題辉阶,那么,肯定有解決的辦法,怎么解決呢谆甜?
欲知后事如何..(pia垃僚,知你妹啊,還來规辱?)谆棺,好吧,下面是解決辦法罕袋。

0x04 解決問題


(敲黑板8氖纭!)重點(diǎn)來了浴讯,為了更好的解決問題朵夏,我們需要做一些調(diào)整,同樣先聲明Land接口(抽象構(gòu)件榆纽,是具體構(gòu)件和抽象構(gòu)件的共同父類)和Room類( ConcreteComponent 仰猖,具體構(gòu)件,抽象構(gòu)件的子類奈籽,裝飾器可以額外添加職責(zé)饥侵。),不同的是引入房間的一個(gè)裝飾類RoomDecorator他實(shí)現(xiàn)了Land接口衣屏,因?yàn)闆]有實(shí)現(xiàn)Land的cost方法爆捞,所以需要聲明為抽象類,并且定義一個(gè)Land接口為參數(shù)的構(gòu)造函數(shù)勾拉,傳入對(duì)象會(huì)保存在land中,該屬性修飾符為protected方便子類訪問盗温。(劃重點(diǎn)藕赞,考試要考)

public abstract class RoomDecorator implements Land{
    // 修飾符protected子類訪問
    protected Land land ;
    
    public RoomDecorator(Land land) {  // 參數(shù)為實(shí)現(xiàn)Land接口的所有子類
        this.land = land ;
    }
}

我們重新定義客廳類和餐廳類

public class LivingRoom extends RoomDecorator{

    public LivingRoom(Land land) {
        super(land);
    }

    @Override
    public Integer cost() {
        return this.land.cost()+200;
    }
}

public class DiningRoom extends RoomDecorator{

    public DiningRoom(Land land) {
        super(land);
    }

    @Override
    public Integer cost() {
        return this.land.cost()+100;
    }
}

這兩個(gè)類都繼承了RoomDecorator,這意味著它們同時(shí)擁有指向Land接口的引用卖局,當(dāng)它們的cost()方法被調(diào)用時(shí)斧蜕,都會(huì)先調(diào)用實(shí)現(xiàn)了Land接口的子類的cost()方法,然后執(zhí)行自己特有的操作砚偶。

所以這時(shí)候建造一間客廳和一間餐廳所需的費(fèi)用是這么計(jì)算的:

public static void main(String[] args) {
        LivingRoom livingRoom = new LivingRoom(new Room());
        System.out.println("建造一間客廳需要花費(fèi)"+livingRoom.cost());
        
        DiningRoom diningRoom = new DiningRoom(new Room());
        System.out.println("建造一間餐廳需要花費(fèi)"+diningRoom.cost());
    }
// 輸出
建造一間客廳需要花費(fèi)1200
建造一間餐廳需要花費(fèi)1100

接著回到剛才的問題:建造一間有客廳又有餐廳的房間所需費(fèi)用批销,代碼如下

public static void main(String[] args) {
        LivingRoom diningLivingRoom = new LivingRoom(new DiningRoom(new Room()));
        System.out.println("建造一間客廳和餐廳需要花費(fèi)"+diningLivingRoom.cost());
    }
// 輸出
建造一間客廳和餐廳需要花費(fèi)1300

我們計(jì)算建造費(fèi)用的思路是:我們計(jì)算一間房的基礎(chǔ)費(fèi)用-->在基礎(chǔ)房間裝飾為客廳的費(fèi)用-->在客廳的基礎(chǔ)上加上裝飾餐廳的費(fèi)用-->得到包含客廳餐廳房間的費(fèi)用,已經(jīng)不需要通過麻煩的創(chuàng)建LivingDiningRoom類來計(jì)算包含餐廳客廳房間的建造費(fèi)用了染坯。

這便是裝飾模式均芽,通過一層層的裝飾得到我們可以靈活的得到想要的結(jié)果,可以輕松地添加組件或者裝飾類來創(chuàng)建靈活的結(jié)構(gòu)单鹿。

0x05 完整代碼

//定義一個(gè)land表示這塊地
//  Component:組件對(duì)象的接口掀宋,可以給這些對(duì)象動(dòng)態(tài)的添加職責(zé)
public interface Land {
     // 定義蓋別墅需要花錢的規(guī)則
    Integer cost();
}
// ConcreteComponent:具體的組件對(duì)象,實(shí)現(xiàn)了組件接口。該對(duì)象通常就是被裝飾器裝飾的原始對(duì)象劲妙,可以給這個(gè)對(duì)象添加職責(zé)湃鹊;
public class Room implements Land{
    protected final int money = 1000 ;
    @Override
    public Integer cost() {
        return money;
    }
}

// Decorator:所有裝飾器的父類,需要定義一個(gè)與組件接口一致的接口
public abstract class RoomDecorator implements Land{
    // 修飾符protected子類訪問
    protected Land land ;
    public RoomDecorator(Land land) {
        this.land = land ;
    }
}

// 裝飾類具體構(gòu)件
public class LivingRoom extends RoomDecorator{
    public LivingRoom(Land land) {
        super(land);
    }
    @Override
    public Integer cost() {
        return this.land.cost()+200;
    }
}
// 裝飾類具體構(gòu)件
public class DiningRoom extends RoomDecorator{
    public DiningRoom(Land land) {
        super(land);
    }
    @Override
    public Integer cost() {
        return this.land.cost()+100;
    }
}

// 測(cè)試
public class RoomTest{
    public static void main(String[] args) {
        LivingRoom livingRoom = new LivingRoom(new Room());
        System.out.println("建造一間客廳需要花費(fèi)"+livingRoom.cost());
        
        DiningRoom diningRoom = new DiningRoom(new Room());
        System.out.println("建造一間餐廳需要花費(fèi)"+diningRoom.cost());
        
        LivingRoom diningLivingRoom = new LivingRoom(new DiningRoom(new Room()));
        System.out.println("建造一間客廳和餐廳需要花費(fèi)"+diningLivingRoom.cost());
    }
}

0x06 The End...


Tips:在北京有塊地一輩子都不可能的镣奋,如果可以币呵,我愿意偷電動(dòng)車養(yǎng)你,點(diǎn)顆心在走嘛侨颈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末余赢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肛搬,更是在濱河造成了極大的恐慌没佑,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件温赔,死亡現(xiàn)場(chǎng)離奇詭異蛤奢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)陶贼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門啤贩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拜秧,你說我怎么就攤上這事痹屹。” “怎么了枉氮?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵志衍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我聊替,道長(zhǎng)楼肪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任惹悄,我火速辦了婚禮春叫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泣港。我一直安慰自己暂殖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布当纱。 她就那樣靜靜地躺著呛每,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坡氯。 梳的紋絲不亂的頭發(fā)上莉给,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天毙石,我揣著相機(jī)與錄音,去河邊找鬼颓遏。 笑死徐矩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叁幢。 我是一名探鬼主播滤灯,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼曼玩!你這毒婦竟也來了鳞骤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤黍判,失蹤者是張志新(化名)和其女友劉穎豫尽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顷帖,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡美旧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贬墩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榴嗅。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陶舞,靈堂內(nèi)的尸體忽然破棺而出嗽测,到底是詐尸還是另有隱情,我是刑警寧澤肿孵,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布唠粥,位于F島的核電站,受9級(jí)特大地震影響停做,放射性物質(zhì)發(fā)生泄漏厅贪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一雅宾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧葵硕,春花似錦眉抬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至介评,卻和暖如春库北,著一層夾襖步出監(jiān)牢的瞬間爬舰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工寒瓦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留情屹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓杂腰,卻偏偏與公主長(zhǎng)得像垃你,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喂很,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355