复习effective C++ ,今天看到了"virtual 函数以外的其它选择",里面介绍了Strategy 模式的两种实现方式,也介绍了std::function 和 std::bind 函数等,结合这段时间学习的进行一个总结。
首先还是先来回顾书上的内容:
问题引入:
一个游戏需要对其中人们生命值,健康状况进行监控计算,因此需要定义一个专门的函数,但是不同的人物的计算方式是不同的,也就是说这个函数需要不同的实现方式。可以使用多态,这是最基本的方法。
实例:
class GameCharacter {
public:
virtual int healthValue() const;// return character's health rating
... // derived classes may redefine this
};
由于healthValue()没有声明为pure virtual , 这暗示我们将有个计算健康指数的缺省算法。这也可能成为一个弱点,在派生类中考虑重定义该函数的时候考虑不周。
因此,也可以使用“函数指针”作为成员变量,对于不同人物传给这个“函数指针”不同的计算函数。这就是:使用Function Pointer实现Strategy模式。
class GameCharacter; // forward declaration
// function for the default health calculation algorithm
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;
};
//下面是同一类人中的不同实现:
class EvilBadGuy: public GameCharacter {
public:
explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc) : GameCharacter(hcf)
{ ... }
...
};
int loseHealthQuickly(const GameCharacter&);// health calculation
int loseHealthSlowly(const GameCharacter&);// funcs with different behavior
EvilBadGuy ebg1(loseHealthQuickly); //sametype charac-ters with
EvilBadGuy ebg2(loseHealthSlowly); //different health-related behavior
从上面的代码可以看出,这样的代码具有很好的扩展性,虽然都是EvilBadGuy对象,但是通过绑定函数指针的方式可以使得实现方式灵活多变。
上面的:
typedef int (*HealthCalcFunc)(const GameCharacter&);
作用是定义 一个 返回值为 int 类型,以GameCharacter 的引用为参数的的函数的类型,以后用HealthCalcFunc 声明的函数都是这个类型。(记得以前竟然看不懂这种方式)。
也可以使用tr1::function 来完成Strategy模式(注:tr1::fucntion 现在已经是C++11 标准的一部分,存在于std namespace 中)
class GameCharacter; // as before
int defaultHealthCalc(const GameCharacter& gc); // as before
class GameCharacter {
public:
// HealthCalcFunc is any callable entity that can be called with
// anything compatible with a GameCharacter and that returns
// anything compatible with an int; see below for details
typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf){}
int healthValue() const
{ return healthFunc(*this); }
...
private:
HealthCalcFunc healthFunc;
};
实现了上面的类之后,计算“健康状况”可以用下面的方式都行:
short calcHealth(const GameCharacter &);
struct HealthCalculator { // class for health
int operator()(const GameCharacter&) const // calculation function
{ ... } // objects
};
class GameLevel {
public:
float health(const GameCharacter&) const; // health calculation
... // mem function; note
}; // non-int return type
class EvilBadGuy: public GameCharacter { // as before
...
};
class EyeCandyCharacter: public GameCharacter { // another character
... // type; assume same
}; // constructor as EvilBadGuy
EvilBadGuy ebg1(calcHealth); // character using a
// health calculation function
EyeCandyCharacter ecc1(HealthCalculator()); // character using a
// health calculation
// function object
GameLevel currentLevel;
...
EvilBadGuy ebg2( // character using a
std::tr1::bind(&GameLevel::health, // health calculation
currentLevel, // member function;
_1) // see below for details
);
注:上面的bind函数,有两个参数第一个是currentLevel 实际上是this指针,因为我们知道类的成员函数其实都有一个隐藏的参数 this 指针,在使用bind函数的时候需要指明该参数;后面的_1 叫做占位符,我们实际传进来的参数只有一个就用这个占位符代替。
下面来看一下Strategy 模式的经典实现方法:
这个图告诉你:GameCharacter是某个继承体系的根类,体系中的EvilBadGuy和EyeCandyCharacter都是derived classes;HealthCalcFunc是另一个继承体系的根类,体系中的SlowHealthLoser和FastHealthLoser都是derived class,每个GameCharacter对象内含一个指针,指向一个来自HealthCalcFunc继承体系的对象。
class GameCharacter; // forward declaration
class HealthCalcFunc {
public:
...
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;
};
当你为解决问题而寻找某个设计方法时,不放考虑virtual函数的替代方案。一下有几个替代方案:
-
使用non-virtual interface (NVI)手法,它以public non-virtual成员函数包裹较低访问性(private或protected)的virtual函数
-
将virtual函数替换为“函数指针成员变量”,这是Strategy设计模式的一部分表现形式
-
以“tr1 :: function”成员变量替换virtual函数,因而允许使用任何可调用实体搭配一个兼容于需求的签名式。
-
将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。这是Strategy设计模式的传统实现手法。
附:UML关系图的含义:
实际上,有两种不同的 has-a 关系。一个对象可以拥有另一个对象,其中被包含的对象是包含对象的一部分——或者不是。下图 中,我表示出 Airport拥 Aircraft。Aircraft 并不是 Airport 的一部分,但仍然可以说 Airport 拥有 Aircraft,这种关系称为聚集。
另一种 has-a 关系是包含,被包含对象是包含对象的一部分,这种关系也称为组合。
下图显示了 Car(轿车)拥有 Tire(轮胎),后者是它的一部分(也就是说,Car 由 Tire 和其他东西组成),这种 has-a 关系,称为组合关系(composition),用实心菱形表示。此图上还显示了 Car 使用了 GasStation(加油站)类,这种使用关系用带箭头的虚线表示,也称依赖关系(dependencyrelationship)。
组合和聚集都有“一个对象包含一个或多个对象”的意思,但是,组合意味着“被包含对象是包含对象的一部分”,而聚集意味着被包含对象更像是一个集合。我们可以认为组合是一种非共享的关联,被包含对象的生存周期由包含对象控制。适当使用构造函数和析构函数在这里有助于对象的创建和销毁过程。
原文链接: https://www.cnblogs.com/zhuyp1015/archive/2012/08/12/2634316.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/59027
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!