延續上篇的範例,要來看看怎麼去實作裝飾者模式,簡單來說我們的目的就是要在不動到Beverage()
的情況下,用調味料去裝飾它,進而做出我們要的飲料,飲料有各自的價格,多加調味料就會按照調味料價格加價。
首先是飲料的抽象類別,也可以用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://github.com/changtintin/Design-Pattern/tree/master/Ch3/Java/Starbuzz
Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!