程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 我的Design Pattern之旅[7]:使用泛型改進Adapter Pattern(OO)

我的Design Pattern之旅[7]:使用泛型改進Adapter Pattern(OO)

編輯:關於C#

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++

/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com

Filename  : DP_AdpaterPattern_Strategy_ClassByTemplate.cpp
Compiler  : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to use Strategy Pattern with Adpater Pattern (Class Adapter) By Template
Release   : 07/11/2007 1.0
*/
#include <iostream>
using namespace std;

class IDrawStrategy {
public:
 virtual void draw() const = 0;
};

class Grapher {
public:
 Grapher(IDrawStrategy* drawStrategy = 0) : _drawStrategy(drawStrategy) {}

public:
 void drawShape() const;
 void setShape(IDrawStrategy* drawStrategy);

protected:
 IDrawStrategy* _drawStrategy;
};

void Grapher::drawShape() const {
 if (_drawStrategy)
   _drawStrategy->draw();
}

void Grapher::setShape(IDrawStrategy* drawStrategy) {
 _drawStrategy = drawStrategy;
}

class IPaint {
public:
 virtual void paint() const = 0;
};

class Triangle : public IPaint {
public:
 void paint() const;
};

void Triangle::paint() const {
 cout << "Draw Triangle" << endl;
}

class Circle : public IPaint {
public:
 void paint() const;
};

void Circle::paint() const {
 cout << "Draw Circle" << endl;
}

class Square : public IPaint {
public:
 void paint() const;
};

void Square::paint() const {
 cout << "Draw Square" << endl;
}

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

template<typename T>
void DrawAdapter<T>::draw() const {
 paint();
}

int main() {
 Grapher grapher(&DrawAdapter<Triangle>());
 grapher.drawShape();

 grapher.setShape(&DrawAdapter<Circle>());
 grapher.drawShape();

 grapher.setShape(&DrawAdapter<Square>());
 grapher.drawShape();
}

執行結果

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

/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com

Filename  : DP_AdpaterPattern_Strategy_ClassByTemplate.cpp
Compiler  : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to use Strategy Pattern with Adpater Pattern (Class Adapter) By Template
Release   : 07/12/2007 1.0
*/
#include "stdafx.h"
using namespace System;

interface class IDrawStrategy {
 void draw();
};

ref class Grapher {
public:
 Grapher() : _drawStrategy(nullptr) {}
 Grapher(IDrawStrategy^ drawStrategy) : _drawStrategy(drawStrategy) {}

public:
 void drawShape();
 void setShape(IDrawStrategy^ drawStrategy);

protected:
 IDrawStrategy^ _drawStrategy;
};

void Grapher::drawShape() {
 if (_drawStrategy != nullptr)
   _drawStrategy->draw();
}

void Grapher::setShape(IDrawStrategy^ drawStrategy) {
 _drawStrategy = drawStrategy;
}

interface class IPaint {
 void paint();
};

ref class Triangle : public IPaint {
public:
 virtual void paint();
};

void Triangle::paint() {
 Console::WriteLine("Draw Triangle");
}

ref class Circle : public IPaint {
public:
 virtual void paint();
};

void Circle::paint() {
 Console::WriteLine("Draw Circle");
}

ref class Square : public IPaint {
public:
 virtual void paint();
};

void Square::paint() {
 Console::WriteLine("Draw Square");
}

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

template<typename T>
void DrawAdapter<T>::draw() {
 paint();
}

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();
}

執行結果

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) OOMusou 2007 http://oomusou.cnblogs.com

Filename  : DP_AdpaterPattern_Strategy_ClassByTemplate_Error.cs
Compiler  : Visual Studio 2005 / C# 2.0
Description : Demo how to use Strategy Pattern with Adpater Pattern (Class Adapter) By Template
Release   : 07/11/2007 1.0
*/
using System;

interface IDrawStrategy {
 void draw();
}

class Grapher {
 protected IDrawStrategy _drawStrategy;

 public Grapher() { }
 public Grapher(IDrawStrategy drawStrategy) {
  _drawStrategy = drawStrategy;
 }

 public void drawShape() {
  if (_drawStrategy != null)
   _drawStrategy.draw();
 }

 public void setShape(IDrawStrategy drawStrategy) {
  _drawStrategy = drawStrategy;
 }
}

interface IPaint {
 void paint();
}

class Triangle : IPaint {
 public void paint() {
  Console.WriteLine("Draw Triangle");
 }
}

class Circle : IPaint {
 public void paint() {
  Console.WriteLine("Draw Circle");
 }
};

class Square : IPaint {
 public void paint() {
  Console.WriteLine("Draw Square");
 }
}

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

class Client {
 static void Main() {
  Grapher grapher = new Grapher(new DrawAdapter<Triangle>());
  grapher.drawShape();

  grapher.setShape(new DrawAdapter<Circle>());
  grapher.drawShape();

  grapher.setShape(new DrawAdapter<Square>());
  grapher.drawShape();
 }
}

以上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

/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com

Filename  : DP_AdpaterPattern_Strategy_ClassByGenerics.cs
Compiler  : Visual Studio 2005 / C# 2.0
Description : Demo how to use Strategy Pattern with Adpater Pattern (Class Adapter) By Template
Release   : 07/11/2007 1.0
*/
using System;

interface IDrawStrategy {
 void draw();
}

class Grapher {
 protected IDrawStrategy _drawStrategy;

 public Grapher() { }
 public Grapher(IDrawStrategy drawStrategy) {
  _drawStrategy = drawStrategy;
 }

 public void drawShape() {
  if (_drawStrategy != null)
   _drawStrategy.draw();
 }

 public void setShape(IDrawStrategy drawStrategy) {
  _drawStrategy = drawStrategy;
 }
}

interface IPaint {
 void paint();
}

class Triangle : IPaint {
 public void paint() {
  Console.WriteLine("Draw Triangle");
 }
}

class Circle : IPaint {
 public void paint() {
  Console.WriteLine("Draw Circle");
 }
};

class Square : IPaint {
 public void paint() {
  Console.WriteLine("Draw Square");
 }
}

class DrawAdapter<T> : IDrawStrategy where T : IPaint, new() {
 protected T _adaptee = new T();

 public void draw() {
  _adaptee.paint();
 }
}

class Client {
 static void Main() {
  Grapher grapher = new Grapher(new DrawAdapter<Triangle>());
  grapher.drawShape();

  grapher.setShape(new DrawAdapter<Circle>());
  grapher.drawShape();

  grapher.setShape(new DrawAdapter<Square>());
  grapher.drawShape();
 }
}

執行結果

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

/**//*
(C) OOMusou 2007 http://oomusou.cnblogs.com

Filename  : DP_AdpaterPattern_Strategy_ClassByGenerics.cs
Compiler  : Visual Studio 2005 / C++/CLI
Description : Demo how to use Strategy Pattern with Adpater Pattern (Class Adapter) By Generics
Release   : 07/20/2007 1.0
*/

#include "stdafx.h"

using namespace System;

interface class IDrawStrategy {
 void draw();
};

ref class Grapher {
public:
 Grapher() {}
 Grapher(IDrawStrategy^ drawStrategy) : _drawStrategy(drawStrategy) {}

public:
 void drawShape();
 void setShape(IDrawStrategy^ drawStrategy);

protected:
 IDrawStrategy^ _drawStrategy;
};

void Grapher::drawShape() {
 if (_drawStrategy != nullptr)
  _drawStrategy->draw();
}

void Grapher::setShape(IDrawStrategy^ drawStrategy) {
 _drawStrategy = drawStrategy;
}

interface class IPaint {
 void paint();
};

ref class Triangle : public IPaint {
public:
 virtual void paint();
};

void Triangle::paint() {
 Console::WriteLine("Draw Triangle");
}

ref class Circle : public IPaint {
public:
 virtual void paint();
};

void Circle::paint() {
 Console::WriteLine("Draw Circle");
}

ref class Square : public IPaint {
public:
 virtual void paint();
};

void Square::paint() {
 Console::WriteLine("Draw Square");
}

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

protected:
 T _adaptee;
};

generic<typename T>
void DrawAdapter<T>::draw() {
 _adaptee->paint();
}

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();
}

執行結果

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

'
'(C) OOMusou 2007 http://oomusou.cnblogs.com
'
'Filename  : DP_AdpaterPattern_Strategy_ClassByGenerics.vb
'Compiler  : Visual Studio 2005 / VB 9
'Description : Demo how to use Strategy Pattern with Adpater Pattern (Class Adapter) By Generics
'Release   : 07/20/2007 1.0
'

Imports System

Interface IDrawStrategyInterface IDrawStrategy
 Sub draw()Sub draw()
End Interface

Class GrapherClass Grapher
 Protected _drawStrategy As IDrawStrategy

 Public Sub New()Sub New(Optional ByRef drawStrategy As IDrawStrategy = Nothing)
  _drawStrategy = drawStrategy
 End Sub

 Public Sub drawShape()Sub drawShape()
  If (_drawStrategy IsNot Nothing) Then
   _drawStrategy.draw()
  End If
 End Sub

 Public Sub setShape()Sub setShape(ByRef drawStrategy As IDrawStrategy)
  _drawStrategy = drawStrategy
 End Sub
End Class

Interface IPaintInterface IPaint
 Sub paint()Sub paint()
End Interface

Class TriangleClass Triangle
 Implements IPaint

 Public Sub paint()Sub paint() Implements IPaint.paint
  Console.WriteLine("Draw Triangle")
 End Sub
End Class

Class CircleClass Circle
 Implements IPaint

 Public Sub paint()Sub paint() Implements IPaint.paint
  Console.WriteLine("Draw Circle")
 End Sub
End Class

Class SquareClass Square
 Implements IPaint

 Public Sub paint()Sub paint() Implements IPaint.paint
  Console.WriteLine("Draw Square")
 End Sub
End Class

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

 Protected _adaptee As T = New T

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

Class ClientClass Client
 Shared Sub Main()Sub Main()
  Dim grapher As New Grapher(New DrawAdapter(Of Triangle))
  grapher.drawShape()

  grapher.setShape(New DrawAdapter(Of Circle))
  grapher.drawShape()

  grapher.setShape(New DrawAdapter(Of Square))
  grapher.drawShape()
 End Sub
End Class

執行結果

Draw Triangle
Draw Circle
Draw Square

62行

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

 Protected _adaptee As T = New T

 Public Sub draw1()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,所以繼承和組合方法皆適用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved