(原創) 我的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
oo_strategy.gif

/* 
(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。
template_strategy.gif

/* 
(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
template_strategy_any.gif

/* 
(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行

private:
  any _drawStrategy; 


使用了boost::any,簡單的說,他允許任何型別,類似C#的Object,所以藉此能容納各種strategy。

37行

template<typename Strategy>
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

posted on 2007-09-14 01:53  真 OO无双  阅读(3001)  评论(4编辑  收藏  举报

导航