(原創) 我的Design Pattern之旅[8]:如何使用泛型打造動態改變的Strategy Pattern? (OO) (Design Pattern) (C/C++) (template) (boost)
Abstract
一般若用泛型實現Strategy Pattern,缺點是無法動態改變strategy,本文將介紹可以動態改變的泛型Strategy Pattern。
Introduction
正統若用OO實現Strategy Pattern,會用到interface和virtual。
OO Virtual
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : oo_virtual_strategy.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++ / boost
Description : Traditional Strategy Pattern by virtual
Release : 09/13/2007 1.0
*/
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() const = 0;
};
class Triangle : public Shape {
public:
virtual void draw() const {
cout << "Draw Triangle" << endl;
}
};
class Circle : public Shape {
public:
virtual void draw() const {
cout << "Draw Circle" << endl;
}
};
class Grapher {
public:
Grapher() : _drawStrategy(0) {}
Grapher(Shape& drawStrategy) : _drawStrategy(&drawStrategy) {}
void draw() {
_drawStrategy->draw();
}
void setDrawStrategy(Shape& drawStrategy) {
_drawStrategy = &drawStrategy;
}
private:
Shape* _drawStrategy;
};
int main() {
Triangle triangle;
Grapher grapher(triangle);
grapher.draw();
Circle circle;
grapher.setDrawStrategy(circle);
grapher.draw();
}
這種做法有著OO傳統的優點,可以動態改變strategy。
泛型
若用泛型實現Strategy Pattern,一般我們會使用class template。
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : template_strategy.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++ / boost
Description : Traditional Strategy Pattern by template
Release : 09/13/2007 1.0
*/
#include <iostream>
using namespace std;
class Triangle {
public:
void draw() const {
cout << "Draw Triangle" << endl;
}
};
class Circle {
public:
void draw() const {
cout << "Draw Circle" << endl;
}
};
template<typename Strategy>
class Grapher {
public:
void draw() {
_drawStrategy.draw();
}
private:
Strategy _drawStrategy;
};
int main() {
Grapher<Triangle> grapher;
grapher.draw();
Grapher<Circle> grapher2;
grapher2.draw();
}
由於使用class template,已經在compile-time定了下來,所以無法在由run-time改變strategy。
但別忘了template另外一個技術:function template,配合boost::any則可實現run-time改變strategy。
泛型 by boost::any
(C) OOMusou 2007 http://oomusou.cnblogs.com
Filename : template_strategy_boost_any.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++ / boost
Description : Demo how to use boost:any to implement Strategy Pattern
Release : 03/16/2007 1.0
*/
#include <iostream>
#include "boost/any.hpp"
using namespace std;
using namespace boost;
class Triangle {
public:
void draw() const {
cout << "Draw Triangle" << endl;
}
};
class Circle {
public:
void draw() const {
cout << "Draw Circle" << endl;
}
};
class Grapher {
public:
Grapher() : _drawStrategy(0) {}
template<typename Strategy>
Grapher(Strategy& drawStrategy) : _drawStrategy(drawStrategy) {}
template<typename Strategy>
void draw() {
any_cast<Strategy>(&_drawStrategy)->draw();
}
template<typename Strategy>
void setDrawStrategy(Strategy& drawStrategy) {
_drawStrategy = drawStrategy;
}
private:
any _drawStrategy;
};
int main() {
Triangle triangle;
Grapher grapher(triangle);
grapher.draw<Triangle>();
Circle circle;
grapher.setDrawStrategy(circle);
grapher.draw<Circle>();
}
Grapher由class template改成function template,關鍵在於45行
any _drawStrategy;
使用了boost::any,簡單的說,他允許任何型別,類似C#的Object,所以藉此能容納各種strategy。
37行
void draw() {
any_cast<Strategy>(&_drawStrategy)->draw();
}
若要由boost::any使用某型別的member function,必須先用any_cast轉型成才可使用。
Conclusion
若改用function template和boost::any,很多Design Pattern皆可使用泛型實現,同時又可在run-time改變,一掃泛型無法在run-time改變的缺點。
Reference
Bjorn Karlsson, Beyond C++ Standard Library : An Introduction to Boost. 2005