(原創) 我的Design Pattern之旅[4]:使用Generic改進Strategy Pattern (OO) (Design Pattern) (.NET) (C#)...

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

ExpandedBlockStart.gif ContractedBlock.gif public   void  drawShape()  dot.gif {
InBlock.gif  
this.shape.draw(this);
ExpandedBlockEnd.gif}


為了達成此需求,我們的interface須如此定義

ExpandedBlockStart.gif ContractedBlock.gif interface  IShape  dot.gif {
InBlock.gif  
void draw(Grapher grapher);
ExpandedBlockEnd.gif}


完整的程式碼如下


 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /* 
 2InBlock.gif(C) OOMusou 2007 http://oomusou.cnblogs.com
 3InBlock.gif
 4InBlock.gifFilename    : DP_StrategyPattern3_polymorphism_this.cs
 5InBlock.gifCompiler    : Visual Studio 2005 / C# 2.0
 6InBlock.gifDescription : Demo how to use Strategy Pattern with this
 7InBlock.gifRelease     : 04/07/2007 1.0
 8ExpandedBlockEnd.gif*/

 9 None.gif using  System;
10 None.gif
11 ExpandedBlockStart.gifContractedBlock.gif interface  IShape  dot.gif {
12InBlock.gif  void draw(Grapher grapher);
13ExpandedBlockEnd.gif}

14 None.gif
15 ExpandedBlockStart.gifContractedBlock.gif class  Grapher  dot.gif {
16InBlock.gif  private IShape shape;
17InBlock.gif  private string text;
18InBlock.gif  
19ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher() dot.gif{ }
20ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher(IShape shape) : this(shape, "Hello Shape!!"dot.gif{ }
21ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher(IShape shape, string text) dot.gif{
22InBlock.gif    this.shape = shape;
23InBlock.gif    this.text = text;
24ExpandedSubBlockEnd.gif  }
 
25InBlock.gif  
26ExpandedSubBlockStart.gifContractedSubBlock.gif  public void drawShape() dot.gif{
27InBlock.gif    this.shape.draw(this);
28ExpandedSubBlockEnd.gif  }

29InBlock.gif  
30ExpandedSubBlockStart.gifContractedSubBlock.gif  public void setShape(IShape shape, string text) dot.gif{
31InBlock.gif    this.text = text;
32InBlock.gif    this.shape = shape;
33ExpandedSubBlockEnd.gif  }

34InBlock.gif  
35ExpandedSubBlockStart.gifContractedSubBlock.gif  public string getText () dot.gif{
36InBlock.gif    return this.text;
37ExpandedSubBlockEnd.gif  }

38ExpandedBlockEnd.gif}

39 None.gif
40 None.gif
41 ExpandedBlockStart.gifContractedBlock.gif class  Triangle : IShape  dot.gif {
42ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(Grapher grapher) dot.gif{
43InBlock.gif    Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
44ExpandedSubBlockEnd.gif  }

45ExpandedBlockEnd.gif}

46 None.gif
47 ExpandedBlockStart.gifContractedBlock.gif class  Circle: IShape  dot.gif {
48ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(Grapher grapher) dot.gif{
49InBlock.gif    Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
50ExpandedSubBlockEnd.gif  }

51ExpandedBlockEnd.gif}

52 None.gif
53 ExpandedBlockStart.gifContractedBlock.gif class  Square : IShape  dot.gif {
54ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(Grapher grapher) dot.gif{
55InBlock.gif    Console.WriteLine("Draw {0:s} in Square", grapher.getText());
56ExpandedSubBlockEnd.gif  }

57ExpandedBlockEnd.gif}

58 None.gif
59 ExpandedBlockStart.gifContractedBlock.gif class  main  dot.gif {
60ExpandedSubBlockStart.gifContractedSubBlock.gif  public static void Main() dot.gif{
61InBlock.gif    Grapher theGrapher = new Grapher(new Square());
62InBlock.gif    theGrapher.drawShape();
63InBlock.gif
64InBlock.gif    theGrapher.setShape(new Circle(), "Hello C#!!");
65InBlock.gif    theGrapher.drawShape();
66ExpandedSubBlockEnd.gif  }

67ExpandedBlockEnd.gif}


執行結果

None.gif Draw Hello Shape!! in Square
None.gifDraw Hello C#!! in Circle


這樣的設計看似完美,但淺在一個問題

ExpandedBlockStart.gif ContractedBlock.gif interface  IShape  dot.gif {
InBlock.gif  
void draw(Grapher grapher);
ExpandedBlockEnd.gif}


規定了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

ExpandedBlockStart.gif ContractedBlock.gif interface  IShape < T >   dot.gif {
InBlock.gif  
void draw(T grapher);
ExpandedBlockEnd.gif}


接著其他實做IShape<T>的程式亦需改寫成

ExpandedBlockStart.gif ContractedBlock.gif class  Triangle < T >  : IShape < T >  where T : IGrapher  dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif 
public void draw(T grapher) dot.gif{
InBlock.gif   Console.WriteLine(
"Draw {0:s} in Triangle", grapher.getText());
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}


C#多了where這個keyword,這是我卡最久的地方,也是C# generic和C++ template不同之處,C++ template並沒有限定泛型的型別,但C# generic的泛型是『有限的汎型』,或稱『以interface為基礎的汎型』、『強型別泛型』,也就是說,C#泛型不能像C++泛型那樣天馬行空的汎型,C#泛型必須『限制』在interface下,所以where稱為『constraint』

None.gif where T : IGrapher


表示泛型T需限制在IGrapher的interface下

也因此,因為各strategy會用到物件的getText(),所以我們定義IGrapher interface一定要有getText()

ExpandedBlockStart.gif ContractedBlock.gif interface  IGrapher  dot.gif {
InBlock.gif  
string getText();
ExpandedBlockEnd.gif}


日後若有Painter class也想使用IShape的strategy,只要也實做IGrapher即可。

完整程式碼如下


 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /* 
 2InBlock.gif(C) OOMusou 2007 http://oomusou.cnblogs.com
 3InBlock.gif
 4InBlock.gifFilename    : DP_StrategyPattern3_polymorphism_this.cs
 5InBlock.gifCompiler    : Visual Studio 2005 / C# 2.0
 6InBlock.gifDescription : Demo how to use Strategy Pattern with this by Generic
 7InBlock.gifRelease     : 04/07/2007 1.0
 8ExpandedBlockEnd.gif*/

 9 None.gif using  System;
10 None.gif
11 ExpandedBlockStart.gifContractedBlock.gif interface  IGrapher  dot.gif {
12InBlock.gif  string getText();
13ExpandedBlockEnd.gif}

14 None.gif
15 ExpandedBlockStart.gifContractedBlock.gif interface  IShape < T >   dot.gif {
16InBlock.gif  void draw(T grapher);
17ExpandedBlockEnd.gif}

18 None.gif
19 ExpandedBlockStart.gifContractedBlock.gif class  Grapher : IGrapher  dot.gif {
20InBlock.gif  private IShape<Grapher> shape;
21InBlock.gif
22InBlock.gif  private string text;
23InBlock.gif
24ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher() dot.gif{ }
25ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher(IShape<Grapher> shape) : this(shape, "Hello Shape!!"dot.gif{ }
26ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher(IShape<Grapher> shape, string text) dot.gif{
27InBlock.gif    this.shape = shape;
28InBlock.gif    this.text = text;
29ExpandedSubBlockEnd.gif  }
 
30InBlock.gif
31ExpandedSubBlockStart.gifContractedSubBlock.gif  public void drawShape() dot.gif{
32InBlock.gif    this.shape.draw(this);
33ExpandedSubBlockEnd.gif  }

34InBlock.gif  
35ExpandedSubBlockStart.gifContractedSubBlock.gif  public void setShape(IShape<Grapher> shape, string text) dot.gif{
36InBlock.gif    this.text = text;
37InBlock.gif    this.shape = shape;
38ExpandedSubBlockEnd.gif  }

39InBlock.gif  
40ExpandedSubBlockStart.gifContractedSubBlock.gif  public string getText () dot.gif{
41InBlock.gif    return this.text;
42ExpandedSubBlockEnd.gif  }

43ExpandedBlockEnd.gif}

44 None.gif
45 ExpandedBlockStart.gifContractedBlock.gif class  Triangle < T >  : IShape < T >  where T : IGrapher  dot.gif {
46ExpandedSubBlockStart.gifContractedSubBlock.gif public void draw(T grapher) dot.gif{
47InBlock.gif   Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
48ExpandedSubBlockEnd.gif  }

49ExpandedBlockEnd.gif}

50 None.gif
51 ExpandedBlockStart.gifContractedBlock.gif class  Circle < T >  : IShape < T >  where T : IGrapher dot.gif {
52ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(T grapher) dot.gif{
53InBlock.gif    Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
54ExpandedSubBlockEnd.gif  }

55ExpandedBlockEnd.gif}

56 None.gif
57 ExpandedBlockStart.gifContractedBlock.gif class  Square < T >  : IShape < T >  where T : IGrapher  dot.gif {
58ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(T grapher) dot.gif{
59InBlock.gif    Console.WriteLine("Draw {0:s} in Square", grapher.getText());
60ExpandedSubBlockEnd.gif  }

61ExpandedBlockEnd.gif}

62 None.gif
63 ExpandedBlockStart.gifContractedBlock.gif class  main  dot.gif {
64ExpandedSubBlockStart.gifContractedSubBlock.gif  public static void Main() dot.gif{
65InBlock.gif    Grapher theGrapher = new Grapher(new Square<Grapher>());
66InBlock.gif    theGrapher.drawShape();
67InBlock.gif  
68InBlock.gif    theGrapher.setShape(new Circle<Grapher>(), "Hello C#!!");
69InBlock.gif    theGrapher.drawShape();
70ExpandedSubBlockEnd.gif  }

71ExpandedBlockEnd.gif}


執行結果

None.gif Draw Hello Shape!! in Square
None.gifDraw Hello C#!! in Circle


Conclusion
C#泛型有什麼好處呢?

None.gif where T : IGrapher, IPainter


where後面的限制,並不是只能接一個interface或class而已,還能繼續接,若以後要擴充泛型T的使用,只要繼續接下去即可,如此的寫法已經比物件導向的多型還強,多型限定只能在一個interface或abstract class的多型物件體系,但泛型可以是多個interface或多個abstract class的物件體系,所以泛型可視為『更強的多型』。

假如你同時懂C++和C#,或許會覺得用C#泛型解決strategy pattern『不是那麼漂亮』!!因為若還要用interface,我大可這樣寫

 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /* 
 2InBlock.gif(C) OOMusou 2007 http://oomusou.cnblogs.com
 3InBlock.gif
 4InBlock.gifFilename    : DP_StrategyPattern3_polymorphism_this_interface.cs
 5InBlock.gifCompiler    : Visual Studio 2005 / C# 2.0
 6InBlock.gifDescription : Demo how to use Strategy Pattern with this by interface
 7InBlock.gifRelease     : 04/07/2007 1.0
 8ExpandedBlockEnd.gif*/

 9 None.gif using  System;
10 None.gif
11 ExpandedBlockStart.gifContractedBlock.gif interface  IGrapher  dot.gif {
12InBlock.gif  string getText();
13ExpandedBlockEnd.gif}

14 None.gif
15 ExpandedBlockStart.gifContractedBlock.gif interface  IShape  dot.gif {
16InBlock.gif  void draw(IGrapher grapher);
17ExpandedBlockEnd.gif}

18 None.gif
19 ExpandedBlockStart.gifContractedBlock.gif class  Grapher : IGrapher  dot.gif {
20InBlock.gif  private IShape shape;
21InBlock.gif
22InBlock.gif  private string text;
23InBlock.gif
24ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher() dot.gif{ }
25ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher(IShape shape) : this(shape, "Hello Shape!!"dot.gif{ }
26ExpandedSubBlockStart.gifContractedSubBlock.gif  public Grapher(IShape shape, string text) dot.gif{
27InBlock.gif    this.shape = shape;
28InBlock.gif    this.text = text;
29ExpandedSubBlockEnd.gif  }

30InBlock.gif
31ExpandedSubBlockStart.gifContractedSubBlock.gif  public void drawShape() dot.gif{
32InBlock.gif    this.shape.draw(this);
33ExpandedSubBlockEnd.gif  }

34InBlock.gif
35ExpandedSubBlockStart.gifContractedSubBlock.gif  public void setShape(IShape shape, string text) dot.gif{
36InBlock.gif    this.text = text;
37InBlock.gif    this.shape = shape;
38ExpandedSubBlockEnd.gif  }

39InBlock.gif
40ExpandedSubBlockStart.gifContractedSubBlock.gif  public string getText() dot.gif{
41InBlock.gif    return this.text;
42ExpandedSubBlockEnd.gif  }

43ExpandedBlockEnd.gif}

44 None.gif
45 ExpandedBlockStart.gifContractedBlock.gif class  Triangle : IShape  dot.gif {
46ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(IGrapher grapher) dot.gif{
47InBlock.gif    Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
48ExpandedSubBlockEnd.gif  }

49ExpandedBlockEnd.gif}

50 None.gif
51 ExpandedBlockStart.gifContractedBlock.gif class  Circle : IShape  dot.gif {
52ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(IGrapher grapher) dot.gif{
53InBlock.gif    Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
54ExpandedSubBlockEnd.gif  }

55ExpandedBlockEnd.gif}

56 None.gif
57 ExpandedBlockStart.gifContractedBlock.gif class  Square : IShape  dot.gif {
58ExpandedSubBlockStart.gifContractedSubBlock.gif  public void draw(IGrapher grapher) dot.gif{
59InBlock.gif    Console.WriteLine("Draw {0:s} in Square", grapher.getText());
60ExpandedSubBlockEnd.gif  }

61ExpandedBlockEnd.gif}

62 None.gif
63 ExpandedBlockStart.gifContractedBlock.gif class  main  dot.gif {
64ExpandedSubBlockStart.gifContractedSubBlock.gif  public static void Main() dot.gif{
65InBlock.gif    Grapher theGrapher = new Grapher(new Square());
66InBlock.gif    theGrapher.drawShape();
67InBlock.gif
68InBlock.gif    theGrapher.setShape(new Circle(), "Hello C#!!");
69InBlock.gif    theGrapher.drawShape();
70ExpandedSubBlockEnd.gif  }

71ExpandedBlockEnd.gif}


沒錯,這也是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#)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值