繼承是面向對象的三大特性之一,但不少時候使用繼承的結果卻不盡如人意。除了人盡皆知的緊耦合問題外,有的時候還會致使子類的快速膨脹。設計模式
設想這樣一個場景:最初設計的時候有一個類型Product,但後來隨着新需求的出現,X緣由致使了它的變化,X有兩種狀況,則經過繼承須要建立兩個新的子類ProductX1,ProductX2,但後來有出現了Y因素也會致使Product的變化,若是Y有三種狀況,則會出現ProductX1Y1,ProductX1Y2,ProductX1Y3...等,一共2*3=6個類。ide
使用這種繼承的方式,若是再出現新的變化因素,或者某個變化因素出現了新的狀況,都會致使子類的快速膨脹,給維護帶來很大的挑戰。this
形成這個問題的根本緣由是類型在沿着多個維度變化。爲了應對變化,通常會經過抽象的方法,找到其中比較穩定的部分,而後抽象其行爲,令客戶程序依賴於抽象而不是具體實現。一樣的道理,當一個類型同時受到多個因素變化的影響時,也經過把每一個因素抽象,讓類型依賴於一系列抽象因素的辦法儘可能處理這個問題,這即是橋接模式解決問題的思路。設計
GOF對橋接模式的描述爲:
Decouple an abstraction from its implementationso that the two can vary independently.
— Design Patterns : Elements of Reusable Object-Oriented Software
橋接模式將抽象部分與它的實現部分分離,使它們均可以獨立地變化。code
橋接模式的UML類圖爲
對象
示例代碼:blog
public interface IImpl { void OperationImpl(); } public interface IAbstraction { IImpl Implementor { get; set; } void Operation(); } public class ConcreteImplementatorA : IImpl { public void OperationImpl() { ... } } public class ConcreteImplementatorB : IImpl { public void OperationImpl() { ... } } public class RefinedAbstration : IAbstraction { public IImpl Implementor { get; set; } public void Operation() { Implementor.OperationImpl(); } }
這樣子看起來仍是比較抽象,再舉個具體的例子汽車-道路,目前汽車有小汽車、巴士兩類,路有水泥路、石子路兩類,這樣「車在路上行駛」就會有四種狀況,這個場景用橋接模式來描述的話能夠是:
汽車類的抽象與實現:繼承
public interface IVehicle { string Drive(); } public class Car : IVehicle { public string Drive() { return "Car"; } } public class Bus : IVehicle { public string Drive() { return "Bus"; } }
經過橋接模式關聯道路與汽車:get
public abstract class Road { protected IVehicle vehicle; public Road(IVehicle vehicle) { this.vehicle = vehicle; } public abstract string DriveOnRoad(); } public class UnpavedRoad : Road { public UnpavedRoad(IVehicle vehicle) : base(vehicle) { } public override string DriveOnRoad() { return vehicle.Drive() + " is on Unpaved Road"; } } public class CementRoad : Road { public CementRoad(IVehicle vehicle) : base(vehicle) { } public override string DriveOnRoad() { return vehicle.Drive() + " is on Cement Road"; } }
調用:string
IVehicle vehicle = new Car(); Road road = new CementRoad(vehicle); Console.WriteLine(road.DriveOnRoad()); // Car is on Cement Road Speed speed = new FastSpeed(road); Console.WriteLine(speed.DriveWithSpeed()); // Car is on Cement Road,
在這裏Road依賴的是IVehivle抽象,具體的汽車實如今調用的時候決定。
對比直接繼承出四種類型的方式,這樣作的好處貌似並不明顯,仍是須要四個類,並且更復雜,但若是汽車或者道路類型繼續增長,或者引入了別的變化因素,狀況就不同了。
有個疑惑是關於橋接模式的名稱的,爲何叫橋接模式呢?以前的工廠、適配器等名稱都挺形象的,但橋接模式好像有點不一樣,這要從橋接模式解決問題的思路提及,橋接模式更多的是提示咱們面向對象的設計分解方式,能夠歸納爲三步:
示意圖:
到了第三步,就能夠大概看出橋接模式的形象化表示了,IX、IY和IZ構成鏈接部(被稱爲支座),而每一個具體類造成了一個個橋墩。
因此橋接模式,是以抽象之間的依賴爲橋面、以具體實現爲橋墩,建起了一座鏈接需求與實現的橋樑。
參考書籍: 王翔著 《設計模式——基於C#的工程化實現及擴展》