假如你正在写一个视频游戏软件,你想计算不同人物的健康指数,那么平常的方法肯定是这样实现:
class GameCharacter
{
public:
virtual int healthValue() const;
........
};
healthValue被声明为virtual,说明会提供缺省方法。但是从某个角度来讲这里存在弱点。这里考虑一些其他替代方法:
由Non-Virtual Interface手法实现Template Method模式
较好的方法是保留healthValue为public成员函数,但让他为non-virtual,并调用一个private virtual函数:
class GameCharacter
{
public:
int healthValue() const
{
...
int retVal = doHealthValue();
....
return retVal;
}
.....
private:
virtual int doHealthValue() const
{
....
}
};
这样做可以在调用virtual之前做一些事前工作,如锁定互斥器,运转日志记录。在virtual结束后做一些事后工作,如解锁等。以上的手法我们可以叫做NVI手法。
NVI手法在派生类中重新定义private virtual函数,表示某些事如何被完成,在基类中表明何时被调用。
由Function Pointers实现Strategy模式
另外一个更戏剧性的设计主张“人物的健康指数的计算与人物类型无关”
class GameCharacter;
int defaultHealthCacl(const GameCharacter& gc);
class GameCharacter
{
public:
typedef int (*HealthFunc)(const GameCharacter&);
explicit GameCharacter(HealthFunc hf = defaultHealthCacl):healthFunc(hf){.....}
int healthValue() const
{ return healthFunc(*this);}
....
private:
HealthFunc healthFunc;
};
同一人物类型的不同实体可以有不同的健康计算函数
class EvilBadGuy:public GameCharacter
{
public:
explicit EvilBadGuy(HealthFunc hf = defaultHealthCacl):GameCharacter(hf){ ... };
....
};
int loseHealtQ(const GameCharacter&);
int loseHealtS(const GameCharacter&);
EvilBadGuy ebg1(loseHealthQ);
EvilBadGuy ebg2(loseHealthS);
某些人物健康指数计算函数可以在运行期间变更,替代当前的健康指数。
这里的健康指数计算函数不再是类的成员函数的一部分,不再属于继承体系。
如果根据该人物的public接口计算是没有什么问题的,但是要是non-public呢。实际上当我们将class内的某个机能替换到class外部去做都是存在争议的。一般来说唯一解决的方法是弱化class的封装性。如friend函数。
由tr1::function完成strategy模式
在上面我们要求健康计算函数是一个函数,现在我们觉得这种做法过于死板。为什么不是像函数的东西呢,返回值为什么不可以是可以转换为int的类型呢?我们不再使用函数指针,而是用函数对象tr1::function。
tr1::function:
class GameCharacter;
int defaultHealth(const GameCharacter& gc);
class GameCharacter
{
public:
typedef std::tr1::function<int (const GameCharater&)> HealthFunc;
explicit GameCharacter(HealthFunc hf = defaultHealth):healthFunc(hf){ ....... }
int healthValue() const
{ return healthFunc(*this); }
....
private:
HealtFunc healthFunc;
};
上面只是使用了函数对象,其他地方和以前的设计是一样的,但是他的功能是如此的强大:
short call(const GameCharacter&);
struct HealtCall
{
int operator()(const GameCharacter&) const{.......}
};
class GameLevel
{
public:
float health(const GameCharacter&) const;
........
};
class Evil:public GameCharacter
{
....
};
class Eye:public GameCharacter
{.........};
Evi eg1(call);
Eye eye(HealthCall());
GameLevel cur;
...
Evil eg2(std::tr1::bind(&GameLevel::health,cur,_1));
我们可以看到它是如此的强大。
古典的Strategy模式
健康计算函数做成一个分离的继承体系中的virtual成员函数。
class GameCharacter;
class HealthCalcFunc {
...
virtual int calc(const GameCharacter& gc) const
{...}
...
};
HealthCalcFunc defaultHealthCalc;
class GameCharacter {
public:
explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc)
:pHealthCalc(phcf);
{}
int healthValue() const
{
return pHealthCalc->calc(*this);
}
...
private:
HealthCalcFunc* pHealthCalc;
};