我们考虑下面这个问题:假设我们在开发一款游戏,游戏中有不同的角色,每个角色有自己的生命值的初始值,生命值的计算方法等等。你会怎么设计这个类呢?我们很自然的就会想到:
class GameCharacter
{
public:
virtual int healthValue()const;
};
就是说基类里定义了一个计算生命值的函数,派生类通过重新定义这个函数来完成不同类型的角色的生命值的计算。
假如生命值的计算分为如下几步:
1.获得生命值。
2.通过一个函数计算生命值。
3.将生命值返回。
那么每个派生类的healthValue函数都需要完成这几步,我们能不能重构这个代码呢?先看一下重构的结果:
class GameCharacter
{
public:
int healthValue()
{
int val = getInitialVal();
val = calcVal(val);
return val;
}
protected:
virtual int getInitialVal() = 0;
virtual int calcVal(int ) = 0;
};
class Soldier:public GameCharacter
{
private:
int getInitialVal();
int calcVal(int );
};
class Patient:public GameCharacter
{
private:
int getInitialVal();
int calcVal(int );
};
//战士的初始生命值较高
int Soldier::getInitialVal()
{
return 50;
}
//但是生命值会减半
int Soldier::calcVal(int val)
{
return val = val/2;
}
//病人的声明值较低
int Patient::getInitialVal()
{
return 10;
}
//但是生命值会翻倍
int Patient::calcVal(int val)
{
return val = val*2;
}
这种做法乍看起来不是很习惯,我们要对它的思路仔细说说:
1.它在基类中声明了2个不会被继承的虚函数getInitialVal()和calcVal(int val)。但是在基类的可以被继承的(且不希望被修改的)healthValue函数中调用了。
2.在派生类中定义了healthValue所要调用的函数。
他这样做的好处是:在基类中,限定了先做什么,后做什么。但是具体怎样做,把权力移交给了派生类。
这种思路,称为模板方法模式,它的定义为:定义一个操作中的算法的骨架,而将一些方法实现延迟到子类。模板方法使得子类可以不改变一个算法的结构即可以重定义该算法的某些特定步骤。
但是这样做其实并不灵活,假如我希望同一个类型的不同对象有不同的计算生命值的方法,就麻烦了。换个角度思考,人物健康指数的计算,其实,不一定与人物的特定类型有关,对于同一个类型,也可以有不同的计算方法。由此我们想到,不能让每个类型的声明计算与一个函数相关,而对于不同的对象,可以调用不同的函数来完成这件事。依照这个思路,我们可以这么写:
//人物健康指数的计算与人物类型