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

C# 適配器模式實戰:類別與物件適配器雙重實作指南

     編輯:關於C#

前言

當需要使用現有類別,但其介面不符合當前系統需求時,適配器模式提供標準解決方案:將一個類別的介面轉換成客戶端期望的另一個介面。適配器模式分為「類別適配器」(多重繼承/介面實作)與「物件適配器」(組合/委派)兩種形式。本文以訊息系統為例,展示如何將 SqlMessage(Get/Insert)適配為 IMessage(Select/Add),並完整對比兩種適配器實作優劣。

模式結構與角色

  • Target(目標介面):IMessage,客戶端期望介面(Select()、Add())。
  • Adaptee(被適配者):SqlMessage,現有類別(Get()、Insert())。
  • Adapter(適配器):實作 Target,轉發呼叫給 Adaptee。
    • Class Adapter(類別適配器):Message : SqlMessage, IMessage,繼承 Adaptee + 實作 Target。
    • Object Adapter(物件適配器):Message2 : IMessage,組合 Adaptee 實例 + 實作 Target。
  • Client(客戶端):僅依賴 IMessage 介面使用。
適配器模式類別圖

核心程式碼機制深度解析

1. 目標介面與被適配者

public interface IMessage
{
    List<MessageModel> Select();
    bool Add(MessageModel mm);
}

public class SqlMessage
{
    public List<MessageModel> Get()
    {
        var l = new List<MessageModel>();
        l.Add(new MessageModel("SQL方式獲取Message", DateTime.Now));
        return l;
    }
    public bool Insert(MessageModel mm) { return true; }
}

2. 類別適配器:繼承 + 實作

public class Message : SqlMessage, IMessage
{
    public List<MessageModel> Select() => base.Get();   // 轉發給繼承的 Get()
    public bool Add(MessageModel mm) => base.Insert(mm); // 轉發給繼承的 Insert()
}

特點:

  • 使用繼承(C# 單繼承類別 + 實作介面),適配器**是一個** Adaptee。
  • 可覆寫 Adaptee 的虛擬方法(擴展性)。
  • 無法適配 Adaptee 的子類別(繼承固定)。
  • C# 不支援多重類別繼承,故 Adaptee 必須為類別(非介面)。

3. 物件適配器:組合 + 委派

public class Message2 : IMessage
{
    private SqlMessage _sqlMessage = new SqlMessage(); // 組合

    public List<MessageModel> Select() => _sqlMessage.Get();
    public bool Add(MessageModel mm) => _sqlMessage.Insert(mm);
}

特點:

  • 使用組合,適配器**有一個** Adaptee。
  • 可適配 Adaptee 及其所有子類別(多型)。
  • 無法輕易覆寫 Adaptee 方法(需子類別化 Adaptee 再組合)。
  • 符合「組合優於繼承」原則,更靈活、更主流。

4. 客戶端:統一介面使用


IMessage m;

m = new Message();    // 類別適配器
Response.Write("類適配器方式");
Response.Write(m.Add(new MessageModel("插入", Now)));
Response.Write(m.Select()[0].Message);

m = new Message2();   // 物件適配器
Response.Write("對象適配器方式");
Response.Write(m.Add(new MessageModel("插入", Now)));
Response.Write(m.Select()[0].Message);

執行結果驗證


類適配器方式
True
SQL方式獲取Message 2007-4-8 20:59:29

對象適配器方式
True
SQL方式獲取Message 2007-4-8 20:59:29

兩種適配器對客戶端行為完全一致,驗證介面轉換正確。

類別適配器 vs 物件適配器:深度對比

維度類別適配器物件適配器
實現方式繼承 Adaptee + 實作 Target組合 Adaptee + 實作 Target
與 Adaptee 關係Is-A (繼承)Has-A (組合)
覆寫 Adaptee 行為容易 (override virtual)困難 (需替換組合物件)
適配 Adaptee 子類別不可 (繼承固定)可 (多型注入)
耦合度較高 (繼承耦合)較低 (介面耦合)
適用場景Adaptee 穩定、需擴展行為Adaptee 多變、需多型、現代首選

適配器模式進階變體與應用

雙向適配器

適配器同時實作 Target 與 Adaptee 介面,可在兩種系統中互相流通。
實作:class TwoWayAdapter : ITarget, IAdaptee { ... }

預設適配器 / 預設適配器模式

為介面提供空實作預設類別,子類別只需覆寫關心方法。
應用:MouseAdapter (滑鼠事件)、WindowAdapter (視窗事件) — Java Swing/AWT 經典。

.NET 生態中的適配器無處不在

  • 資料適配器:SqlDataAdapter 適配 IDbDataAdapter,填充 DataSet。
  • 集合適配器:List<T> 實作 IList<T>、IReadOnlyList<T>,陣列適配 IList<T>。
  • 流適配器:StreamReader 適配 TextReader 讀取 Stream。
  • COM 互操作:RCW (Runtime Callable Wrapper) 適配 COM 介面為 .NET 介面。
  • ASP.NET Core Middleware:UseMiddleware<T>() 適配自定義類別為 RequestDelegate。
  • 第三方庫整合:AutoMapper、MediatR、FluentValidation 皆大量使用適配器封裝外部庫。

優缺點總結

優點

  • 復用既有類別:無需修改 Adaptee 原始碼即可在新介面下使用。
  • 解耦客戶端與具體實現:客戶端只依賴 Target 介面。
  • 符合開閉原則:新增適配器無需修改既有程式碼。
  • 物件適配器支援多型:一個適配器適配整個 Adaptee 階層。

缺點

  • 增加類別/間接層:過度使用導致「適配器地獄」,難以追蹤呼叫鏈。
  • 類別適配器受限於單繼承,無法同時適配多個 Adaptee 類別。
  • 若 Adaptee 介面龐大,適配器實作繁瑣(可用預設適配器/原始碼生成緩解)。

最佳實務

  • 優先使用**物件適配器**(組合),除非需覆寫 Adaptee 虛擬方法。
  • 適配器命名以 Adapter 結尾,職責單一(只做介面轉換,不加業務邏輯)。
  • 若轉換邏輯複雜,考慮建立「轉換器/映射器」類別專責轉換,適配器只委派。
  • 文件化記錄:為什麼需要適配、源介面與目標介面差異、效能影響。

總結

適配器模式以「介面轉換」消除「介面不兼容」障礙,讓既有資產在新架構中無縫復用。本範例完整展示了類別適配器(繼承)與物件適配器(組合)雙重實作,驗證了兩者對客戶端行為一致性,並深度對比了繼承與組合在適配場景下的優劣。實務中,從資料庫驅動、集合介面、流包裝到微服務網關轉換、第三方 SDK 整合、舊系統遷移包裝,適配器模式是系統整合、架構遷移、庫封裝的核心工具。掌握其兩種形式優劣、進階變體(雙向/預設)與現代最佳實務(組合優先、單一職責、文件化),能讓你在面對「介面不匹配、舊新共存、多庫整合」的系統挑戰時,從容設計出清晰邊界、低耦合、易演進的適配層架構。


參考資源:DoFactory Adapter Pattern

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