(原創) 我的Design Pattern之旅[3]:使用template改進Strategy Pattern (OO) (Design Pattern) (C/C++) (template)
在strategy pattern中,為了讓各strategy能方便存取原來物件的所有public member function,我們常用*this將整個物件傳給各strategy,這樣的設計並沒有什麼不好,但各strategy和原物件過於tight coupling,導致各strategy難以再和其他各物件搭配,本文使用template解決此問題。
在(原創) 我的Design Pattern之旅:Strategy Pattern (初級) (Design Pattern) (C++) (OO C++) (Template C++)中,我們使用了strategy pattern讓Grapher能畫Triangle、Circle和Square
因為需求再次改變,:D,我們希望Grapher能將文字印在各Shape中,執行結果如下
Draw Hello C++!! in Circle
為了達到此需求,我們可以將IShape interface改成
public:
virtual void draw(const char *text) const = 0;
};
但若將來需求再改變,希望畫的不是文字,而是一張圖片,那interface又必須再變動,為了一勞永逸,我們會將整個物件傳給各strategy,IShape interface改成如下
public:
virtual void draw(Grapher &grapher) const = 0;
};
Grapher::drawShpae()將*this傳給各strategy
this->shape->draw(*this);
}
完整的程式碼如下
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : DP_StrategyPattern3_polymorphism_this.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use Strategy Pattern with *this
7Release : 04/04/2007 1.0
8*/
9#include <iostream>
10#include <string>
11
12using namespace std;
13
14class Grapher;
15
16class IShape {
17public:
18 virtual void draw(Grapher &grapher) const = 0;
19};
20
21class Grapher {
22private:
23 IShape *shape;
24 string text;
25
26public:
27 Grapher() {}
28 Grapher(IShape *shape, const char *text = "Hello Shape!!") : shape(shape), text(string(text)) {}
29
30public:
31 void drawShape() {
32 this->shape->draw(*this);
33 }
34
35 void setShape(IShape *shape, const char* text = "Hello Shape!!") {
36 this->text = text;
37 this->shape = shape;
38 }
39
40 string getText() const {
41 return this->text;
42 }
43};
44
45
46class Triangle : public IShape {
47public:
48 void draw(Grapher &grapher) const {
49 cout << "Draw " << grapher.getText() << " in Triangle" << endl;
50 }
51};
52
53class Circle : public IShape {
54public:
55 void draw(Grapher &grapher) const {
56 cout << "Draw " << grapher.getText() << " in Circle" << endl;
57 }
58};
59
60class Square : public IShape {
61public:
62 void draw(Grapher &grapher) const {
63 cout << "Draw " << grapher.getText() << " in Square" << endl;
64 }
65};
66
67
68int main() {
69 Grapher theGrapher(&Square());
70 theGrapher.drawShape();
71
72 theGrapher.setShape(&Circle(), "Hello C++!!");
73 theGrapher.drawShape();
74}
執行結果
Draw Hello C++!! in Circle
這樣的設計看似完美,但IShape和Grapher相依程度太高,若將來有個Painter class,和Grapher完全不同,沒有任何繼承或多型的關係,但想重複使用IShape interface的strategy,這樣的設計就無法讓Painter使用了。若我們能讓IShape interface的draw()不再只限定Grapher型別,改用template,就能讓將來所有型別都能使用IShape interface。
class IShape {
public:
virtual void draw(T &grapher) const = 0;
};
我們用泛型T取代了Grapher,任何型別都可傳進IShape::draw()。
完整程式碼如下
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : DP_StrategyPattern3_template_this.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use Strategy Pattern with *this by template
7Release : 04/03/2007 1.0
8*/
9#include <iostream>
10#include <string>
11
12using namespace std;
13
14class Grapher;
15
16template <typename T>
17class IShape {
18public:
19 virtual void draw(T &grapher) const = 0;
20};
21
22class Grapher {
23private:
24 IShape<Grapher> *shape;
25 string text;
26
27public:
28 Grapher() {}
29 Grapher(IShape<Grapher> *shape, const char* text = "Hello Shape!!") : shape(shape), text(string(text)) {}
30
31public:
32 void drawShape() {
33 this->shape->draw(*this);
34 }
35
36 void setShape(IShape<Grapher> *shape, const char* text = "Hello Shape!!") {
37 this->text = text;
38 this->shape = shape;
39 }
40
41 string getText() const {
42 return this->text;
43 }
44};
45
46template <typename T>
47class Triangle : public IShape<T> {
48public:
49 void draw(T &grapher) const {
50 cout << "Draw " << grapher.getText() << " in Triangle" << endl;
51 }
52};
53
54template <typename T>
55class Circle : public IShape<T> {
56public:
57 void draw(T &grapher) const {
58 cout << "Draw " << grapher.getText() << " in Circle" << endl;
59 }
60};
61
62template <typename T>
63class Square : public IShape<T> {
64public:
65 void draw(T &grapher) const {
66 cout << "Draw " << grapher.getText() << " in Square" << endl;
67 }
68};
69
70
71int main() {
72 Grapher theGrapher(&Square<Grapher>());
73 theGrapher.drawShape();
74
75 theGrapher.setShape(&Circle<Grapher>(), "Hello C++!!");
76 theGrapher.drawShape();
77}
執行結果
Draw Hello C++!! in Circle
Conclusion
泛型的應用相當廣,在此範例僅僅是泛型的小小應用,在OOP世界使用strategy pattern,常將*this傳給strategy,若搭配GP可讓strategy pattern的resuse程度更高。
See Also
(原創) 我的Design Pattern之旅[1]:Strategy Pattern (初級) (Design Pattern) (C++) (OO C++) (Template C++)
(原創) 我的Design Pattern之旅[4]:使用Generic改進Strategy Pattern (高級) (Design Pattern) (C#) (Generic)