1. 程式人生 > >Head First設計模式——複合模式

Head First設計模式——複合模式

  複合模式是HeadFirst上面詳細講的最後一個模式,其前面的模式作者認為都是成熟的經常使用的模式。所以這是詳細講解模式的最後一篇,同時這個模式講解的篇幅也是最長的,接下來我就對其進行總結提煉進行講解。複合模式顧名思義就是使用其他模式聯合使用解決問題,但是將某些模式結合使用並不代表這些模式就能稱為複合模式。複合模式必須夠一般性,適合解決許多問題。相信我們大家都知道的MVC就是複合模式的應用,那麼我們就來看模式如何結合使用和MVC中使用到的模式。

模式結合

  記得第一個模式,策略模式就是以鴨子開頭,而最後一個模式用鴨子結尾,做到首尾呼應吧。我們依然看鴨子的例子來講解我們的複合模式。

  (1)首先建立一個Quackable,然後讓某些鴨子實現介面。

 1     public interface Quackable
 2     {
 3         public void Quack();
 4     }
 5 
 6     public class RedheadDuck : Quackable
 7     {
 8         public void Quack()
 9         {
10             Console.WriteLine("呱呱呱");
11         }
12     }

  (2)我們寫了一個紅頭鴨的類,然後我們再加點其他種類的鴨子,例如橡皮鴨。

1     public class RubberDuck : Quackable
2     {
3         public void Quack()
4         {
5             //橡皮鴨的叫聲
6             Console.WriteLine("吱吱吱");
7         }
8     }

  (3)測試一下寫的例子,測試程式碼

 

   (4)當我們有鴨子的時候,我們也許有一隻鵝。

1     public class Goose
2     {
3         public void Honk() {
4             Console.WriteLine("咯咯咯");
5         }
6     }

  (5)如果我們要把鵝也加入到模擬器中,那麼為了統一處理我們可以使用介面卡將鵝適配成鴨子

 1     public class GooseAdapter : Quackable
 2     {
 3         private Goose goose;
 4         public GooseAdapter(Goose goose) {
 5             this.goose = goose;
 6         }
 7         public void Quack()
 8         {
 9             goose.Honk();
10         }
11     }

  (6)當我們適配成鴨子後在模擬器中加入鵝

   (7)如果我們要知道叫聲的次數我們需要一個裝飾者,通過把鴨子包裝進裝飾者物件,給鴨子一些新行為(計算次數的行為)。我們不用修改鴨子的程式碼。

 1     public class QuackCounter : Quackable
 2     {
 3         Quackable duck;
 4         private static int numberOfQuacks;
 5         public QuackCounter(Quackable duck)
 6         {
 7             this.duck = duck;
 8         }
 9 
10         public void Quack()
11         {
12             duck.Quack();
13             numberOfQuacks++;
14         }
15 
16         public static int GetQuacks()
17         {
18             return numberOfQuacks;
19         }
20     }

  (8)包裝例項化Quackable,統計叫聲次數。

   (9)對於裝飾和沒被裝飾的鴨子我們想分別管理,讓建立的和裝飾部分包裝起來。我們需要建立一個工廠,而且是不同型別鴨子的產品家族,所以我們要用抽象工廠模式。

 1     /// <summary>
 2     /// 定義抽象工廠,由子類建立不同的家族
 3     /// </summary>
 4     public abstract class AbstractDuckFactory
 5     {
 6         public abstract Quackable CreateReadheadDuck();
 7         public abstract Quackable CreateRubberDuck();
 8     }
 9 
10 
11     /// <summary>
12     /// 沒有裝飾者的工廠
13     /// </summary>
14     public class DuckFactory : AbstractDuckFactory
15     {
16         public override Quackable CreateReadheadDuck()
17         {
18             return new RedheadDuck();
19         }
20 
21         public override Quackable CreateRubberDuck()
22         {
23             return new RubberDuck();
24         }
25     }
26 
27 
28     /// <summary>
29     /// 有裝飾者的工廠
30     /// </summary>
31     public class CountingDuckFactory : AbstractDuckFactory
32     {
33         public override Quackable CreateReadheadDuck()
34         {
35             return new QuackCounter(new RedheadDuck());
36         }
37 
38         public override Quackable CreateRubberDuck()
39         {
40             return new QuackCounter(new RubberDuck());
41         }
42     }

  (10)使用工廠模式

   (11)有了工廠模式統一建立鴨子,我們還可以統一管理鴨子,而組合模式允許我們像對待單個物件一樣對待物件集合。組合需要和葉節點元素一樣實現相同的介面,這裡的葉節點就是Quackable。

 1     public class Flock : Quackable
 2     {
 3         private List<Quackable> quackables = new List<Quackable>();
 4 
 5         public void Add(Quackable quackable) {
 6             quackables.Add(quackable);
 7         }
 8         public void Quack()
 9         {
10             var enumerator= quackables.GetEnumerator();
11             while (enumerator.MoveNext()) {
12                 Quackable quackable = enumerator.Current;
13                 quackable.Quack();
14                     
15             }
16         }
17     }

  (12)在這個組合模式中我們遍歷鴨子的叫聲的時候用到了foreach迴圈的本質方法,這裡實際是另外一個迭代器模式。

  然後我們改造一下測試模擬器,看看輸出結果。

  (13)最後我們還整合一個需求,當有人想要觀察鴨子的行為,我們可以給鴨子加上一個觀察者模式。

  觀察者需要一個Observable介面,所謂的Observable就是被觀察的物件。Observable需要註冊和通知觀察者的方法。

1     public interface QuackObservable
2     {
3         public void RegisterObserver(Observer observer);
4         public void NotifyObservers();
5     }

  鴨子要實現介面QuackObservable,由於鴨子都實現了Quackable介面,所以我們可以讓Quackable實現QuackObservable介面。

1     public interface Quackable:QuackObservable
2     {
3         public void Quack();
4     }

  (14)我們需要在鴨子的每個類中實現註冊和通知,但是這裡我們可以新建一個類Observable用來封裝註冊和通知的程式碼,然後將他和QuackObservable組合在一起,這樣我們只需要一份註冊和通知的程式碼,QuackObservable所有的呼叫都委託給Observable這個輔助類。

 1     public class Observable : QuackObservable
 2     {
 3         //觀察者
 4         List<Observer> observers = new List<Observer>();
 5         QuackObservable duck;
 6 
 7         public Observable(QuackObservable duck) {
 8             this.duck = duck;
 9         }
10 
11         public void RegisterObserver(Observer observer)
12         {
13             observers.Add(observer);
14         }
15 
16         public void NotifyObservers()
17         {
18             foreach (var observer in observers)
19             {
20                 observer.Update(duck);
21             }
22         }
23     }

  (15)然後我們以紅頭鴨為例改造被觀察者,整合Quackable類和Observable。

 1     public class RedheadDuck : Quackable
 2     {
 3         Observable observable;
 4         public RedheadDuck() {
 5             observable = new Observable(this);
 6         }
 7         public void RegisterObserver(Observer observer)
 8         {
 9             observable.RegisterObserver(observer);
10         }
11 
12         public void NotifyObservers()
13         {
14             observable.NotifyObservers();
15         }
16 
17         public void Quack()
18         {
19             Console.WriteLine("呱呱呱");
20             NotifyObservers();
21         }
22     }

  (16)如果是一群鴨子觀察,則我們修改Flock類,註冊時候註冊到每個要觀察的葉節點上,然後當通知的時候各自葉節點會呼叫自己的NotifyObservers,所有Flock的NotifyObservers就不用做任何事情。

 1     public class Flock : Quackable
 2     {
 3         private List<Quackable> quackables = new List<Quackable>();
 4 
 5         public void Add(Quackable quackable)
 6         {
 7             quackables.Add(quackable);
 8         }
 9 
10         public void Quack()
11         {
12             var enumerator = quackables.GetEnumerator();
13             while (enumerator.MoveNext())
14             {
15                 Quackable quackable = enumerator.Current;
16                 quackable.Quack();
17             }
18         }
19 
20         public void RegisterObserver(Observer observer)
21         {
22             foreach (var duck in quackables)
23             {
24                 duck.RegisterObserver(observer); 
25             }
26         }
27 
28         public void NotifyObservers(){}
29     }

  (17)現在完成Observer端。

 1     public interface Observer
 2     {
 3         void Update(QuackObservable duck);
 4     }
 5 
 6     public class QuackObserver : Observer
 7     {
 8         public void Update(QuackObservable duck)
 9         {
10             Console.WriteLine("觀察者:"+duck+" 正在叫");
11         }
12     }

  (18)加入觀察者測試。

 

 

   通過這個例子我們組合了6個設計模式,你可能要問這就是複合模式?不,這只是一群模式攜手合作。所謂的複合模式,是指一群模式被結合起來使用,以解決一般性問題。而這個例子只是為了演示如何將模式結合起來,但它不是為了解決一般性問題。

  結合上面的程式碼我們畫一個類圖便於理解例子中模式如何協作結合的。

 

 

 複合模式:結合兩個或以上的模式,組成一個解決方案,解決以再發生的一般性問題。

 MVC複合模式

M:model模型,模型持有所有的資料、狀態和程式邏輯。

V:visual檢視,用來呈現狀態和資料,是和使用者互動的介面。

C:control控制,取得使用者的輸入並告知模型如何作出對應的動作。

MVC包含的設計模式

策略模式:檢視通過控制器對模型進行修改,檢視是一個物件,可以被調整為使用不同的控制器(不同策略)。

組合模式:介面中的每個顯示元件不是組合節點就是葉節點。當控制器想要做某種更新時,只需告訴檢視最頂層的元件即可,組合模式會處理組合節點或葉節點的更新。

觀察模式:當模型發生改變時,需要立即反饋到檢視中,此時可以把控制器或檢視作為觀察者,觀測模型的動態變化。這樣模型完全獨立於檢視和控制器,是一個鬆耦合的實現。
雖然MVC中的設計模式也許不再試經典意義上的模型,但現實中設計模式都不一定照搬經典設計,會有優化或改動,所以並不影響它就是設計模式的使用。

這就是複合模式的概念和例子,HeadFirst中的所有細講模式都已經結束,下一次我會羅列其他沒有詳細講解的設計模概念,也許在其他地方使用到了我會回來補全例子和代