模式動機
設想如果要繪制矩形、圓形、橢圓、正方形,我們至少需要4個形狀類,但是如果繪制的圖形需要具有不同的顏色,如紅色、綠色、藍色等,此時至少有如下兩種設計方案:
第一種設計方案是為每一種形狀都提供一套各種顏色的版本。
第二種設計方案是根據實際需要對形狀和顏色進行組合。
對於有兩個變化維度(即兩個變化的原因)的系統,采用方案二來進行設計系統中類的個數更少,且系統擴展更為方便。設計方案二即是橋接模式的應用。橋接模式將繼承關系轉換為關聯關系,從而降低了類與類之間的耦合,減少了代碼編寫量。
模式定義
橋接模式(Bridge Pattern):將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種對象結構型模式,又稱為柄體(Handle and Body)模式或接口(Interface)模式。
Bridge Pattern: Decouple an abstraction from its implementation so that the two can vary independently.
Frequency of use: medium
UML圖
模式結構
橋接模式包含如下角色:
Abstraction:抽象類
RefinedAbstraction:擴充抽象類
Implementor:實現類接口
ConcreteImplementor:具體實現類
模式分析
理解橋接模式,重點需要理解如何將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化。
抽象化:抽象化就是忽略一些信息,把不同的實體當作同樣的實體對待。在面向對象中,將對象的共同性質抽取出來形成類的過程即為抽象化的過程。
實現化:針對抽象化給出的具體實現,就是實現化,抽象化與實現化是一對互逆的概念,實現化產生的對象比抽象化更具體,是對抽象化事物的進一步具體化的產物。
脫耦:脫耦就是將抽象化和實現化之間的耦合解脫開,或者說是將它們之間的強關聯改換成弱關聯,將兩個角色之間的繼承關系改為關聯關系。橋接模式中的所謂脫耦,就是指在一個軟件系統的抽象化和實現化之間使用關聯關系(組合或者聚合關系)而不是繼承關系,從而使兩者可以相對獨立地變化,這就是橋接模式的用意。
模式實例與解析
手機軟件何時統一—橋接模式
體系結構
Implementor:實現類接口HandsetSoft.cs
namespace BridgePattern { abstract class HandsetSoft { public abstract void Run(); } }
ConcreteImplementor:具體實現類
游戲、通訊錄等具體類
HandsetGame.cs
using System; namespace BridgePattern { class HandsetGame : HandsetSoft { public override void Run() { Console.WriteLine("運行手機游戲"); } } }
HandsetAddressList.cs
using System; namespace BridgePattern { class HandsetAddressList : HandsetSoft { public override void Run() { Console.WriteLine("運行手機通訊錄"); } } }
Abstraction:抽象類 HandsetBrand.cs
namespace BridgePattern { //手機品牌 abstract class HandsetBrand { protected HandsetSoft soft; //設置手機軟件 public void SetHandsetSoft(HandsetSoft soft) { this.soft = soft; } //運行 public abstract void Run(); } }
RefinedAbstraction:擴充抽象類
HandsetBrandN.cs
namespace BridgePattern { //手機品牌N class HandsetBrandN : HandsetBrand { public override void Run() { soft.Run(); } } }
HandsetBrandM.cs
namespace BridgePattern { class HandsetBrandM : HandsetBrand { public override void Run() { soft.Run(); } } }
Client:客戶類
using System; namespace BridgePattern { class Program { static void Main(string[] args) { HandsetBrand ab; ab = new HandsetBrandN(); ab.SetHandsetSoft(new HandsetGame()); ab.Run(); ab.SetHandsetSoft(new HandsetAddressList()); ab.Run(); ab = new HandsetBrandM(); ab.SetHandsetSoft(new HandsetGame()); ab.Run(); ab.SetHandsetSoft(new HandsetAddressList()); ab.Run(); Console.Read(); } } }
現在如果要增加一個功能,比如MP3音樂播放功能,那么只要增加HandsetMP3這個類就行了。不會影響其他任何類。類的個數增加也只是一個;
如果是要增加S品牌,只需要增加一個品牌子類HandsetBrandS就可以了。個數也是一個,不會影響其他類的改動。
模式優缺點
橋接模式的優點
分離抽象接口及其實現部分。
橋接模式有時類似於多繼承方案,但是多繼承方案違背了類的單一職責原則(即一個類只有一個變化的原因),復用性比較差,而且多繼承結構中類的個數非常龐大,橋接模式是比多繼承方案更好的解決方法。
橋接模式提高了系統的可擴充性,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統。
實現細節對客戶透明,可以對用戶隱藏實現細節。
橋接模式的缺點
橋接模式的引入會增加系統的理解與設計難度,由於聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與編程。
橋接模式要求正確識別出系統中兩個獨立變化的維度,因此其使用范圍具有一定的局限性。
模式適用環境
在以下情況下可以使用橋接模式:
如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯系,通過橋接模式可以使它們在抽象層建立一個關聯關系。
抽象化角色和實現化角色可以以繼承的方式獨立擴展而互不影響,在程序運行時可以動態將一個抽象化子類的對象和一個實現化子類的對象進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。
一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展。
雖然在系統中使用繼承是沒有問題的,但是由於抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
對於那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。