1. 程式人生 > >《Head First 設計模式》之裝飾者模式

《Head First 設計模式》之裝飾者模式

的人 開放 override ext es2017 必須 想要 ret ted

前言:

  時間過得挺快的,轉眼間就到了十月中旬了,再晃著晃著新的一年就要來臨。今天lz有幸來到了浙大圖書館來學習,想想自己已經畢業兩年了,今日再次踏進校園裏,心裏頗有一絲感悟,說不出來,只有把它放在心裏。lz前段時間看到一篇文章,覺得裏面有句話說的很好,想拿出來與大家分享。文章的標題是《在寫作中成長》,有句話是說,我們可以通過寫作來學習,學習方法是每個輸入都盡量有所輸出。 無論是讀書,看文章,參加技術講座,還是看電影,盡量或多或少的總結輸出出來,輸出形式要麽是一篇博客,要麽是一篇微博,要麽是一篇筆記。因為自己完全掌握的知識應該是自己能表達出來的知識。

今日分享:

  老樣子,今天給大家帶來兩句話。

  1.很喜歡這句話:你現在的氣質裏,藏著你走過的路,讀過的書和愛過的人。

  2.一句英文,The only you in the world, ever if no one appreciate, should love and take care of yourself.世界上唯一的你,就算沒有人欣賞,也要好好愛自己。真正的美就是做自己,你不需要被大部分人認可,你只需要接受最真實的自我!

裝飾者模式

  今天給大家帶來的是裝飾者模式。一說到復用我想大家腦子裏的第一反應應該就是繼承,通過對這個模式的學習,大家可以領悟到運行時期的擴展遠比編譯時期的繼承威力大。也即,我們可以采取對象組合的方式,做到在運行時裝飾類。貌似說的有點抽象哈,OK,下面繼續和大家探討裝飾者模式。

  咖啡實例:

  現給出一個具體的實例讓大家初步感受下裝飾者模式。某家咖啡連鎖店裏面有多種飲料供應,每種飲料也可以要求在其中加入各種調料,這家連鎖店會根據所加入的調料收取不同的費用。先畫出該案例的UML類圖:

  UML:

  技術分享

下面給出具體代碼示例:

先給出被裝飾者角色的抽象類:

 1 package xin.yangmj.decorate.decorated;
 2 
 3 /**
 4  * 這是飲料抽象類,角色相當於被裝飾者
 5  *
 6  * @author Eric Yang
 7  * @create 2017-10-14 下午2:24
 8  **/
 9 public abstract
class Beverage { 10 11 public String description = "Unknown Beverage"; 12 13 public String getDescription() { 14 return description; 15 } 16 17 public abstract double cost(); 18 }

再給出四個具體的飲料實現類:

 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 一種具體的飲料
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:30
10  **/
11 public class DarkRoast extends Beverage {
12 
13     public DarkRoast() {
14         description = "Dark Roast Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 2.99;
20     }
21 }
 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 一種具體的飲料
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:30
10  **/
11 public class Decaf extends Beverage {
12 
13     public Decaf() {
14         description = "Decaf Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 2.09;
20     }
21 }
 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 這是濃縮咖啡,一種具體的飲料
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:30
10  **/
11 public class Espresso extends Beverage {
12 
13     public Espresso() {
14         description = "Espresso Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 1.99;
20     }
21 }
 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 另一種飲料的實現
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:34
10  **/
11 public class HouseBlend extends Beverage {
12 
13     public HouseBlend() {
14         description = "House Blend Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 0.89;
20     }
21 }

再給出抽象調料裝飾者,為裝飾者角色:

 1 package xin.yangmj.decorate.decorator;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 這是調料抽象類,角色相當於裝飾者
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:28
10  **/
11 public abstract class CondimentDecorator extends Beverage {
12 
13     public abstract String getDescription();
14 }

再給出四個具體的調料裝飾者實現類:

 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 這是調料的一種實現
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:54
11  **/
12 public class Milk extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Milk(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是該調料裝飾者飲料,這也解釋了
22      * 為什麽調料裝飾者要有個抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Milk";
29     }
30 
31     /**
32      * 這是總價錢,包含包裝的飲料和此調料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.30 + beverage.cost();
39     }
40 }
 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 這是調料的一種實現
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:35
11  **/
12 public class Mocha extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Mocha(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是該調料裝飾者飲料,這也解釋了
22      * 為什麽調料裝飾者要有個抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Mocha";
29     }
30 
31     /**
32      * 這是總價錢,包含包裝的飲料和此調料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.20 + beverage.cost();
39     }
40 }
 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 這是調料的一種實現
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:54
11  **/
12 public class Soy extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Soy(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是該調料裝飾者飲料,這也解釋了
22      * 為什麽調料裝飾者要有個抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Soy";
29     }
30 
31     /**
32      * 這是總價錢,包含包裝的飲料和此調料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.15 + beverage.cost();
39     }
40 }
 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 這是調料的一種實現
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:54
11  **/
12 public class Whip extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Whip(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是該調料裝飾者飲料,這也解釋了
22      * 為什麽調料裝飾者要有個抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Whip";
29     }
30 
31     /**
32      * 這是總價錢,包含包裝的飲料和此調料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.18 + beverage.cost();
39     }
40 }

最後給出客戶端測試代碼:

 1 package xin.yangmj.decorate;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorated.impl.DarkRoast;
 5 import xin.yangmj.decorate.decorated.impl.Espresso;
 6 import xin.yangmj.decorate.decorated.impl.HouseBlend;
 7 import xin.yangmj.decorate.decorator.impl.Mocha;
 8 import xin.yangmj.decorate.decorator.impl.Soy;
 9 import xin.yangmj.decorate.decorator.impl.Whip;
10 
11 /**
12  * 這是客戶端,用來測試供應咖啡
13  *
14  * @author Eric Yang
15  * @create 2017-10-14 下午2:50
16  **/
17 public class StarbuzzCoffee {
18     public static void main(String[] args){
19         Beverage beverage = new Espresso();
20         // 沒有加任何調料
21         System.out.println(beverage.getDescription() + "--- $" + beverage.cost());
22 
23         // 用兩個Mocha裝飾它,然後再用Whip裝飾
24         Beverage beverage2 = new DarkRoast();
25         beverage2 = new Mocha(beverage2);
26         beverage2 = new Mocha(beverage2);
27         beverage2 = new Whip(beverage2);
28         System.out.println(beverage2.getDescription() + "--- $" + beverage2.cost());
29 
30         // 分別用Soy, Mocha, Whip來裝飾
31         Beverage beverage3 = new HouseBlend();
32         beverage3 = new Soy(beverage3);
33         beverage3 = new Mocha(beverage3);
34         beverage3 = new Whip(beverage3);
35         System.out.println(beverage3.getDescription() + "--- $" + beverage3.cost());
36     }
37 }

運行結果如下:

技術分享

  認識裝飾者模式

  通過上面的例子,我們可以以飲料為主體,然後在運行時以調料來“裝飾”(decorate)飲料。比如,顧客想要摩卡和奶泡深焙咖啡,那麽,要做的是:

  1.拿一個深焙咖啡(DarkRoast)對象

  2.以摩卡(Mocha)對象裝飾它

  3.以奶泡(Whip)對象裝飾它

  4調用cost()方法,並依賴委托(delegate)將調料的價錢加上去

  定義裝飾者模式

  官方定義:裝飾者模式動態地將責任附加到對象上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。具體UML類圖如下:

  UML類圖

技術分享

通過上面的定義和UML類圖,可以得出關於裝飾者模式的以下幾點結論:

  1.裝飾者和被裝飾者對象有相同的超類型。

  2.你可以用一個或多個裝飾者包裝一個對象。

  3.既然裝飾者和被裝飾者對象有相同的超類型,所以可以在任何需要原始對象(被包裝的)的場合,都可以用裝飾過的對象來替代它。

  4.裝飾者可以在所委托被裝飾者的行為之前/或之後,加上自己的行為,以達到特定的目的。

  5.對象可以在任何時候被裝飾,所以可以在運行時動態地,不限量地用你喜歡的裝飾者來裝飾對象。

  滿足的設計原則

  開放-關閉原則:類應該對擴展開發,對修改關閉。可以理解為,允許類容易擴展,在不修改現有代碼的情況下,就可搭配新的行為。這樣子的設計具有彈性可以應對改變,可以接受新的功能來應對改變的需求。具體在本例中,我們可以任意增加飲料和調料的種類,方便此咖啡店對它們的產品進行擴展,並且無需對現有的代碼進行改變。只要新增加的種類滿足現有的規範即可,也即:實現裝飾者和被裝飾者抽象類即可!

  答惑:在此特別和大家說明一點,裝飾者模式最重要的一點是:裝飾者需要和被裝飾者(被包裝的組件)有相同的“接口”,因為,裝飾者必須能取代被裝飾者。也即:這兩者必須是一樣的類型,具有共同的超類。在這裏,我們利用繼承達到“類型匹配”,而不是利用繼承獲得“行為”。行為來自裝飾者和基礎組件,或與其他裝飾者之間的組合關系。

  真實世界的裝飾者:Java I/O

  此處有待後續補充...

  

  

  

  

《Head First 設計模式》之裝飾者模式