1.虚函数的替代方案有:
- (1)使用non-virtual interface(NVI)方法,它是Template Method设计模式的一种特殊形式。使客户通过仅有的非虚函数间接调用私有的虚函数,该公有的非虚函数称为私有虚函数的“外覆器”(wrapper)。公有的非虚函数可以在调用虚函数前后做一些其他工作(如互斥器的锁定与解锁,验证约束条件等)。
- (2)将虚函数替换为“函数指针成员变量”,它是Strategy设计模式的一种分解表现形式。
- (3)以tr1::function成员变量替换虚函数,从而允许使用任何可调用物搭配一个兼容于需求的签名式(这句话表达太晦涩了,很难理解,例子见下)。它也是Strategy设计模式的某种形式。
- (4)将继承体系内的虚函数替换为另一个继承体系内的虚函数,这是Strategy设计模式的传统实现手法。
2.考虑案例:游戏中的人物伤害值计算问题。
(1)一般来讲可以使用虚函数的方法:
class GameCharacter{
public:
virtual int healthValue() const; //返回人物的体力值,派生类可以做出修改
...
};
(2)使用NVI方法,在基类中使用一个公有的普通函数调用私有的虚函数。
class GameCharacter{
public:
int healthValue() const { //派生类不能重新定义它
... //做一些事前工作
int retVal = doHealthValue(); //调用私有函数进行计算
... //做一些事后工作
return retVal;
}
...
private:
virtual int doHealthValue() const{ //派生类可以重新定义
... //提供缺省算法
}
};
(3)使用函数指针。
class GameCharacter; //前置声明
//以下函数是计算体力值的缺省算法
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter{
public:
typedef int (*HealthCalcFunc)(const GameCharacter&);
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf)
{ }
int healthValue() const{
return healthFunc(*this);
}
...
private:
HealthCalcFunc healthFunc;
};
这种方法能够:
- 〈1〉通过定义不同的体力值计算方法,同种类型的人物通过调用不同的函数可以实现不同的计算方法。
- 〈2〉人物的体力计算方法可以在运行期间变更(相当于为GameCharacter的私有变量重新赋值)。
这种方法(包括以后的两种方法)可能会使用类外的函数,从而降低封装性。
(4)使用tr1::function完成Strategy模式。
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter{
public:
//HealthCalcFunc可以是任何“可调用物”,可被调用并接受任何兼容于GameCharacter之物,返回任何兼容于int的东西,详下:
typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
//这种定义表示HealthCalcFunc作为一种类型,接受GameCharacter类型的引用,并返回整数值,其中支持隐式类型转换
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf)
{}
int healthValue() const{
return healthFunc(*this);
}
...
private:
HealthCalcFunc healthFunc;
};
在使用这个方法时P175介绍了三种调用方式,即使用三种方式初始化GameCharacter的派生类:一个具体的函数,一个函数对象,以及一个像std::tr1::bind(&GameLevel::health, currentLevel, _1)这样用一个对象的成员函数。
EvilBadGuy ebg1(calcHealth); //使用某个函数
EyeCandyCharacter ecc1(HeathCalculator()); //使用某个函数对象(包含一个函数的结构体)
GameLevel currentLevel;
EvilBadGuy ebg2(std::tr1::bind(&GameLevel::health, currentLevel, _1));
(5)使用典型的Strategy。类图如下:
在GameCharacter类中的具体定义为:
class GameCharacter{
public:
explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc) : pHealthCalc(phcf)
{}
int healthValue() const{
return pHealthCalc->calc(*this);
}
...
private:
HealthCalcFunc* pHealthCalc;
};
3.Strategy模式:在《设计模式》中,定义为:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。Strategy模式使算法可独立于使用它的客户而变化。
Strategy模式以下面几条原则为基础:
- 1) 对象都具有职责;
- 2) 这些职责不同的具体实现是通过多态的使用完成的;
- 3) 概念上相同的算法具有多个不同的实现,需要进行管理。