裝飾者(裝飾器)模式

裝飾器模式

裝飾器模式(Decorator Pattern)允許向一個現(xiàn)有的對象添加新的功能荡碾,同時又不改變其結構矾睦。這種類型的設計模式屬于結構型模式勾栗,它是作為現(xiàn)有類的一個包裝苍苞。裝飾器在代碼程序中適用于以下場景:

  1. 用于擴展 一個類的功能或者給一個類添加附加職責
  2. 動態(tài)的給一個對象添加功能狰晚,這些功能可以再動態(tài)的撤銷

為什么要用裝飾器模式

裝飾器模式能很好的解決過多的繼承所帶來的問題筒饰,下面我們通過一個例子來看看裝飾器模式的作用。生活中我們很多人都很喜歡喝奶茶壁晒,只喝奶茶又感覺有點太單調瓷们,會加一些配料,比如珍珠秒咐、脆啵啵谬晕、芋圓等,下面我們通過代碼來模擬一下携取。

最原始不放任何調料的特調奶茶

/**
 * @author: Winston
 * @createTime: 2021/7/1
 */
public class MilkTea {

    public String info() {
        return "特調奶茶";
    }

    public double price() {
        return 8D;
    }

}

由于我想吃點珍珠攒钳,于是我通過繼承的方式給奶茶增加珍珠

public class MilkTearWithPearl extends MilkTea {
    @Override
    public String info() {
        return super.info() + "加珍珠";
    }

    @Override
    public double price() {
        // 奶茶加上珍珠的價格
        return super.price() + 2D;
    }
}

我還想要一杯珍珠加上脆啵啵的奶茶

public class MilkTearWithPearlAndBoo extends MilkTea {
    @Override
    public String info() {
        return super.info() + "加珍珠加脆啵啵";
    }

    @Override
    public double price() {
        // 加珍珠加脆啵啵后的價格
        return super.price() + 2D + 3D;
    }
}

我同事想要一杯加珍珠加脆啵啵加西米露的奶茶

public class MilkTearWithPearlAndBooAndSago extends MilkTea {
    @Override
    public String info() {
        return super.info() + "加珍珠加脆啵啵加西米露";
    }

    @Override
    public double price() {
        // 加珍珠加脆啵啵加西米露后的價格
        return super.price() + 2D + 3D + 2D;
    }
}

測試類

public class MilkTeaTest {
    public static void main(String[] args) {
        MilkTea milkTea = new MilkTea();
        System.out.println(milkTea.info() + ",總價格:"+milkTea.price());
        System.out.println("====================================================");

        MilkTearWithPearl milkTearWithPearl = new MilkTearWithPearl();
        System.out.println(milkTearWithPearl.info() + "雷滋,總價格:"+milkTearWithPearl.price());
        System.out.println("====================================================");

        MilkTearWithPearlAndBoo milkTearWithPearlAndBoo = new MilkTearWithPearlAndBoo();
        System.out.println(milkTearWithPearlAndBoo.info() + "不撑,總價格:"+milkTearWithPearlAndBoo.price());
        System.out.println("====================================================");

        MilkTearWithPearlAndBooAndSago milkTearWithPearlAndBooAndSago = new MilkTearWithPearlAndBooAndSago();
        System.out.println(milkTearWithPearlAndBooAndSago.info() + ",總價格:"+milkTearWithPearlAndBooAndSago.price());
        System.out.println("====================================================");

    }
}
特調奶茶晤斩,總價格:8.0
====================================================
特調奶茶加珍珠焕檬,總價格:10.0
====================================================
特調奶茶加珍珠加脆啵啵,總價格:13.0
====================================================
特調奶茶加珍珠加脆啵啵加西米露澳泵,總價格:15.0
====================================================

從上面的例子我們可以看出只要加的小料不同那么我就要新創(chuàng)建一個類來繼承奶茶類MikTea实愚,如果只有三種料珍珠、芋圓、脆啵啵不重復添加爆侣,根據(jù)排列組合就有6種方式萍程,更何況有可能有的人會要雙份珍珠,這么一來我們的類就特別特別多兔仰,非常的冗余茫负。有沒有什么方式能夠進行改造呢?下面我們就用裝飾器模式將上面的案例進行改造乎赴,讓大家體會一下用了它有何不同忍法。

裝飾器模式改造案例

首先先創(chuàng)造一個奶茶的抽象類MilkTea,所有的奶茶都必須有介紹信息和價格信息

public abstract class MilkTea {

    /**
     * 奶茶中的信息
     */
    protected abstract String info();

    /**
     * 奶茶總價格,這個方法需要在具體實現(xiàn)類中實現(xiàn)
     *
     * @return
     */
    protected abstract double price();

}

創(chuàng)造一個基礎款奶茶榕吼,要繼承基類MilkTea

public class BaseMilkTea extends MilkTea{

    private String info = "本店招牌特調奶茶";

    @Override
    protected String info() {
        return this.info;
    }

    @Override
    protected double price() {
        return 12D;
    }
}

再創(chuàng)建一個巧克力奶茶類饿序,同樣要繼承基類MilkTea

public class ChocolateMilkTea extends MilkTea {

    private String info = "巧克力奶茶";
    
    @Override
    protected String info() {
        return this.info;
    }

    @Override
    protected double price() {
        return 15D;
    }
}

創(chuàng)建一個奶茶的裝飾類MilkTeaDecorate,同樣這個類也繼承MilkTea羹蚣,這里大家可能會有疑問原探,既然MilkTeaDecorate這個抽象類的方法和MilkTea抽象類的方法一樣為什么還要單獨寫一個類。這里的話是為了能夠區(qū)分出哪個是裝飾者顽素,哪個是被裝飾者咽弦。

public abstract class MilkTeaDecorate extends MilkTea {
    /**
     * 要添加小料的奶茶,通過構造函數(shù)傳入奶茶信息
     */
    public MilkTea milkTea;

    public MilkTeaDecorate(MilkTea milkTea) {
        this.milkTea = milkTea;
    }


    /**
     * 所有的調料裝飾者都必須重新實現(xiàn)info()方法
     * 這樣才能夠得到所選奶茶的整體描述
     *
     * @return
     */
    @Override
    protected String info() {
        return this.info();
    }

    /**
     * 所有的調料裝飾者都必須重新實現(xiàn)price()方法
     * 這樣才能夠得到所選奶茶的整體價格
     *
     * @return
     */
    @Override
    protected double price() {
        return this.price();
    }
}

編寫珍珠裝飾類

public class PearlDecorate extends MilkTeaDecorate {


    public PearlDecorate(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    protected double price() {
        return this.milkTea.price() + 2D;
    }

    @Override
    protected String info() {
        return this.milkTea.info() + "加一份珍珠";
    }
}

脆啵啵裝飾類

public class BooDecorate extends MilkTeaDecorate {


    public BooDecorate(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    protected double price() {
        return this.milkTea.price() + 3D;
    }

    @Override
    protected String info() {
        return this.milkTea.info() + "加一份波波";
    }
}

西米露裝飾類

public class SagoDecorate extends MilkTeaDecorate {

    public SagoDecorate(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    protected double price() {
        return this.milkTea.price() + 3D;
    }

    @Override
    protected String info() {
        return this.milkTea.info() + "加一份西米露";
    }
}

測試類:

public class MilkTeaTest {
    public static void main(String[] args) {
        //1.一份原始的特調奶茶
        System.out.println("=======================================");
        MilkTea milkTea;
        milkTea = new BaseMilkTea();
        System.out.println(milkTea.info() + "總價價格:" + milkTea.price());
        // 想要加一份珍珠
        milkTea = new PearlDecorate(milkTea);
        System.out.println(milkTea.info() + "總價價格:" + milkTea.price());
        // 加一份脆啵啵
        milkTea = new BooDecorate(milkTea);
        System.out.println(milkTea.info() + "總價價格:" + milkTea.price());

        //1.一份巧克力奶茶
        System.out.println("=======================================");
        MilkTea milkTea2;
        milkTea2 = new ChocolateMilkTea();
        System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
        // 想要加一份珍珠
        milkTea2 = new PearlDecorate(milkTea2);
        System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
        // 加一份脆啵啵
        milkTea2 = new BooDecorate(milkTea2);
        System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
        // 再加一份珍珠
        milkTea2 = new PearlDecorate(milkTea2);
        System.out.println(milkTea2.info() + "總價價格:" + milkTea2.price());
        System.out.println("=======================================");
    }
}

結果:

=======================================
本店招牌特調奶茶總價價格:12.0
本店招牌特調奶茶加一份珍珠總價價格:14.0
本店招牌特調奶茶加一份珍珠加一份波波總價價格:17.0
=======================================
巧克力奶茶總價價格:15.0
巧克力奶茶加一份珍珠總價價格:17.0
巧克力奶茶加一份珍珠加一份波波總價價格:20.0
巧克力奶茶加一份珍珠加一份波波加一份珍珠總價價格:22.0
=======================================

案例小結

通過上面這個案例胁出,我們可以看出裝飾者(裝飾器)模式的特點型型。

  1. 裝飾者類擁有被裝飾者類的對象,一般是當做構造函數(shù)傳入的全蝶。
  2. 在裝飾者類當中調用被裝飾者類的方法闹蒜,封裝成新的功能方法。
  3. 裝飾者模式主要是利用多態(tài)抑淫,將子類對象作為參數(shù)互相傳遞(主要是為了傳遞實現(xiàn)的函數(shù))绷落,達到互相裝飾的效果,從而減少代碼重復率丈冬,優(yōu)化代碼結構嘱函。

從上面的例子我們又可以看出跟靜態(tài)代理有所相似,靜態(tài)代理的被代理類和代理類都實現(xiàn)了同一接口埂蕊,都是對類功能進行加強往弓。這邊裝飾者模式和靜態(tài)代理有何區(qū)別的?裝飾者模式和靜態(tài)代理的最大區(qū)別就是職責不同蓄氧。代理模式(Proxy Pattern)函似,為其它對象提供一種代理以控制對這個對象的訪問。裝飾模式(Decorator Pattern)喉童,動態(tài)地給一個對象添加一些額外的職責撇寞。換句話說:代理模式的目標是控制對被代理對象的訪問,而裝飾模式是給原對象增加額外功能。雖然代理模式也可以實現(xiàn)對被代理對象功能的增強蔑担,但其核心是隱藏對被代理類的訪問牌废。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啤握,隨后出現(xiàn)的幾起案子鸟缕,更是在濱河造成了極大的恐慌,老刑警劉巖排抬,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懂从,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹲蒲,警方通過查閱死者的電腦和手機番甩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來届搁,“玉大人缘薛,你說我怎么就攤上這事】溃” “怎么了掩宜?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長么翰。 經(jīng)常有香客問我,道長辽旋,這世上最難降的妖魔是什么浩嫌? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮补胚,結果婚禮上码耐,老公的妹妹穿的比我還像新娘。我一直安慰自己溶其,他們只是感情好骚腥,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓶逃,像睡著了一般束铭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厢绝,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天契沫,我揣著相機與錄音,去河邊找鬼昔汉。 笑死懈万,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播会通,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼口予,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涕侈?” 一聲冷哼從身側響起沪停,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驾凶,沒想到半個月后牙甫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡调违,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年窟哺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片技肩。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡且轨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虚婿,到底是詐尸還是另有隱情旋奢,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布然痊,位于F島的核電站至朗,受9級特大地震影響,放射性物質發(fā)生泄漏剧浸。R本人自食惡果不足惜锹引,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唆香。 院中可真熱鬧嫌变,春花似錦、人聲如沸躬它。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冯吓。三九已至倘待,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桑谍,已是汗流浹背延柠。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锣披,地道東北人贞间。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓贿条,卻偏偏與公主長得像,于是被迫代替她去往敵國和親增热。 傳聞我的和親對象是個殘疾皇子整以,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

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