引言
例如我有好幾個專案,需要外包出去做各種型別的測試,不同的公司基礎費用不同,不同的測試型別價格也是不同的。此時不同的專案選擇不同的公司和不同型別的測試進行測試價格都是不同的。於是我們可以建立一個專案抽象類,一個公司抽象類,一個測試型別抽象類,然後實現各自的具體類,不同的專案使用不同的公司和測試型別,進行測試。抽象化與實現化解耦,二者可以獨立變化。我們將這種通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦的型別稱為橋接模式。
概念
橋接(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。 ======================================================
適用場景
- “抽象部分”和“實現部分”可以以繼承的方式獨立擴充套件而互不影響,在程式執行時可以動態將一個抽象化子類的物件和一個實現化子類的物件進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。
- 一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都需要獨立進行擴充套件。
- 對於那些不希望使用繼承或因為多層繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。
優缺點
優點
- 抽象與實現分離,擴充套件能力強。
- 符合開閉原則。
- 符合合成複用原則。
- 單一職責原則。 抽象部分專注於處理高層邏輯, 實現部分處理平臺細節。
缺點
- 由於聚合關係建立在抽象層,要求開發者針對抽象化進行設計與程式設計,能正確地識別出系統中兩個獨立變化的維度,這增加了系統的理解與設計難度。
與裝飾器模式區別
- 裝飾器模式是為了動態地給一個物件增加功能,而橋接模式時為了讓類在多個維度上自由擴充套件。
- 裝飾器模式的裝飾者和被裝飾者需要繼承自同一父類,而橋接模式通常不需要;
- 裝飾器模式通常可以巢狀使用,而橋接模式不能。