(原創) 我的Design Pattern之旅[7]:使用泛型改進Adapter Pattern (OO) (Design Pattern) (C/C++) (template) (.NET) (C#) (C++/CLI) (VB)

Abstract
Adapter Pattern有Class Adapter和Object Adapter兩種實現方式。Class Adapter的優點是可override Adaptee,且實現方式較簡單,但缺點是只能針對特定class量身訂做Adapter,配合泛型,可解決Class Adapter的缺點。

Introduction
(原創) 我的Design Pattern之旅[6] : Adapter Pattern (OO) (Design Pattern) (C/C++) (.NET) (C#) (C++/CLI) (VB) 中的Grapher範例,我們看到Class Adapter必須針對Triangle、Circle、Square量身訂做TriangleDrawAdapter、CircleDrawAdapter、SquareDrawAdapter,雖然符合OCP,但每個class就得需要專屬的Adapter,會造成class爆炸,本文試著用泛型來解決此問題。


ISO C++


執行結果

Draw Triangle
Draw Circle
Draw Square


從UML可以發現,TriangleDrawAdapter、CircleDrawAdapter和SquareDrawAdapter都不見了,只剩下一個DrawAdapter,為什麼可以這樣子呢?因為Class Adapter的致命傷就是得繼承Adaptee,這是很強的coupling,利用泛型,我們將繼承的class變成泛型的參數
70行

template<typename T>
class DrawAdapter : public IDrawStrategy, private T {
public:
  
virtual void draw() const;  
}
;


這樣就可以在Client動態的以Adaptee為泛型參數傳入Class Adapter

int main() {
  Grapher grapher(
&DrawAdapter<Triangle>());
  grapher.drawShape();
  
  grapher.setShape(
&DrawAdapter<Circle>());
  grapher.drawShape();
  
  grapher.setShape(
&DrawAdapter<Square>());
  grapher.drawShape();
}


C++/CLI


執行結果

Draw Triangle
Draw Circle
Draw Square


在.NET語言內,C++/CLI是唯一得天獨厚有兩種泛型的語言,C++/CLI可以用ISO C++既有的template,也可以用CLI的Generics。有了template,C++/CLI也可以實現這個技巧。


69行

template<typename T>
ref class DrawAdapter : public IDrawStrategy, public T {
public:
  
virtual void draw();  
}
;


80行

int main() {
  Grapher
^ grapher = gcnew Grapher(gcnew DrawAdapter<Triangle>);
  grapher
->drawShape();
  
  grapher
->setShape(gcnew DrawAdapter<Circle>);
  grapher
->drawShape();
  
  grapher
->setShape(gcnew DrawAdapter<Square>);
  grapher
->drawShape();
}


C++/CLI也可以在Client動態改變泛型參數來改善Class Adapter。

看到這裡,你或許會問,C#可以嗎?既然C# 2.0也有泛型,答案很遺憾,目前C# 2.0不行,C# 3.0我還沒試,或許有興趣的朋友可以試看看。


C#


以上C#無法compile成功,錯在55行

// Error : Cannot derive from 'T' because it is a type parameter
class DrawAdapter<T> : IDrawStrategy, T {
  
public void draw() {
    paint();
  }

}


錯誤訊息為

Error : Cannot derive from 'T' because it is a type parameter


也就是說,C#不允許繼承泛型,CLI的語言C#、VB、C++/CLI的Generics都無法用這個技巧,但C#卻可用『組合泛型』的方式完成。

C# by Generics


執行結果

Draw Triangle
Draw Circle
Draw Square


55行

class DrawAdapter<T> : IDrawStrategy where T : IPaint, new() {
  
protected T _adaptee = new T();
  
  
public void draw() {
    _adaptee.paint();
  }

}


C#若要使用泛型的method,就得加上constraint,又因為使用delegation的方式,所以必須將泛型new起來,C#規定要在constraint加上new()。

C++/CLI by Generics


執行結果

Draw Triangle
Draw Circle
Draw Square


72行

generic<typename T>
where T : IPaint, gcnew()
ref class DrawAdapter : public IDrawStrategy  {
public:
  DrawAdapter() : _adaptee(gcnew T)
{}
public:
  
virtual void draw();
  
protected:
  T _adaptee;
}
;


C++/CLI的Generics寫法和C#類似,不過又不完全一樣。

VB by Generics


執行結果

Draw Triangle
Draw Circle
Draw Square


62行

Class DrawAdapter(Of T As {IPaint, New})
  
Implements IDrawStrategy

  
Protected _adaptee As T = New T

  
Public Sub draw1() Implements IDrawStrategy.draw
    _adaptee.paint()
  
End Sub

End Class


我第一次使用VB的泛型,不過我覺得VB泛型的語法挺好的,一目了然。

Conclusion
Design Pattern雖然是OO思維的產物,但很多時候可借用泛型讓Design Pattern更好用,如(原創) 我的Design Pattern之旅[3]:使用template改進Strategy Pattern (OO) (Design Pattern) (C/C++) (template) 及 (原創) 我的Design Pattern之旅[4]:使用Generic改進Strategy Pattern (OO) (Design Pattern) (.NET) (C#) 都曾經用泛型讓Strategy Pattern更加好用。CLI語言C#、VB、C++/CLI若要使用Generics,不能使用傳統Class Adapter的繼承方式,必須使用組合來delegation。C++/CLI因同時有Template和Generics,所以繼承和組合方法皆適用。

See Also
(原創) 我的Design Pattern之旅[3]:使用template改進Strategy Pattern (OO) (Design Pattern) (C/C++) (template)
原創) 我的Design Pattern之旅[4]:使用Generic改進Strategy Pattern (OO) (Design Pattern) (.NET) (C#)
(原創) 我的Design Pattern之旅[6] : Adapter Pattern (OO) (Design Pattern) (C/C++) (.NET) (C#) (C++/CLI) (VB)

posted on 2007-07-19 00:11 真 OO无双 阅读(...) 评论(...) 编辑 收藏

导航

统计