iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0
自我挑戰組

深入淺出設計模式 (Head First Design Pattern) - 重點整理及範例分享系列 第 9

[深入淺出設計模式] Ch2 The Decorator Pattern (2) 【裝飾者模式】範例 Starbuzz Order System

  • 分享至 

  • xImage
  •  

延續上篇的範例,要來看看怎麼去實作裝飾者模式,簡單來說我們的目的就是要在不動到Beverage()的情況下,用調味料去裝飾它,進而做出我們要的飲料,飲料有各自的價格,多加調味料就會按照調味料價格加價。

https://ithelp.ithome.com.tw/upload/images/20230923/20163178iKsHTnS1VO.png

首先是飲料的抽象類別,也可以用Interface,getIngredient()可以讓我們看到總共有哪些調味料,cost()會加總所有調味料及飲料的價錢來算出總價。

public abstract class Beverage {
    public String description;
    public String getIngredient(){
        return description;
    }
    abstract public int cost();
}

以下是不同的飲料基底,繼承Beverage(),定義自己的價錢和描述。

public class DarkRoast extends Beverage{
    public DarkRoast(){
        description = " DarkRoast ";
    }

    public int cost(){
        return 120;
    }
}

public class Espresso extends Beverage{
    public Espresso(){
        description = " Espresso ";
    }

    public int cost(){
        return 100;
    }
}

public class Latte extends Beverage{
    public Latte(){
        description = " Latte ";
    }

    public int cost(){
        return 150;
    }
}

這邊的繼承在意義上主要是不同的飲料及調味料都需要跟Beverage有相同的行為,但並不像前篇在不同子類別中去計算各自成本,接下來我們會用調味料也就是裝飾器CondimentDecorator() 去完成它與飲料的複合。
調味料的類別都包含Beverage,可以讓我們把未裝飾或是已經裝飾的飲料放進來,用getDescription()一次獲得所有調味料,同時用cost()拿到飲料目前為止的總價。


public abstract class CondimentDecorator extends Beverage{
    public abstract String getIngredient();
}

public class Chocolate extends CondimentDecorator{
    Beverage beverage;

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

    public String getIngredient(){
        return "Chocolate " + beverage.getIngredient();
    }

    public int cost(){
        return 40 + beverage.cost();
    }
}

public class Milk extends CondimentDecorator{
    Beverage beverage;

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

    public String getIngredient(){
        return "Milk " + beverage.getIngredient();
    }

    public int cost(){
        return 30 + beverage.cost();
    }
}

public class Strawberry extends CondimentDecorator{
    Beverage beverage;

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

    public String getIngredient(){
        return "Strawberry " + beverage.getIngredient();
    }

    public int cost(){
        return 75 + beverage.cost();
    }
}

public class WhipCream extends CondimentDecorator{
    Beverage beverage;

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

    public String getIngredient(){
        return "WhipCream"+" "+beverage.getIngredient();
    }

    public int cost(){
        return 30 + beverage.cost();
    }
}

到這邊大家可能會覺得有點奇怪,繼承就是繼承,為什麼冠上一個裝飾器模式的名字突然就變得比較「彈性」?
舉例來說,如果我們用DarkRoast()去繼承其他調味料,或是用調味料去繼承飲料,飲料和調味料種類一多,繼承關係就會變成八點擋,我爸爸是我阿公哥哥是養子所以愛上我。
假如今天要做出一杯雙倍抹茶鮮奶油咖啡,可能要用咖啡去繼承抹茶和鮮奶油,更不用說我們要怎麼繼承兩次抹茶。
所以裝飾器模式的優點就是可以去彈性擴充原有的類別,可以重複添加裝飾,完成多種組合。

最後是實際跑一次看看~

public class orderSystem {
    public static void main(String arg[]){
        Beverage firstDrink = new Latte();
        firstDrink = new WhipCream(firstDrink);
        firstDrink = new Chocolate(firstDrink);
        System.out.printf(firstDrink.getIngredient());
        System.out.println(": TWD$ "+firstDrink.cost());

        Beverage secDrink = new DarkRoast();
        secDrink = new Mocha(secDrink);
        secDrink = new Mocha(secDrink);
        secDrink = new Strawberry(secDrink);
        secDrink = new Milk(secDrink);

        System.out.printf(secDrink.getIngredient());
        System.out.println(": TWD$ "+secDrink.cost());
    }
}

輸出結果
https://ithelp.ithome.com.tw/upload/images/20230924/20163178ToRFJNolnH.png


參考資料:

  1. 《深入淺出設計模式 (Head First Design Patterns) 》
  2. 書中官方程式碼傳送門

本篇程式碼:

https://github.com/changtintin/Design-Pattern/tree/master/Ch3/Java/Starbuzz


Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!


上一篇
[深入淺出設計模式] Ch2 The Decorator Pattern (1) 【裝飾者模式】
下一篇
[深入淺出設計模式] 聊一聊目前為止設計模式改變了我什麼?
系列文
深入淺出設計模式 (Head First Design Pattern) - 重點整理及範例分享35
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言