天天看點

C#面向對象設計模式縱橫談:Bridge 橋接模式

轉: http://kb.cnblogs.com/page/79503/

抽象與實作

  抽象不應該依賴于實作細節,實作細節應該依賴于抽象。

C#面向對象設計模式縱橫談:Bridge 橋接模式

  問題在于如果抽象B由于固有的原因,本身并不穩定,也有可能變化,怎麼辦?

  舉例來說

  假如我們需要開發一個同時支援PC和手機的坦克遊戲,遊戲在PC和手機上功能都一樣,都有同樣的類型,面臨同樣的功能需求變化,比如坦克可能有很多種不同的型号:T50,T75,T90……對于其中的坦克設計,我們可能很容易設計出來一個Tank的抽象基類,然後各種不同型号的Tank繼承自該類;

C#面向對象設計模式縱橫談:Bridge 橋接模式

  另外的變化原因

  但是PC和手機上的圖形繪制、聲效、操作等實作完全不同……是以對于各種型号的坦克,都要提供各種不同平台上的坦克實作:

C#面向對象設計模式縱橫談:Bridge 橋接模式

  這樣的設計會帶來很多問題:有很多重複代碼,類的結構過于複雜,難以維護,最緻命的是引入任何新平台,比如在TV上的Tank遊戲,都會讓整個類層級結構複雜化。

  動機(Motivation)

  思考上述問題的症結:事實上由于Tank類型的固有邏輯,使得Tank類型具有了兩個變化的次元——一個變化的次元為“平台的變化”,一個變化的次元為“型号的變化”。如何應對這種“多元度的變化”?如何利用面向對象技術來使得Tank類型可以輕松地沿着“平台”和“型号”兩個方向變化,而不引入額外的複雜度?

  意圖(Intent)

  将抽象部分與實作部分分離,使它們都可以獨立地變化。  

                 ——《設計模式》GoF

  橋模式不能隻是認為是抽象和實作的分離,它其實并不僅限于此。如下面的例子,兩個都是抽象的部分。更确切的了解,應該是将一個事物中多個次元的變化分離。

  例說Bridge應用

  版本一 

C#面向對象設計模式縱橫談:Bridge 橋接模式
C#面向對象設計模式縱橫談:Bridge 橋接模式

  先寫各種不同的坦克型号類T50、T75等繼承自Tank,然後再讓各種平台的坦克繼承自對應型号的類,如PCT50,MobileT50繼承自T50等。這樣設計可能會有很多重複的代碼,例PCT50和PCT75。

  版本二

C#面向對象設計模式縱橫談:Bridge 橋接模式

  因為平台和坦克型号都是變化,是以我們把平台的變化作為屬性放到基抽象類中。

C#面向對象設計模式縱橫談:Bridge 橋接模式
C#面向對象設計模式縱橫談:Bridge 橋接模式
C#面向對象設計模式縱橫談:Bridge 橋接模式

  平台實作類

C#面向對象設計模式縱橫談:Bridge 橋接模式

  下面是整個代碼的骨架

C#面向對象設計模式縱橫談:Bridge 橋接模式

  Tank的型号,和Tank的平台都繼承自各自的抽象類,是以它們的變化都不會影響到對方。而它們之間的關聯,我們使用組合的方式,把平台類放到Tank類中作為屬性。這再次展現了組合優先于繼承的思想。多繼承的方法就是版本一的代碼,這種方式子類和父類的關系太緊,造成緊耦合。

  這就是橋模式,把變化引出了基類Tank,使得Tank僅守與型号的變化。應用程式

C#面向對象設計模式縱橫談:Bridge 橋接模式

  在環境互動中使用的都是抽象類,并且把平台實作隐藏,在應用程式中new平台的方式也可以根據情況用Singleton模式或者Abstract Factory模式等實作。

  結構(Structure)

C#面向對象設計模式縱橫談:Bridge 橋接模式

  其中imp的地方就是一個組合。Abstraction就是我們之前例子中的Tank,它的子類RefinedAbstraction就是T50等型号。Implementor是TankPlatformImplementation類,ConcreteImplementorA和ConcreteImplementorB分别是PCTankImplementation和MobileTankImplementation。整個設計模式的關鍵就是組合的使用。

  Bridge模式的幾個要點

  Bridge模式使用“對象間的組合關系”解耦了抽象和實作之間固有的綁定關系,使得抽象(Tank的型号)和實作(不同的平台)可以沿着格子的次元來變化。所謂抽象和實作沿着各自次元的變化,即“子類化”它們(比如不同的Tank型号子類,和不同的平台子類),得到各個子類之後,便可以任意組合它們,進而獲得不同平台上的不同型号。

  Bridge模式有時候類似于多繼承方案,但是多繼承方案往往違背單一職責原則(即一個類隻有一個變化的原因),複用性比較差。Bridge模式是比多繼承方案更好的解決方法。下面是針對上面的例子,多繼承接口的一種寫法: 

C#面向對象設計模式縱橫談:Bridge 橋接模式

  這樣PCT50既需要寫T50的實作,又要寫Platform的實作,它把型号和平台的變化都引入了PCT50。這樣就把兩個本不該扭在一起的事務扭在了一起,這樣的設計更加糟糕,而且也違背了類的單一職責原則。Bridge模式的應用一般在“兩個非常強的變化次元”,有時候即使有兩個變化的次元,但是某個方向的變化次元并不劇烈——換言之兩個變化不會導緻縱橫交錯的結果,并不一定要使用Bridge模式。

  橋模式并不同于擴充卡模式,擴充卡模式其實是一個事後諸葛亮,當發現以前的東西不适用了才去做一個彌補的措施。橋模式相對來說所做的改變比擴充卡模式早,它可以适用于有兩個甚至兩個以上次元的變化。

繼續閱讀