從一碗面想到的裝飾模式

裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個(gè)包裝。

情景

在廈門的小伙伴們一定對(duì)沙茶面很熟悉,作為廈門的一個(gè)特色小吃,大街小巷到處能看到掛有“沙茶面”招牌的面館,味道鮮美,可以加各種配料,是外地游客必吃的一種小吃。

假設(shè)面館現(xiàn)在要做一個(gè)沙茶面訂單系統(tǒng),能計(jì)算出每一碗面的價(jià)格。而面館有兩種面,一種沙茶面,一種清湯面,兩種面有不同的定價(jià),一開始我們可能會(huì)這樣設(shè)計(jì)的,定義兩種面:



每一碗面顧客可以加配料,有瘦肉、豬肝、魷魚等,每一種配料都有不同的價(jià)格,清湯面和沙茶面加了不同的料更是有多種價(jià)格,接下去怎么處理呢?可能你會(huì)想到用成員變量加繼承方式,如下:



這樣每一碗面都可以設(shè)置自己的配料,在計(jì)算價(jià)格調(diào)用cost()時(shí)就可以根據(jù)Nooddle對(duì)象是否含有對(duì)應(yīng)的配料,有的話再加上配料的價(jià)錢就能計(jì)算出這碗面的價(jià)格;

缺點(diǎn)

這樣處理有點(diǎn)問(wèn)題,假如現(xiàn)在面館又增加了一種配料,比如老板今天新進(jìn)了花蛤、肉丸,這時(shí)候Nooddle基類還得加上這兩種配料的成員,每加一種配料,基類就要改動(dòng)一次,這樣明顯違背了設(shè)計(jì)模式:對(duì)擴(kuò)展開發(fā),對(duì)修改關(guān)閉的重要原則。

方案

這時(shí)候我們就可以使用裝飾器模式了,它能使我們加每一種調(diào)料時(shí)可以不改變?cè)械拿娴膶傩?,極大地提高了靈活性和可擴(kuò)展性,下面介紹一下通過(guò)裝飾模式處理的步驟:

1. 創(chuàng)建Nooddle基類

抽象面條類有mDescription標(biāo)注是哪一種面條,mPrice為定價(jià),每一種面的價(jià)格和名稱都在子類重寫

public abstract class Nooddle {

    public String mDescription = "Unknow Nooddle";

    public float mPrice = 0.0f;

    public String getDescription(){
        return mDescription;
    }
    public float cost(){
        return mPrice;
    }
}
2. 實(shí)現(xiàn)兩種面:沙茶面和清湯面

假設(shè)沙茶面每碗不含任何配料定價(jià)15塊,清湯面每碗不含任何配料定價(jià)10塊

public class SatayNooddle extends Nooddle {
    @Override
    public String getDescription() {
        return "SatayNooddle";
    }
    @Override
    public float cost() {
        return 15.0f;
    }
}
public class LightSoupNooddle extends Nooddle {

    @Override
    public String getDescription() {
        return "LightSoupNooddle";
    }

    @Override
    public float cost() {
        return 10.0f;
    }
}
3. 創(chuàng)建抽象裝飾類

這個(gè)類持有一個(gè)Nooddle對(duì)象,而自己本身也是Nooddle類型,這樣能保持動(dòng)作一致性,都有cost()方法,而且cost可以在調(diào)用成員變量的cost()方法基礎(chǔ)上再加上自己額外的處理,即能計(jì)算被裝飾者的價(jià)格外還能加上自己的價(jià)格

abstract class DecorateNooddle extends Nooddle {

    public Nooddle mNooddle;

    public DecorateNooddle(Nooddle nooddle){
        mNooddle = nooddle;
    }
    @Override
    public String getDescription() {
        return mNooddle.getDescription() + mDescription;
    }
    @Override
    public float cost() {
        return mNooddle.cost() + mPrice;
    }
}
4. 實(shí)現(xiàn)兩種配料:瘦肉和豬肝,它們屬于裝飾者
public class LeanBurdening extends DecorateNooddle {

    public LeanBurdening(Nooddle nooddle) {
        super(nooddle);
    }
    @Override
    public String getDescription() {
        mDescription = " + Lean";
        return super.getDescription();
    }
    @Override
    public float cost() {
        mPrice = 1.5f;
        return super.cost();
    }
}
public class LiverBurdening extends DecorateNooddle {

    public LiverBurdening(Nooddle nooddle) {
        super(nooddle);
    }
    @Override
    public String getDescription() {
        mDescription = " + Liver";
        return super.getDescription();
    }
    @Override
    public float cost() {
        mPrice = 2.0f;
        return super.cost();
    }
}

這樣準(zhǔn)備工作就做好了,這里總結(jié)一下裝飾模式的工作流程,假設(shè)一個(gè)顧客點(diǎn)了一碗沙茶面,加了瘦肉和豬肝配料,這是就有如下處理:

1. 創(chuàng)建一個(gè)沙茶面對(duì)象;
2. 創(chuàng)建一個(gè)瘦肉配料對(duì)象,這個(gè)配料裝飾了沙茶面,即沙茶面對(duì)象是自己的成員變量,要計(jì)算價(jià)格時(shí)就可以把自己的價(jià)格機(jī)上沙茶面的價(jià)格;
3. 創(chuàng)建一個(gè)豬肝對(duì)象,同第2點(diǎn);
4. 由于裝飾對(duì)象兩種配料和被裝飾對(duì)象沙茶面都是Nooddle類型,所以計(jì)算價(jià)格時(shí)只需要計(jì)算最外圍的裝飾對(duì)象的價(jià)格就能得出這碗面的總價(jià)格了。

用如下代碼處理:

public class NooddleStoreDemo {

    public static void main(String[] args){

        Nooddle satayNooddle = new SatayNooddle();
        satayNooddle = new LiverBurdening(satayNooddle);
        satayNooddle = new LeanBurdening(satayNooddle);
        System.out.println(satayNooddle.getDescription() + ", price: " + satayNooddle.cost());

        Nooddle lightSoupNooddle = new LightSoupNooddle();
        lightSoupNooddle = new LiverBurdening(lightSoupNooddle);
        lightSoupNooddle = new LeanBurdening(lightSoupNooddle);
        System.out.println(lightSoupNooddle.getDescription() + ", price: " + lightSoupNooddle.cost());
    }
}

看下輸出的結(jié)果:

SatayNooddle + Liver + Lean, price: 18.5
LightSoupNooddle + Liver + Lean, price: 13.5

總結(jié)

一般的,我們?yōu)榱藬U(kuò)展一個(gè)類經(jīng)常使用繼承方式實(shí)現(xiàn),由于繼承為類引入靜態(tài)特征,并且隨著擴(kuò)展功能的增多,子類會(huì)很膨脹。這時(shí)就可以動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),這樣裝飾類和被裝飾類能獨(dú)立發(fā)展,不相互耦合,裝飾模式是繼承的一個(gè)替代模式,裝飾模式可以動(dòng)態(tài)擴(kuò)展一個(gè)實(shí)現(xiàn)類的功能。

示例代碼戳這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 復(fù)雜的獎(jiǎng)金計(jì)算## 考慮這樣一個(gè)實(shí)際應(yīng)用:就是如何實(shí)現(xiàn)靈活的獎(jiǎng)金計(jì)算。 獎(jiǎng)金計(jì)算是相對(duì)復(fù)雜...
    七寸知架構(gòu)閱讀 4,288評(píng)論 4 67
  • 標(biāo)簽: 設(shè)計(jì)模式初涉 描述性文字 還記得工廠方法模式小豬開的奶茶店嗎?在那一節(jié)中講解的是通過(guò)工廠方法模式來(lái)做奶茶,...
    coder_pig閱讀 546評(píng)論 1 2
  • 一、設(shè)計(jì)模式的分類 總體來(lái)說(shuō)設(shè)計(jì)模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    lichengjin閱讀 983評(píng)論 0 8
  • 辟谷養(yǎng)生,是通過(guò)心能量啟動(dòng)和開發(fā),同時(shí)運(yùn)用自然催眠等方式讓受試者不僅食物,只喝水,在身心放松的作用下,調(diào)節(jié)全身心,...
    活的像風(fēng)閱讀 529評(píng)論 0 0
  • 文~二嬤嬤 1. 一直酷愛(ài)港劇的我,看完了電影《澳門風(fēng)云3》,追新劇,也不厭其煩的重復(fù)看過(guò)的電視劇。而最近看的幾步...
    二嬤嬤閱讀 1,299評(píng)論 7 22

友情鏈接更多精彩內(nèi)容