1. 程式人生 > >每天一個設計模式之裝飾者模式

每天一個設計模式之裝飾者模式

什麼是“裝飾者模式”

裝飾者模式:裝飾者模式可以動態地將責任附加到物件上。若要拓展功能,裝飾者模式提供了比繼承更有彈性的替代方案。

在面向物件設計過程中,我們可以遵循一個原則:開閉原則(對拓展開放,對修改關閉)

而裝飾者模式就很好的遵循了這個原則,在對類進行拓展的時候可以不修改程式碼,以便搭配新的行為。這種設計更具有彈性,可以更好地應對改變和接收新的需求。

為什麼要用“裝飾者模式”

我們舉個簡單的例子:

我們需要設計一個咖啡訂單系統,我們很容易就可以想到,新建一個抽象類Beverage

/**
 * 咖啡的介面
 * */
public abstract class
Beverage {
String description = "這只是個咖啡,我也不知道是什麼咖啡"; public String getDescription() { return description; } public abstract double cost(); }

然後通過子類繼承Beverage並實現抽象方法cost(),得到不同的價格。

那麼問題來了: 如果我要在咖啡中新增各種調料,例如:蒸奶、豆漿、摩卡、奶泡等等。 新增不同的調料收取的價格不同,我們該怎麼辦?有些人會想到,根據不同的搭配組合實現不同的類,那麼這個想法會發生什麼事情呢?沒錯。。。類爆炸!

如何使用裝飾者模式

那麼這個時候,裝飾者模式就可以上場了:

我們首先來看看裝飾者模式的類圖

裝飾者模式類圖

組成

  • Component 抽象構件 :一個抽象類或者一個介面
  • ComcreateComponent 具體構件:抽象類或者介面的實現,我們要裝飾的就是它(咖啡)
  • Decorator 裝飾角色: 一個抽象類或者一個介面,裡面必然有一個指向Component 抽象構件的屬性
  • 具體裝飾角色:裝飾用的具體角色,實現或繼承了Decorator 裝飾角色

實現

瞭解了具體的組成角色,那麼我們就可以實現一下了

首先,根據類圖我們需要準備一個Component 抽象構件,其實我們在文章開始就已經準備好了:

/**
 * 咖啡的介面
 * */
public abstract class Beverage {
    String description = "這只是個咖啡,我也不知道是什麼咖啡";

    public String getDescription() {
        return description;
    }
    public abstract double cost();
}

接下來,我們準備一個ComcreateComponent 具體構件,也就是需要被我們裝飾的物件(這裡是咖啡)

/**
 * 通過繼承Beverage抽象類,可以根據不同的咖啡定義不同的價格
 * */
public class Espresso extends Beverage {
    public Espresso() {
        super.description = "濃縮咖啡";
    }

    @Override
    public double cost() {
        return 18.8;
    }
}

然後,我們準備一個Decorator 裝飾角色

 **
 * Decorator 裝飾角色, 繼承Beverage就可以對它的價格進行修改
 * */
public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();

}

最後,我們實現一個這個Decorator 裝飾角色抽象類,使它變成具體的裝飾角色

public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    //新增上摩卡
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 摩卡";
    }
    //將價格改變為新增摩卡後的價格
    @Override
    public double cost() {
        return beverage.cost() + 2.0;
    }
}

/*=======================================================*/

public class Whip extends CondimentDecorator {
    Beverage beverage;

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 奶泡";
    }

    @Override
    public double cost() {
        return beverage.cost() + 1.0;
    }
}

//其實我們可以多實現幾種調料,這樣可能會吸引更多顧客呢

完成之後,我們測試一下被我們裝飾後的咖啡長什麼樣:

public class StarbuzzCoffee {
    public static void main(String[] args){

        Beverage beverage = new Espresso();
        beverage = new Mocha(beverage);
        beverage = new Whip(beverage);
        System.out.println(beverage.getDescription() + " ¥" + beverage.cost());
    }

輸出結果:

濃縮咖啡, 摩卡, 奶泡 ¥21.8

從輸出結果看,我們確實根據調料改變了濃縮咖啡的價格,所以這就是裝飾者模式的好處。

那麼,總結來了

優點:

  • 裝飾類和被裝飾類可以獨立發展,不會互相耦合。
  • 裝飾者模式是繼承的一個替代方案。我們不管裝飾多少層,最終返回的還是該物件。
  • 裝飾者模式可以動態拓展一個實現類的功能。

缺點:

  • 裝飾層數過多的話,會導致除錯困難,系統複雜度提高。

應用場景:

  • 需要動態地拓展某個類的功能或者新增一個附加功能。
  • 需要為一批兄弟類新增功能或者改裝,首選裝飾者模式。

感謝閱讀本部落格。

需要聯絡請加QQ:352642663

歡迎聯絡我共同交流