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

我的Design Pattern之旅[4]:使用Generic改進Strategy Pattern(OO)

編輯:關於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

public void drawShape() {
 this.shape.draw(this);
}

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

interface IShape {
 void draw(Grapher grapher);
}

完整的程式碼如下

1/**//*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename  : DP_StrategyPattern3_polymorphism_this.cs
5Compiler  : Visual Studio 2005 / C# 2.0
6Description : Demo how to use Strategy Pattern with this
7Release   : 04/07/2007 1.0
8*/
9using System;
10
11interface IShape {
12 void draw(Grapher grapher);
13}
14
15class Grapher {
16 private IShape shape;
17 private string text;
18
19 public Grapher() { }
20 public Grapher(IShape shape) : this(shape, "Hello Shape!!") { }
21 public Grapher(IShape shape, string text) {
22  this.shape = shape;
23  this.text = text;
24 }
25
26 public void drawShape() {
27  this.shape.draw(this);
28 }
29
30 public void setShape(IShape shape, string text) {
31  this.text = text;
32  this.shape = shape;
33 }
34
35 public string getText () {
36  return this.text;
37 }
38}
39
40
41class Triangle : IShape {
42 public void draw(Grapher grapher) {
43  Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
44 }
45}
46
47class Circle: IShape {
48 public void draw(Grapher grapher) {
49  Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
50 }
51}
52
53class Square : IShape {
54 public void draw(Grapher grapher) {
55  Console.WriteLine("Draw {0:s} in Square", grapher.getText());
56 }
57}
58
59class main {
60 public static void Main() {
61  Grapher theGrapher = new Grapher(new Square());
62  theGrapher.drawShape();
63
64  theGrapher.setShape(new Circle(), "Hello C#!!");
65  theGrapher.drawShape();
66 }
67}

執行結果

Draw Hello Shape!! in Square
Draw Hello C#!! in Circle

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

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

interface IShape<T> {
 void draw(T grapher);
}

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

class Triangle<T> : IShape<T> where T : IGrapher {
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()

interface IGrapher {
 string getText();
}

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

完整程式碼如下

1/**//*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename  : DP_StrategyPattern3_polymorphism_this.cs
5Compiler  : Visual Studio 2005 / C# 2.0
6Description : Demo how to use Strategy Pattern with this by Generic
7Release   : 04/07/2007 1.0
8*/
9using System;
10
11interface IGrapher {
12 string getText();
13}
14
15interface IShape<T> {
16 void draw(T grapher);
17}
18
19class Grapher : IGrapher {
20 private IShape<Grapher> shape;
21
22 private string text;
23
24 public Grapher() { }
25 public Grapher(IShape<Grapher> shape) : this(shape, "Hello Shape!!") { }
26 public Grapher(IShape<Grapher> shape, string text) {
27  this.shape = shape;
28  this.text = text;
29 }
30
31 public void drawShape() {
32  this.shape.draw(this);
33 }
34
35 public void setShape(IShape<Grapher> shape, string text) {
36  this.text = text;
37  this.shape = shape;
38 }
39
40 public string getText () {
41  return this.text;
42 }
43}
44
45class Triangle<T> : IShape<T> where T : IGrapher {
46 public void draw(T grapher) {
47  Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
48 }
49}
50
51class Circle<T> : IShape<T> where T : IGrapher{
52 public void draw(T grapher) {
53  Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
54 }
55}
56
57class Square<T> : IShape<T> where T : IGrapher {
58 public void draw(T grapher) {
59  Console.WriteLine("Draw {0:s} in Square", grapher.getText());
60 }
61}
62
63class main {
64 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/**//*
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename  : DP_StrategyPattern3_polymorphism_this_interface.cs
5Compiler  : Visual Studio 2005 / C# 2.0
6Description : Demo how to use Strategy Pattern with this by interface
7Release   : 04/07/2007 1.0
8*/
9using System;
10
11interface IGrapher {
12 string getText();
13}
14
15interface IShape {
16 void draw(IGrapher grapher);
17}
18
19class Grapher : IGrapher {
20 private IShape shape;
21
22 private string text;
23
24 public Grapher() { }
25 public Grapher(IShape shape) : this(shape, "Hello Shape!!") { }
26 public Grapher(IShape shape, string text) {
27  this.shape = shape;
28  this.text = text;
29 }
30
31 public void drawShape() {
32  this.shape.draw(this);
33 }
34
35 public void setShape(IShape shape, string text) {
36  this.text = text;
37  this.shape = shape;
38 }
39
40 public string getText() {
41  return this.text;
42 }
43}
44
45class Triangle : IShape {
46 public void draw(IGrapher grapher) {
47  Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
48 }
49}
50
51class Circle : IShape {
52 public void draw(IGrapher grapher) {
53  Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
54 }
55}
56
57class Square : IShape {
58 public void draw(IGrapher grapher) {
59  Console.WriteLine("Draw {0:s} in Square", grapher.getText());
60 }
61}
62
63class main {
64 public static void Main() {
65  Grapher theGrapher = new Grapher(new Square());
66  theGrapher.drawShape();
67
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#泛型看成『更強的多型』,很多原本多型設計,可以考慮用泛型讓設計應用更廣。

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