今天我們來講一下橋接模式。 一、案例 我有N牌子的一個手機,需要運行一款游戲軟體。咱們用簡單的控制台應用程式來實現一下。 客戶端調用: 二、演繹 1、第一步演繹: 如果我不僅有N品牌的手機,還有M品牌的手機也需要運行這款游戲軟體,怎麼辦? 我們可以將運行游戲軟體抽象出一個父類,讓N,M品牌的手機繼承 ...
今天我們來講一下橋接模式。
一、案例
我有N牌子的一個手機,需要運行一款游戲軟體。咱們用簡單的控制台應用程式來實現一下。
1 /// <summary> 2 /// N品牌的手機中的游戲 3 /// </summary> 4 class HandsetNGame 5 { 6 public void Run() 7 { 8 Console.WriteLine("運行N品牌手機游戲"); 9 } 10 }
客戶端調用:
1 public static void Main() 2 { 3 HandsetNGame n = new HandsetNGame(); 4 n.Run(); 5 Console.ReadKey(); 6 }
二、演繹
1、第一步演繹:
如果我不僅有N品牌的手機,還有M品牌的手機也需要運行這款游戲軟體,怎麼辦?
我們可以將運行游戲軟體抽象出一個父類,讓N,M品牌的手機繼承這個父類。代碼如下:
1 /// <summary> 2 /// 父類 3 /// </summary> 4 class HandsetGame 5 { 6 public virtual void Run() 7 { 8 9 } 10 } 11 /// <summary> 12 /// 子類 13 /// </summary> 14 class HandsetNGame : HandsetGame 15 { 16 public override void Run() 17 { 18 Console.WriteLine("運行N品牌手機游戲"); 19 } 20 } 21 /// <summary> 22 /// 子類 23 /// </summary> 24 class HandsetMGame : HandsetGame 25 { 26 public override void Run() 27 { 28 Console.WriteLine("運行M品牌手機游戲"); 29 } 30 }
客戶端調用:
1 public static void Main() 2 { 3 HandsetGame gameM = new HandsetMGame(); 4 HandsetGame gameN = new HandsetNGame(); 5 gameM.Run(); 6 gameN.Run(); 7 Console.ReadKey(); 8 }
2、第二步演繹
還有一個問題是,手機不僅能玩游戲,還要有通訊錄的功能,也就是說N,M品牌的手機都有通訊錄的功能,那麼我們該怎麼做呢?
小伙伴們說,這容易啊,跟第一步一樣,抽象出一個通訊錄的父類,讓N,M品牌手機通訊錄繼承這個父類即可。代碼如下:
1 /// <summary> 2 /// 父類 3 /// </summary> 4 class HandsetBrand 5 { 6 public virtual void Run() 7 { 8 9 } 10 } 11 /// <summary> 12 /// 子類 13 /// </summary> 14 class HandNsetBrand:HandsetBrand 15 { 16 public override void Run() 17 { 18 Console.WriteLine("運行N品牌手機通訊錄"); 19 } 20 } 21 /// <summary> 22 /// 子類 23 /// </summary> 24 class HandMsetBrand:HandsetBrand 25 { 26 public override void Run() 27 { 28 Console.WriteLine("運行M品牌手機通訊錄"); 29 } 30 }
客戶端調用是類似的,在此不寫代碼了。
好,那麼問題來了,我們手機不僅有游戲,通訊錄功能,還有很多很多的功能,例如,照相,彩信等等等等,照這樣下去,我們這種設計將會讓類異常的多,項目異常的龐大,出現這種情況,我們不禁懷疑我們的這種設計是不是出現了問題,如果出現了問題,我們改用什麼方式解決呢?
對象的繼承關係是在編譯時就定義好了,所以無法在運行時改變從父類繼承的實現,子類的實現與它的父類有非常緊密的依賴關係,以至於父類實現中的任何變化必然會導致子類發生變化。當你需要復用子類時,如果繼承下來的實現不適合解決新的問題,則父類必須重寫或者被其他更適合的類替換,這種依賴關係限制了靈活性並最終限制了復用性。
在這裡首先我們來講一個設計原則,合成/聚合復用原則。
合成/聚合復用原則,儘量使用合成/聚合儘量不要使用類繼承,聚合表示一種弱的‘擁有’關係,體現的是A對象可以包含B對象,但B對象不是A對象的一部分;合成表示一種強的‘擁有’關係,體現了嚴格的部分和整體的關係,部分和整體的生命周期一樣。打個比方,大雁有兩隻翅膀,翅膀和大雁就是部分和整體的關係,他們的生命周期是相同的,是合成。大雁和雁群,是聚合關係。
好,本著合成/聚合復用原則,我們重新來設計一下我們案例中的代碼
1 //手機軟體 2 abstract class HandsetSoft 3 { 4 public abstract void Run(); 5 } 6 //手機游戲 7 class HandsetGame:HandsetSoft 8 { 9 public override void Run() 10 { 11 Console.WriteLine("運行手機游戲"); 12 } 13 } 14 //手機通訊錄 15 class HandsetAddressList:HandsetSoft 16 { 17 public override void Run() 18 { 19 Console.WriteLine("運行手機通訊錄"); 20 } 21 } 22 //手機品牌 23 abstract class HandsetBrand 24 { 25 protected HandsetSoft soft; 26 //品牌需要關註軟體,所以可在機器中安裝軟體,以備運行 27 public void SetHandsetSoft(HandsetSoft soft) 28 { 29 this.soft = soft; 30 } 31 //運行 32 public abstract void Run(); 33 } 34 //具體的手機品牌N 35 class HandsetBrandN:HandsetBrand 36 { 37 public override void Run() 38 { 39 soft.Run(); 40 } 41 } 42 //具體的手機品牌M 43 class HandsetBrandM:HandsetBrand 44 { 45 public override void Run() 46 { 47 soft.Run(); 48 } 49 }
客戶端調用:
1 public static void Main() 2 { 3 HandsetBrand ab; 4 ab = new HandsetBrandN(); 5 ab.SetHandsetSoft(new HandsetGame()); 6 ab.Run(); 7 8 ab.SetHandsetSoft(new HandsetAddressList()); 9 ab.Run(); 10 11 ab = new HandsetBrandM(); 12 ab.SetHandsetSoft(new HandsetGame()); 13 ab.Run(); 14 15 ab.SetHandsetSoft(new HandsetAddressList()); 16 ab.Run(); 17 Console.ReadKey(); 18 }
以上就是我們要將的一種設計模式:橋接模式。
橋接模式,將抽象部分與他的實現部分分離,使他們都可以獨立的變化。
這裡解釋一下:什麼叫抽象與他的實現分離?這並不是說,讓抽象類與其派生類分離,因為這沒有任何的意義,實現指的是抽象類和他的派生類用來實現自己的對象。
真正的理解的設計原則,很多的設計模式其實就是設計原則的使用,在不知不覺中,我們就使用了很多的設計模式了。
今天的橋接模式我們先講到這裡,下一篇我們講 命令模式
本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持