適配器模式是結構型模式中的經典,解決「介面不相容」問題:將一個類的介面轉換為客戶端期望的另一個介面,使原本無法協作的類能夠合作。本文以 C# 為載體,完整展示類適配器(繼承)與物件適配器(組合/委派)兩種形式的代碼實現、結構差異、優劣勢對比、適用場景,並延伸討論 C# 介面與類的多重實現機制對模式的影響。
C# 不支援類多重繼承,但可實現多個介面並繼承單一類。類適配器繼承 Adaptee、實現 Target:
// 目標介面
public interface ITarget {
void Operation1(); // 現有方法
void Operation2(); // 新增方法
}
// 被適配者(現有類)
public class Adaptee {
public void Operation1() {
Console.WriteLine("Adaptee.Operation1");
}
}
// 類適配器:繼承 Adaptee + 實現 ITarget
public class ClassAdapter : Adaptee, ITarget {
// Operation1 直接繼承自 Adaptee
// 實現 Target 獨有的 Operation2
public void Operation2() {
Console.WriteLine("ClassAdapter.Operation2 (轉換邏輯)");
}
}
// 客戶端使用
public class Client {
public void Use(ITarget target) {
target.Operation1();
target.Operation2();
}
}
// 測試
var adapter = new ClassAdapter();
new Client().Use(adapter);
// 輸出:
// Adaptee.Operation1
// ClassAdapter.Operation2 (轉換邏輯)
| 優點 | 缺點 |
|---|---|
| 可覆寫 Adaptee 的虛擬方法(Override) | C# 單一繼承限制:無法適配 Adaptee 及其子類(需為每個子類寫適配器) |
| 結構簡單,單一類完成 | 違反「組合優於繼承」原則,耦合度高 |
| 無額外委派開銷 | 適配器即 Adaptee 子類,型別爆炸風險 |
適配器持有 Adaptee 參考,實現 Target 介面,將呼叫委派給 Adaptee:
// 物件適配器
public class ObjectAdapter : ITarget {
private readonly Adaptee _adaptee;
public ObjectAdapter(Adaptee adaptee) {
_adaptee = adaptee ?? throw new ArgumentNullException(nameof(adaptee));
}
public void Operation1() {
_adaptee.Operation1(); // 委派
}
public void Operation2() {
Console.WriteLine("ObjectAdapter.Operation2 (轉換邏輯)");
}
}
// 使用:可適配 Adaptee 及其任意子類
var adaptee = new Adaptee();
var adapter = new ObjectAdapter(adaptee);
new Client().Use(adapter);
// 甚至適配子類
public class SubAdaptee : Adaptee {
public void SpecialMethod() { }
}
var subAdapter = new ObjectAdapter(new SubAdaptee()); // 同一適配器類
| 優點 | 缺點 |
|---|---|
| 一個適配器適配 Adaptee 及所有子類 | 需額外委派呼叫(微小效能開銷) |
| 符合組合優於繼承,低耦合 | 難以覆寫 Adaptee 方法(需先建立子類再適配) |
| 執行期可動態切換被適配物件 | 結構稍複雜 |
| 決策維度 | 選擇類適配器 | 選擇物件適配器 |
|---|---|---|
| 被適配類階層 | 單一類、無子類、或不需適配子類 | 類族、需統一適配多個子類 |
| 方法覆寫需求 | 需覆寫 Adaptee 虛擬方法 | 無覆寫需求,或可接受建立子類 |
| 耦合度容忍 | 可接受緊耦合 | 追求鬆耦合、可測試性 |
| C# 語言限制 | 利用「類繼承+介面實現」模擬多重繼承 | 純介面導向,無繼承限制 |
| 現代 C# 偏好 | 罕用 | 推薦(符合 SOLID 原則) |
適配器同時實現 Target 與 Adaptee 介面,可在兩種上下文流通:
public interface ITarget { void Request(); }
public interface IAdaptee { void SpecificRequest(); }
public class TwoWayAdapter : ITarget, IAdaptee {
private readonly Adaptee _adaptee = new Adaptee();
public void Request() => _adaptee.SpecificRequest();
public void SpecificRequest() => _adaptee.SpecificRequest();
}
public interface ITarget {
void Operation1();
void Operation2() => Console.WriteLine("Target 預設 Operation2"); // 預設實現
}
public class Adapter : Adaptee, ITarget {
// 僅需實現 Operation1(若 Adaptee 無則自行實現)
// Operation2 直接用介面預設實現,無須在適配器中寫代碼
}
IDbConnection 適配各廠商具體連線類適配器模式以「介面轉換」解決相容性難題。C# 語言特性(單一繼承、多介面實現、預設介面方法)深刻影響了兩種形式的實現方式與取捨。物件適配器(組合)是現代 C# 開發的首選,符合依賴倒置、開閉原則,支援動態組合與測試替換。類適配器僅在需覆寫被適配者行為、且被適配者為單一密封類時考慮。掌握兩者本質差異,能在重構、整合、遷移等場景做出最優架構決策。