首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 設計模式(9):橋接模式——分離抽象與實現的多維變化應對之道

     編輯:關於.NET

前言:當變化不再單一維度

在軟體系統中,某些類型天生具有「多維度變化」的特性。例如:日誌記錄系統既要支援「資料庫、文字檔、XML」多種記錄方式,又要跨「.NET、Java、Node.js」多平台運行。若用傳統繼承,類別階層將呈笛卡兒積爆炸:NDatabaseLog、JDatabaseLog、NTextFileLog、JTextFileLog... 橋接模式(Bridge Pattern)正是為了解決此類「正交變化」而生:將抽象部分與實現部分分離,使它們都可以獨立地變化(GoF)。

核心洞見:繼承 vs 組合的本質選擇

創建型模式處理「抽象穩定、實現變化」的單向依賴;但當「抽象本身也在變化」時,繼承鏈會將兩個變化維度強綁定。橋接模式的關鍵動作是:將繼承關係轉化為組合關係,打破「是一種」的靜態綁定,建立「有一個」的動態委派。

抽象不應依賴實現細節

實戰演進:日誌系統的橋接重構

階段 1:單一維度變化——記錄方式


public abstract class Log {
    public abstract void Write(string log);
}
public class DatabaseLog : Log { /* ... */ }
public class TextFileLog : Log { /* ... */ }

單一維度類別結構

階段 2:引入平台維度——繼承爆炸的開始


// 每種記錄方式 × 每個平台 = 類別爆炸
public class NDatabaseLog : DatabaseLog { /* .NET 實作 */ }
public class JDatabaseLog : DatabaseLog { /* Java 實作 */ }
public class NTextFileLog : TextFileLog { /* .NET 實作 */ }
public class JTextFileLog : TextFileLog { /* Java 實作 */ }

雙維度繼承結構

看似符合開放封閉原則(新增平台只需新增子類別),實則暗藏禍機:

  • 違反單一職責:NDatabaseLog 同時因「記錄方式」與「平台」兩個原因而變化
  • 程式碼重複:不同平台的資料庫連線邏輯在各子類別中重複
  • 擴展性差:若再新增「XML 記錄」,需建立 NXmlLog、JXmlLog...

階段 3:橋接重構——正交分離

步驟 1:抽象維度(記錄方式)保持不變


public abstract class Log {
    protected ImpLog _implementor; // 橋接引用
    public ImpLog Implementor { set { _implementor = value; } }
    public virtual void Write(string log) => _implementor.Execute(log);
}
public class DatabaseLog : Log { /* 可覆寫 Write 增加特有邏輯 */ }
public class TextFileLog : Log { /* ... */ }

步驟 2:提煉實現維度(平台)介面


public abstract class ImpLog {
    public abstract void Execute(string msg);
}
public class NImpLog : ImpLog { public override void Execute(string msg) { /* .NET 實作 */ } }
public class JImpLog : ImpLog { public override void Execute(string msg) { /* Java 實作 */ } }

實現維度類別結構

步驟 3:客戶端動態組合


class App {
    public static void Main() {
        // .NET 平台的資料庫日誌
        Log dbLog = new DatabaseLog();
        dbLog.Implementor = new NImpLog();
        dbLog.Write("Error");
        
        // Java 平台的文字檔日誌
        Log fileLog = new TextFileLog();
        fileLog.Implementor = new JImpLog();
        fileLog.Write("Info");
    }
}

橋接模式完整結構

為何不用多重繼承?

若 C# 支援多重繼承,可能寫成:


public class NDatabaseLog : DatabaseLog, IImpLog { /* ... */ }

多重繼承模擬橋接

這違反了單一職責原則:NDatabaseLog 同時具備「資料庫記錄特性」與「.NET 平台特性」,有兩個變化原因。橋接模式以組合取代繼承,將「多維度變化」解耦為「多個單一職責類別的動態組合」,是比多重繼承更優的方案。

.NET 生態中的橋接身影

  • ADO.NET Provider 模型:DbConnection、DbCommand(抽象)vs SqlConnection、OracleConnection(實現)
  • ASP.NET Control 架構:控制項邏輯(抽象)vs RenderAdapter/ControlAdapter(實現,針對不同裝置輸出)
  • Entity Framework:DbContext(抽象)vs 各資料庫 Provider(實現)
  • WPF 繪圖系統:Visual(抽象)vs DrawingContext 實現(軟體/硬體光柵化)

橋接模式 vs 策略模式 vs 適配器模式

模式核心意圖關鍵差異
橋接抽象與實現雙向獨立變化結構型、設計時決定、長期共存
策略演算法族群可互換行為型、執行時切換、單向委派
適配器介面不相容轉換事後補救、單向適配、通常不改原類別

實作要點與決策指南

  1. 識別正交變化維度:只有當兩個維度「都強烈變化」且「會導致笛卡兒積爆炸」時,才值得引入橋接
  2. 介面設計:Implementor 介面應針對 Implementor 客戶端(Abstraction)設計,而非完整暴露實現細節
  3. 建立物件的責任:橋接模式不負責建立 ConcreteImplementor,通常搭配工廠模式或 DI 容器
  4. 效能考量:雙重間接呼叫(Abstraction → Implementor)有微小開銷,極高效能場景需評估

總結

橋接模式是應對「多維度正交變化」的終極武器。它以「組合優於繼承」為核心,將單一巨大的類別階層拆解為兩個獨立演化的平行階層,並透過動態委派實現運行期組合。雖然增加了抽象層級,但換來的是系統在面對「新平台、新記錄方式、新序列化格式」等變化時的從容與優雅。記住:當你看到類別名稱出現「平台×功能」的排列組合命名時,橋接模式大概率是正解。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved