裝飾者模式: 動態地將責任附加到對象上。想要擴展功能,裝飾者提供有別於繼承的另一種選擇。 舉例: 不知道大家學校的食堂是什麼點餐制度(或者大家就直接想成吃火鍋,我們要火鍋料 + 配菜),我們學校的點餐是:主食大米 + 你想要吃的菜(每個菜都裝在小碗中)。現在問題來了,我點的是大米(0.8元) + 紅 ...
裝飾者模式:
動態地將責任附加到對象上。想要擴展功能,裝飾者提供有別於繼承的另一種選擇。
舉例:
不知道大家學校的食堂是什麼點餐制度(或者大家就直接想成吃火鍋,我們要火鍋料 + 配菜),我們學校的點餐是:主食大米 + 你想要吃的菜(每個菜都裝在小碗中)。現在問題來了,我點的是大米(0.8元) + 紅燒茄子(2.0元) + 荔枝肉(3.5元) + 一個雞腿(3.5元) + 炒土豆(1.0元),一共10.8元。
這隻是我想要吃的食品,每個同學點餐都不一樣,所以價格會隨著所點食物的不同而不同。還有一點就是我們點餐都要點米飯(這是規定,沒有那麼多理由,就是這麼任性。其實不然,我們需要有個主對象去修飾),那麼我們應該怎樣實現這樣的點餐計價方式呢?
生活中的實例:
有一家咖啡店,它提供給我們如下代碼:
1 public abstract class Beverage{//飲料抽象類 2 String description = "Unknown Beverage";//飲料名稱 3 4 public String getDescription(){//獲取飲料名稱的方法 5 return description; 6 } 7 8 public abstract double cost();//飲料價格 9 }Beverage
這家咖啡店有主飲料(相當於米飯):Espresso(濃縮咖啡1.99$)、HouseBlend(綜合咖啡0.89$)、DarkRoast(深焙0.99$)、Decaf(低咖啡因1.05$)四種
還有各種配料:Milk(牛奶0.10$)、Mocha(摩卡0.20$)、Soy(豆漿0.15$)、Whip(奶泡0.10$)四種
咖啡店想要做出一個點飲料機器,可以點一種主飲料再從四種配料中任意選1到4中配料。該機器可以算出最後的總價,並且可以顯示都點了那些東西。
分析:
主飲料全部繼承自Beverage抽象類,因為店家已經給了Beverage抽象類,我們只能採用繼承方式來實現四種主飲料類,如果沒有給建議使用介面方式實現(多用組合,少用繼承。)
主飲料我們只實現其中兩種:
1 public class Espresso extends Beverage{//濃縮咖啡類 2 3 public Espresso(){ 4 description = "Espresso"; 5 } 6 7 public double cost(){ 8 return 1.99; 9 } 10 }Espresso
1 public class HouseBlend extends Beverage{//綜合咖啡類 2 public HouseBlend(){ 3 description = "House Blend Coffee"; 4 } 5 6 public double cost(){ 7 return .89; 8 } 9 }HouseBlend
配料也繼承自Beverage,不過我們在中間加一個橋梁,將主飲料和配料區別對待,這樣對於以後的修改有好處:
1 public abstract class CondimentDecorator extends Beverage{//配料介面 2 public abstract String getDescription(); 3 }CondimentDecorator
所有配料都繼承自上面的CondimentDecorator介面:
配料我們實現其中三種:
1 public class Mocha extends CondimentDecorator {//配料:摩卡類 2 Beverage beverage;//每個配料中都有一個Beverage對象,該對象存儲的是配料修飾的產品。 3 //關鍵就在這個對象,最後結算的時候,我們可以調用該對象的getDescription和cost方法 4 //我稱它為盒子裡面套盒子 5 6 public Mocha(Beverage beverage){ 7 this.beverage = beverage; 8 } 9 10 public String getDescription(){ 11 return beverage.getDescription() + ", Mocha";//調用beverage對象的getDescription()方法 12 } 13 14 public double cost(){ 15 return .20 + beverage.cost();//調用beverage對象的cost()方法 16 } 17 }Mocha
1 public class Soy extends CondimentDecorator { 2 Beverage beverage; 3 4 public Soy(Beverage beverage){ 5 this.beverage = beverage; 6 } 7 8 public String getDescription(){ 9 return beverage.getDescription() + ", Soy"; 10 } 11 12 public double cost(){ 13 return .15 + beverage.cost(); 14 } 15 }Soy
1 public class Whip extends CondimentDecorator { 2 Beverage beverage; 3 4 public Whip(Beverage beverage){ 5 this.beverage = beverage; 6 } 7 8 public String getDescription(){ 9 return beverage.getDescription() + ", Whip"; 10 } 11 12 public double cost(){ 13 return .10 + beverage.cost(); 14 } 15 }Whip
測試類:
1 public class StarbuzzCoffee{ 2 3 public static void main(String args[]){ 4 Beverage beverage = new Espresso();//只點主飲料:Espresso 5 System.out.println(beverage.getDescription() + " $" + beverage.cost()); 6 7 Beverage beverage2 = new HouseBlend();//點主飲料:HouseBlend 8 beverage2 = new Mocha(beverage2);//在主飲料中加入Mocha配料:HouseBlend + Mocha 9 beverage2 = new Soy(beverage2);//在飲料中加入Soy配料:HouseBlend + Mocha + Soy 10 beverage2 = new Whip(beverage2);//在飲料中加入Whip配料:HouseBlend + Mocha + Whip 11 System.out.println(beverage2.getDescription() + " $" + beverage2.cost());//結算 12 13 Beverage beverage3 = new Whip( new Soy( new Mocha( new HouseBlend())));//這個和beverage2是一樣的只是寫法不同,認真觀察 14 System.out.println(beverage3.getDescription() + " $" + beverage3.cost());//結算 15 } 16 }StarbuzzCoffee
編譯運行結果:
總結:
主飲料和配料都繼承自相同的Beverage抽象類,不過為了區分主飲料和配料的不同,我們在配料和Beverage抽象類之間有多了一個CondimentDecorator介面。
主飲料實現時,只要getDescription()和cost()方法返回自己的名稱和價格就好。配料中則多了一個Beverage對象,getDescription()和cost()方法不僅要返回自己的名稱和價格,還要將自己修飾的Beverage對象的名稱和價格一起算上。
在Java中的Java.io類中使用的就是裝飾者模式,有興趣可以自己搜索資料。
思想提煉:
1.對擴展開放,對修改關閉