經典類別適配器需對每個被適配類別撰寫專屬適配器 (TriangleDrawAdapter, CircleDrawAdapter...),導致類別爆炸。利用泛型可將「被適配類別」參數化,單一 DrawAdapter<T> 即可處理所有實作 IPaint 的類別。
template <typename T>
class DrawAdapter : public IDrawStrategy, private T
{
public:
virtual void draw() const { paint(); } // 直接呼叫 T::paint()
};
// 客戶端動態指定被適配類別
Grapher grapher(&DrawAdapter<Triangle>());
grapher.setShape(&DrawAdapter<Circle>());
C# 泛型不支援繼承型別參數 (class Adapter<T> : T 編譯錯誤),改用組合 + 約束:
public class DrawAdapter<T> : IDrawStrategy where T : IPaint, new()
{
private readonly T _adaptee = new T(); // 約束要求無參建構子
public void Draw() => _adaptee.Paint();
}
// 使用
var grapher = new Grapher(new DrawAdapter<Triangle>());
grapher.SetShape(new DrawAdapter<Circle>());
generic <typename T> where T : IPaint, gcnew()
ref class DrawAdapter : IDrawStrategy
{
T _adaptee = gcnew T();
public: virtual void Draw() { _adaptee->Paint(); }
};
Class DrawAdapter(Of T As {IPaint, New})
Implements IDrawStrategy
Private _adaptee As New T()
Public Sub Draw() Implements IDrawStrategy.Draw
_adaptee.Paint()
End Sub
End Class
| 語言 | 泛型種類 | 適配方式 | 關鍵語法 |
|---|---|---|---|
| ISO C++ | Template | 私有繼承 | template<typename T> class Adapter : T |
| C++/CLI | Template + Generics | 繼承/組合皆可 | template<typename T> ref class / generic<typename T> |
| C# | Generics | 僅組合 | where T : IPaint, new() |
| VB.NET | Generics | 僅組合 | Of T As {IPaint, New} |
public interface IDrawStrategy { void Draw(); }
public interface IPaint { void Paint(); }
public class Triangle : IPaint { public void Paint() => Console.WriteLine("Draw Triangle"); }
public class Circle : IPaint { public void Paint() => Console.WriteLine("Draw Circle"); }
public class Square : IPaint { public void Paint() => Console.WriteLine("Draw Square"); }
public class Grapher
{
private IDrawStrategy _strategy;
public Grapher(IDrawStrategy s = null) => _strategy = s;
public void DrawShape() => _strategy?.Draw();
public void SetShape(IDrawStrategy s) => _strategy = s;
}
public class DrawAdapter<T> : IDrawStrategy where T : IPaint, new()
{
private readonly T _adaptee = new T();
public void Draw() => _adaptee.Paint();
}
// Client
var g = new Grapher(new DrawAdapter<Triangle>());
g.DrawShape(); // Draw Triangle
g.SetShape(new DrawAdapter<Circle>());
g.DrawShape(); // Draw Circle
| 優點 | 缺點 |
|---|---|
| 消除類別爆炸:單一泛型適配器替代 N 個具體適配器 | C#/VB 僅支援組合式,無法覆寫被適配類別虛擬方法 |
| 編譯期型別安全:約束確保 T 實作 IPaint | 要求被適配類別有公用無參建構子 (new() 約束) |
| 客戶端動態組合:執行期決定適配目標 | 無法適配無法實例化的類別 (抽象類、靜態類) |
| 符合開放封閉原則:新增形狀無需修改適配器代碼 | 泛型參數化增加認知複雜度 |
泛型化適配器模式展示了「參數化多態」解決「類別爆炸」的經典應用。C++ Template 支援繼承式適配更靈活;C#/VB Generics 雖限於組合式,但配合介面約束仍能優雅解決核心問題。選擇適配器實作方式時,優先考慮物件適配器 (組合) + 泛型參數化,兼顧彈性與型別安全。