JAVA設計模式之裝飾者模式


咖啡店需要做一個訂單系統,以合乎飲料供應要求。

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中有很多用到裝飾者模式的設計,有興趣的朋友可以了解下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM