在(原創) 我的Design Pattern之旅[1]:使用template改進Strategy Pattern (高級) (Design Pattern) (C++) (OO C++) (Template C++)中,使用了C++的template改進strategy pattern,本文使用C#的generic改進strategy pattern。
Abstract
在(原創) 我的Design Pattern之旅:使用template改進Strategy Pattern (OO) (Design Pattern) (C/C++) (template)中,使用了C++的template改進strategy pattern,本文使用C#的generic改進strategy pattern。
Introduction
C# 2.0加入了generic對泛型的支援,所以想將原來C++的template程式一行一行的改成C# generic。
在strategy pattern中,通常為了讓strategy能完全存取物件的public method/property,我們會利用傳this將整個物件傳給strategy
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
public void drawShape()
{
this.shape.draw(this);
}
為了達成此需求,我們的interface須如此定義
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IShape
{
void draw(Grapher grapher);
}
完整的程式碼如下
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3![](/Images/OutliningIndicators/InBlock.gif)
4
Filename : DP_StrategyPattern3_polymorphism_this.cs
5
Compiler : Visual Studio 2005 / C# 2.0
6
Description : Demo how to use Strategy Pattern with this
7
Release : 04/07/2007 1.0
8
*/
9
using System;
10![](/Images/OutliningIndicators/None.gif)
11![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IShape
{
12
void draw(Grapher grapher);
13
}
14![](/Images/OutliningIndicators/None.gif)
15![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Grapher
{
16
private IShape shape;
17
private string text;
18
19![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher()
{ }
20![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher(IShape shape) : this(shape, "Hello Shape!!")
{ }
21![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher(IShape shape, string text)
{
22
this.shape = shape;
23
this.text = text;
24
}
25
26![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void drawShape()
{
27
this.shape.draw(this);
28
}
29
30![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void setShape(IShape shape, string text)
{
31
this.text = text;
32
this.shape = shape;
33
}
34
35![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string getText ()
{
36
return this.text;
37
}
38
}
39![](/Images/OutliningIndicators/None.gif)
40![](/Images/OutliningIndicators/None.gif)
41![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Triangle : IShape
{
42![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(Grapher grapher)
{
43
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
44
}
45
}
46![](/Images/OutliningIndicators/None.gif)
47![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Circle: IShape
{
48![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(Grapher grapher)
{
49
Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
50
}
51
}
52![](/Images/OutliningIndicators/None.gif)
53![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Square : IShape
{
54![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(Grapher grapher)
{
55
Console.WriteLine("Draw {0:s} in Square", grapher.getText());
56
}
57
}
58![](/Images/OutliningIndicators/None.gif)
59![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class main
{
60![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void Main()
{
61
Grapher theGrapher = new Grapher(new Square());
62
theGrapher.drawShape();
63![](/Images/OutliningIndicators/InBlock.gif)
64
theGrapher.setShape(new Circle(), "Hello C#!!");
65
theGrapher.drawShape();
66
}
67
}
執行結果
Draw Hello Shape!! in Square
Draw Hello C#!! in Circle
這樣的設計看似完美,但淺在一個問題
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IShape
{
void draw(Grapher grapher);
}
規定了draw()一定要傳Grapher型別進去,若將來因為需求改變,又多了一個Painter class,且和Grapher毫無相關,既非繼承亦非多型,但又想使用這些strategy,但因為IShape已規定只能傳Grapher型別,所以Painter無法繼續使用IShape interface。
在(原創) 我的Design Pattern之旅[3]:使用template改進Strategy Pattern (OO) (Design Pattern) (C/C++) (template)中,我們已成功使用C++的template解決這種型別被限制的問題,所以我們試著也使用C#的Generic來解決。
首些我們先將IShape改用Generic
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IShape<T>
{
void draw(T grapher);
}
接著其他實做IShape<T>的程式亦需改寫成
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Triangle<T> : IShape<T> where T : IGrapher
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(T grapher)
{
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
}
}
C#多了where這個keyword,這是我卡最久的地方,也是C# generic和C++ template不同之處,C++ template並沒有限定泛型的型別,但C# generic的泛型是『有限的汎型』,或稱『以interface為基礎的汎型』、『強型別泛型』,也就是說,C#泛型不能像C++泛型那樣天馬行空的汎型,C#泛型必須『限制』在interface下,所以where稱為『constraint』
where T : IGrapher
表示泛型T需限制在IGrapher的interface下
也因此,因為各strategy會用到物件的getText(),所以我們定義IGrapher interface一定要有getText()
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IGrapher
{
string getText();
}
日後若有Painter class也想使用IShape的strategy,只要也實做IGrapher即可。
完整程式碼如下
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3![](/Images/OutliningIndicators/InBlock.gif)
4
Filename : DP_StrategyPattern3_polymorphism_this.cs
5
Compiler : Visual Studio 2005 / C# 2.0
6
Description : Demo how to use Strategy Pattern with this by Generic
7
Release : 04/07/2007 1.0
8
*/
9
using System;
10![](/Images/OutliningIndicators/None.gif)
11![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IGrapher
{
12
string getText();
13
}
14![](/Images/OutliningIndicators/None.gif)
15![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IShape<T>
{
16
void draw(T grapher);
17
}
18![](/Images/OutliningIndicators/None.gif)
19![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Grapher : IGrapher
{
20
private IShape<Grapher> shape;
21![](/Images/OutliningIndicators/InBlock.gif)
22
private string text;
23![](/Images/OutliningIndicators/InBlock.gif)
24![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher()
{ }
25![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher(IShape<Grapher> shape) : this(shape, "Hello Shape!!")
{ }
26![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher(IShape<Grapher> shape, string text)
{
27
this.shape = shape;
28
this.text = text;
29
}
30![](/Images/OutliningIndicators/InBlock.gif)
31![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void drawShape()
{
32
this.shape.draw(this);
33
}
34
35![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void setShape(IShape<Grapher> shape, string text)
{
36
this.text = text;
37
this.shape = shape;
38
}
39
40![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string getText ()
{
41
return this.text;
42
}
43
}
44![](/Images/OutliningIndicators/None.gif)
45![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Triangle<T> : IShape<T> where T : IGrapher
{
46![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(T grapher)
{
47
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
48
}
49
}
50![](/Images/OutliningIndicators/None.gif)
51![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Circle<T> : IShape<T> where T : IGrapher
{
52![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(T grapher)
{
53
Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
54
}
55
}
56![](/Images/OutliningIndicators/None.gif)
57![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Square<T> : IShape<T> where T : IGrapher
{
58![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(T grapher)
{
59
Console.WriteLine("Draw {0:s} in Square", grapher.getText());
60
}
61
}
62![](/Images/OutliningIndicators/None.gif)
63![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class main
{
64![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void Main()
{
65
Grapher theGrapher = new Grapher(new Square<Grapher>());
66
theGrapher.drawShape();
67
68
theGrapher.setShape(new Circle<Grapher>(), "Hello C#!!");
69
theGrapher.drawShape();
70
}
71
}
執行結果
Draw Hello Shape!! in Square
Draw Hello C#!! in Circle
Conclusion
C#泛型有什麼好處呢?
where T : IGrapher, IPainter
where後面的限制,並不是只能接一個interface或class而已,還能繼續接,若以後要擴充泛型T的使用,只要繼續接下去即可,如此的寫法已經比物件導向的多型還強,多型限定只能在一個interface或abstract class的多型物件體系,但泛型可以是多個interface或多個abstract class的物件體系,所以泛型可視為『更強的多型』。
假如你同時懂C++和C#,或許會覺得用C#泛型解決strategy pattern『不是那麼漂亮』!!因為若還要用interface,我大可這樣寫
1![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3![](/Images/OutliningIndicators/InBlock.gif)
4
Filename : DP_StrategyPattern3_polymorphism_this_interface.cs
5
Compiler : Visual Studio 2005 / C# 2.0
6
Description : Demo how to use Strategy Pattern with this by interface
7
Release : 04/07/2007 1.0
8
*/
9
using System;
10![](/Images/OutliningIndicators/None.gif)
11![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IGrapher
{
12
string getText();
13
}
14![](/Images/OutliningIndicators/None.gif)
15![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
interface IShape
{
16
void draw(IGrapher grapher);
17
}
18![](/Images/OutliningIndicators/None.gif)
19![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Grapher : IGrapher
{
20
private IShape shape;
21![](/Images/OutliningIndicators/InBlock.gif)
22
private string text;
23![](/Images/OutliningIndicators/InBlock.gif)
24![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher()
{ }
25![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher(IShape shape) : this(shape, "Hello Shape!!")
{ }
26![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public Grapher(IShape shape, string text)
{
27
this.shape = shape;
28
this.text = text;
29
}
30![](/Images/OutliningIndicators/InBlock.gif)
31![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void drawShape()
{
32
this.shape.draw(this);
33
}
34![](/Images/OutliningIndicators/InBlock.gif)
35![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void setShape(IShape shape, string text)
{
36
this.text = text;
37
this.shape = shape;
38
}
39![](/Images/OutliningIndicators/InBlock.gif)
40![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string getText()
{
41
return this.text;
42
}
43
}
44![](/Images/OutliningIndicators/None.gif)
45![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Triangle : IShape
{
46![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(IGrapher grapher)
{
47
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
48
}
49
}
50![](/Images/OutliningIndicators/None.gif)
51![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Circle : IShape
{
52![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(IGrapher grapher)
{
53
Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
54
}
55
}
56![](/Images/OutliningIndicators/None.gif)
57![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Square : IShape
{
58![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public void draw(IGrapher grapher)
{
59
Console.WriteLine("Draw {0:s} in Square", grapher.getText());
60
}
61
}
62![](/Images/OutliningIndicators/None.gif)
63![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class main
{
64![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void Main()
{
65
Grapher theGrapher = new Grapher(new Square());
66
theGrapher.drawShape();
67![](/Images/OutliningIndicators/InBlock.gif)
68
theGrapher.setShape(new Circle(), "Hello C#!!");
69
theGrapher.drawShape();
70
}
71
}
沒錯,這也是C#泛型的一個限制,『一個基於interface或class的泛型』,這種『強型別泛型』,好處是讓泛型也可有intelliSense,且在compile-time就能發現錯誤,不像C++屬於『弱型別泛型』,常得在run-time才能發現錯誤,所以C#泛型較C++穩定,不過也由於限制過多,所以比較沒有泛型的味道,以我個人來說,我比較喜歡C++泛型,因為既然要泛型,就是放諸四海皆準,擺脫型別的限制,擺脫interface的枷鎖,而不是到最後還是得靠interface。
雖然如此,仍就可將C#泛型看成『更強的多型』,很多原本多型設計,可以考慮用泛型讓設計應用更廣。
See Also
(原創) 我的Design Pattern之旅[3]:使用template改進Strategy Pattern (OO) (Design Pattern) (C/C++) (template)
(轉貼) Anders Hejlsberg談C#、Java和C++中的泛型 (,NET) (C#)