引言
例如我有好幾個項目,需要外包出去做各種類型的測試,不同的公司基礎費用不同,不同的測試類型價格也是不同的。此時不同的項目選擇不同的公司和不同類型的測試進行測試價格都是不同的。於是我們可以創建一個項目抽象類,一個公司抽象類,一個測試類型抽象類,然后實現各自的具體類,不同的項目使用不同的公司和測試類型,進行測試。抽象化與實現化解耦,二者可以獨立變化。我們將這種通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦的類型稱為橋接模式。
概念
橋接(Bridge)是用於把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬於結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。
這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立於接口實現類。這兩種類型的類可被結構化改變而互不影響。
結構圖
橋接(Bridge)模式中的主要角色:
- 抽象化(Abstraction)角色:定義抽象類,並包含一個對實現化對象的引用。
- 擴展抽象化(Refined Abstraction)角色:是抽象化角色的子類,實現父類中的業務方法,並通過組合關系調用實現化角色中的業務方法。
- 實現化(Implementor)角色:定義實現化角色的接口,供擴展抽象化角色調用。
- 具體實現化(Concrete Implementor)角色:給出實現化角色接口的具體實現。
具體實現
以引言中的示例進行實現。一個產品需要外包出去進行測試。
涉及三個角色:產品、外包公司、測試類型
using System; namespace Bridge { class Program { static void Main(string[] args) { Company beijingCompany = new BeijingCompany(); TestType manualTest = new ManualTest(); Prodeuct managementProdeuct = new ManagementProdeuct(beijingCompany, manualTest); managementProdeuct.Run(); Company shanghaiCompany = new ShanghaiCompany(); TestType autoTest = new AutoTest(); Prodeuct operationProdeuct = new ManagementProdeuct(shanghaiCompany, autoTest); operationProdeuct.Run(); Console.Read(); } } /// <summary> /// 軟件產品,抽象類 /// 充當抽象化角色 /// 將抽象部分與實現部分分離,使他們都可以獨立的變化 /// </summary> public abstract class Prodeuct { private readonly Company _company; private readonly TestType _testType; // 使用組合,一個產品外包出去,需要一個公司進行不同測試類型的測試 public Prodeuct(Company company, TestType testType) { this._company = company; this._testType = testType; } public string Name { get; set; } // 執行實現部分 public void Run() { Console.WriteLine($"{this._company.Name}對產品{Name}進行{this._testType.Name},總共花費時間{this._company.BaseTime + this._testType.RunTime}。"); Console.WriteLine("======================================================"); } } /// <summary> /// 公司,對實現部分進行抽象 /// </summary> public abstract class Company { public string Name { get; set; } public Double BaseTime { get; set; } } /// <summary> /// 測試類型,對實現部分進行抽象 /// </summary> public abstract class TestType { public string Name { get; set; } public Double RunTime { get; set; } } /// <summary> /// 管理系統,擴展抽象化角色 /// </summary> public class ManagementProdeuct : Prodeuct { public ManagementProdeuct(Company company, TestType testType) : base(company, testType) { Name = "管理系統"; } } /// <summary> /// 運營系統,擴展抽象化角色 /// </summary> public class OperationProdeuct : Prodeuct { public OperationProdeuct(Company company, TestType testType) : base(company, testType) { Name = "運營系統"; } } /// <summary> /// 公司抽象類具體實現,具體實現化角色 /// </summary> public class BeijingCompany : Company { public BeijingCompany() { Name = "北京公司"; BaseTime = 200; } } /// <summary> /// 公司抽象類具體實現,具體實現化角色 /// </summary> public class ShanghaiCompany : Company { public ShanghaiCompany() { Name = "上海公司"; BaseTime = 100; } } /// <summary> ///測試類型抽象類具體實現,具體實現化角色 /// </summary> public class ManualTest : TestType { public ManualTest() { Name = "手工測試"; RunTime = 30; } } /// <summary> ///測試類型抽象類具體實現,具體實現化角色 /// </summary> public class AutoTest : TestType { public AutoTest() { Name = "自動測試"; RunTime = 10; } } }
運行后結果:
北京公司對產品管理系統進行手工測試,總共花費時間230。 ====================================================== 上海公司對產品管理系統進行自動測試,總共花費時間110。 ======================================================
適用場景
- “抽象部分”和“實現部分”可以以繼承的方式獨立擴展而互不影響,在程序運行時可以動態將一個抽象化子類的對象和一個實現化子類的對象進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。
- 一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都需要獨立進行擴展。
- 對於那些不希望使用繼承或因為多層繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。
優缺點
優點
- 抽象與實現分離,擴展能力強。
- 符合開閉原則。
- 符合合成復用原則。
- 單一職責原則。 抽象部分專注於處理高層邏輯, 實現部分處理平台細節。
缺點
- 由於聚合關系建立在抽象層,要求開發者針對抽象化進行設計與編程,能正確地識別出系統中兩個獨立變化的維度,這增加了系統的理解與設計難度。
與裝飾器模式區別
- 裝飾器模式是為了動態地給一個對象增加功能,而橋接模式時為了讓類在多個維度上自由擴展。
- 裝飾器模式的裝飾者和被裝飾者需要繼承自同一父類,而橋接模式通常不需要;
- 裝飾器模式通常可以嵌套使用,而橋接模式不能。