咖啡店需要做一個訂單系統,以合乎飲料供應要求。 1.最初是這樣設計的: 每一種飲料都需要繼承該抽象類,並覆寫cost()方法。 2.但是購買咖啡時需要考慮到調料的部分,每種咖啡會加不同種的調料,比如蒸奶、豆漿、摩卡或者覆蓋奶泡,那麼訂單系統需要考慮加入不同調料後的價格。因此需要實現不同的子類來定義添 ...
咖啡店需要做一個訂單系統,以合乎飲料供應要求。
1.最初是這樣設計的:
1 /** 2 * 飲料抽象類 3 * 4 */ 5 public abstract class Beverage { 6 7 protected String description; 8 9 public String getDescription() { 10 return this.description; 11 } 12 13 /** 14 * 子類需自定義自己的價格 15 * @return 16 */ 17 public abstract double cost(); 18 19 }
每一種飲料都需要繼承該抽象類,並覆寫cost()方法。
2.但是購買咖啡時需要考慮到調料的部分,每種咖啡會加不同種的調料,比如蒸奶、豆漿、摩卡或者覆蓋奶泡,那麼訂單系統需要考慮加入不同調料後的價格。因此需要實現不同的子類來定義添加不同調料後的價格。大家知道,一種咖啡跟多種調料有多種組合方式,那麼多種咖啡和多種調料的組合後,幾乎是類爆炸!
維護極其困難:
如果某種飲料價格調整;或是新增了某種飲料,怎麼辦?
後來經過改進後,把是否存在某種調料作為飲料的屬性,併在飲料抽象類中實現cost方法,子類可以覆寫cost方法並設置添加的調料最終確定添加不同調料後的價格:
1 /** 2 * 飲料抽象類 3 * 4 */ 5 public abstract class Beverage { 6 7 private boolean milk;//牛奶 8 private boolean soy;//豆漿 9 private boolean mocha;//摩卡 10 private boolean whip;//奶泡 11 12 private double milkCost = 0.19; 13 private double soyCost = 0.26; 14 private double mochaCost = 0.29; 15 private double whipCost = 0.17; 16 17 protected String description; 18 19 //setter getter method 20 21 public String getDescription() { 22 return this.description; 23 } 24 25 public double cost() { 26 double condimentCost = 0.0; 27 if (hasMilk()) { 28 condimentCost += milkCost; 29 } 30 if (hasSoy()) { 31 condimentCost += soyCost; 32 } 33 if (hasMocha()) { 34 condimentCost += mochaCost; 35 } 36 if (hasWhip()) { 37 condimentCost += whipCost; 38 } 39 return condimentCost; 40 } 41 42 } 43 44 /** 45 * 低糖咖啡 46 * 47 */ 48 public class Decaf extends Beverage { 49 50 @Override 51 public String getDescription() { 52 return "It is Decaf."; 53 } 54 55 @Override 56 public double cost() { 57 super.setMilk(true);//添加牛奶調料 58 return 1.99 + super.cost(); 59 } 60 61 }
這樣一來,如果有五種咖啡,那麼只需要實現五個子類即可,不同的子類可以靈活設置添加不同的調料。
但是這樣的設計存在一定的問題:
1)調料價格的改變會使我們改變現有代碼;
2)出現新調料,就需要加上新的方法,並改變父類中的cost方法;
3)若出現新的飲料,如紅茶,那麼新的飲料繼承該父類,父類中的調料屬性並不合適,如奶泡等;
... ...
設計原則:類應該對擴展開放,對修改關閉。
裝飾者模式思想:以飲料為主體,然後在運行時以調料來“裝飾”飲料。
例如客戶需要摩卡和奶泡深焙咖啡,那麼要做的是:
拿一個深焙咖啡對象;
以摩卡對象裝飾;
以奶泡對象裝飾;
摩卡和奶泡屬於調料,但是也是裝飾者,它的類型反映了它裝飾的對象,所謂反映,指的是兩者類型一致。那麼所有調料需要繼承Beverage。
使用裝飾者模式設計的代碼:
1 /** 2 * 飲料抽象類 3 * 4 */ 5 public abstract class Beverage { 6 7 protected String description; 8 9 public String getDescription() { 10 return this.description; 11 } 12 13 /** 14 * 獲取每種飲料的價格 15 * @return 16 */ 17 public abstract double cost(); 18 } 19 20 /** 21 * 調料抽象類 22 * 23 */ 24 public abstract class Condiment extends Beverage { 25 26 public abstract String getDescription(); 27 28 }
這裡調料繼承飲料,僅僅是為了使兩者具有相同的類型,並非為了復用父類的行為。
下麵是飲料的子類:
1 /** 2 * 深焙咖啡 3 * 4 */ 5 public class DarkRoast extends Beverage { 6 7 public DarkRoast() { 8 description = "DarkRoast"; 9 } 10 @Override 11 public double cost() { 12 return 0.19; 13 } 14 15 } 16 17 /** 18 * 濃縮咖啡 19 * 20 */ 21 public class Espresso extends Beverage { 22 23 public Espresso() { 24 description = "Espresso"; 25 } 26 27 @Override 28 public double cost() { 29 return 1.99; 30 } 31 32 } 33 34 /** 35 * 黑咖啡 36 * 37 */ 38 public class HoseBlend extends Beverage { 39 40 public HoseBlend() { 41 description = "Hose Blend Coffee"; 42 } 43 44 @Override 45 public double cost() { 46 return 0.99; 47 } 48 49 }
調料(裝飾者)子類:
1 /** 2 * 摩卡 3 * 4 */ 5 public class Mocha extends Condiment { 6 7 private Beverage beverage; 8 9 public Mocha(Beverage beverage) { 10 this.beverage = beverage; 11 } 12 13 @Override 14 public String getDescription() { 15 return beverage.getDescription() + ", Mocha"; 16 } 17 18 @Override 19 public double cost() { 20 return 0.20 + beverage.cost(); 21 } 22 23 } 24 25 /** 26 * 豆漿 27 * 28 */ 29 public class Soy extends Condiment { 30 31 private Beverage beverage; 32 33 public Soy(Beverage beverage) { 34 this.beverage = beverage; 35 } 36 37 @Override 38 public String getDescription() { 39 return beverage.getDescription() + ", Soy"; 40 } 41 42 @Override 43 public double cost() { 44 return 0.23 + beverage.cost(); 45 } 46 47 } 48 49 /** 50 * 奶泡 51 * 52 */ 53 public class Whip extends Condiment { 54 55 private Beverage beverage; 56 57 public Whip(Beverage beverage) { 58 this.beverage = beverage; 59 } 60 61 @Override 62 public String getDescription() { 63 return beverage.getDescription() + ", Whip"; 64 } 65 66 @Override 67 public double cost() { 68 return 0.69 + beverage.cost(); 69 } 70 71 }
測試代碼:
1 public class ComponentTest { 2 @Test 3 public void test() { 4 Beverage beverage = new Espresso(); 5 System.out.println(beverage.getDescription() + ", $" + beverage.cost()); 6 Beverage beverage2 = new HoseBlend(); 7 beverage2 = new Mocha(beverage2); 8 beverage2 = new Mocha(beverage2); 9 beverage2 = new Whip(beverage2); 10 System.out.println(beverage2.getDescription() + ", $" + beverage2.cost()); 11 Beverage beverage3 = new DarkRoast(); 12 beverage3 = new Soy(beverage3); 13 beverage3 = new Mocha(beverage3); 14 beverage3 = new Whip(beverage3); 15 System.out.println(beverage3.getDescription() + ", $" + beverage3.cost()); 16 } 17 }
運行結果:
1 Espresso, $1.99 2 Hose Blend Coffee, Mocha, Mocha, Whip, $2.08 3 DarkRoast, Soy, Mocha, Whip, $1.31
java/IO中有很多用到裝飾者模式的設計,有興趣的朋友可以瞭解下。