因为其他的事情耽误了看书,现在将看的笔记记录下来。
1 class GameCharacter { 2 public: 3 virtual int healthValue() const; 4 };
1. 藉由Non-Virtual Interface 手法实现 Template Method模式
(1)non-virtual interface(NVI):
令客户通过public non-virtual 成员函数间接调用private virtual函数
(2) virtual 函数的外覆器(wrapper):non-virtual 函数
1 class GameCharacter { 2 public: 3 int healthValue() const // virtual 函数的外覆器(wrapper) 4 { 5 //... 6 /* 7 做些事前工作:锁定互斥器,制造运转日志记录项,验证class约束条件、验证函数先决条件等等 8 */ 9 int retVal = doHealthValue(); 10 //... 11 /* 12 做些事后工作:互斥器解除锁定,验证函数的时候条件、再次验证class约束条件等等。 13 */ 14 return retVal; 15 } 16 private: 17 virtual int doHealthValue() const 18 { 19 //... 20 } 21 };
优点:
先比于直接调用virtual 函数,就没有任何好办法可以处理事前和时候工作。
注意事项:
重新定义virtual 函数,表示某些事“如何”被完成,“调用virtual 函数”则表示它“何时”被完成。
NVI手法允许derived classs重新定义virtual函数,从而赋予他们“如何实现机能”的控制能力,但base class保留诉说“函数何时被调用”的权利。
2.藉由Function Pointers 实现Strategy模式
1 class GameCharacter; // forward declaration 2 3 int defaultHealthCalc(const GameCharacter& gc); /*计算健康指数的缺省算法*/ 4 class GameCharacter { 5 public: 6 typedef int (*HealthCalcFunc) (const GameCharacter&); 7 explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) 8 :healthFunc(hcf) 9 {} 10 11 int healthValue() const 12 { 13 return healthFunc(*this); 14 } 15 private: 16 HealthCalcFunc healthFunc; 17 };
上述模式的解释说明:
(1)同一任务类型的不同实体可以不同的健康计算函数。e.g:
1 class EvilBadGuy:public GameCharacter { 2 public: 3 explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc) 4 :GameCharacter(hcf) 5 { 6 //... 7 } 8 }; 9 10 int loseHealthQuickly(const GameCharacter&); 11 int loseHealthSlowly(const GameCharacter&); 12 13 /* 相同类型的的人物搭配不同的健康计算方式 */ 14 EvilBadGuy ebg1(loseHealthQuickly); 15 EvilBadGuy ebg2(loseHealthSlowly);
(2)某已知人物的健康指数计算函数可在运行期变更。
例如GameCharacter提供一个setHealthCalculator函数,用来替换当前的健康指数计算函数。
(3)一般而言,唯一能够解决“需要以non-member函数访问class的non-public成分”的办法是:弱化class的封装。例如,class可声明那个non-member函数为friends或为其实现的某一部分提供public访问函数。
----> 藉由Function Pointers 实现Strategy模式的缺点是:弱化了class的封装性。
3.藉由tr1::function完成Strategy模式
1 class GameCharacter; 2 int defaultHealthCalc(const GameCharacter& gc); 3 class GameCharacter { 4 public: 5 /* 6 HealthCalcFunc 可以是任何“可调用物”(callable entity),可被调用并接受任何兼容于 7 GameCharacter的事物(参数可被隐式转换为const GameCharacter&), 8 返回任何兼容于int的东西(可被隐式转换为int)。 9 10 类似一个指向函数的泛化指针 11 */ 12 typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc; 13 explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) 14 :healthFunc(hcf) 15 {} 16 17 int healthValue() const 18 { 19 return healthFunc(*this); 20 } 21 private: 22 HealthCalcFunc healthFunc; 23 24 }; 25 26 /*健康计算函数,注意其返回类型为non-int*/ 27 short calcHealth(const GameCharacter&); 28 29 /*为计算健康而设计的函数对象*/ 30 struct HealthCalculator { 31 int operator() (const GameCharacter&) const 32 { 33 //... 34 } 35 }; 36 37 class GameLevel { 38 public: 39 /*成员函数,用以计算健康;注意其non-int返回类型*/ 40 float health(const GameCharacter&) const; 41 //... 42 }; 43 44 class EvilBadGuy:public GameCharacter { 45 public: 46 explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc) 47 :GameCharacter(hcf) 48 { 49 //... 50 } 51 }; 52 53 class EyeCandyCharacter:public GameCharacter { 54 public: 55 explicit EyeCandyCharacter(HealthCalcFunc hcf = defaultHealthCalc) 56 :GameCharacter(hcf) 57 { 58 //... 59 } 60 }; 61 62 /*使用函数计算健康指数*/ 63 EvilBadGuy ebg1(calcHealth); 64 /*使用某个函数对象计算健康指数*/ 65 EyeCandyCharacter eccl(HealthCalculator()); 66 67 /*使用某个成员函数计算健康指数*/ 68 GameLevel currentLevel; 69 EvilBadGuy ebg2(std::tr1::bind(&GameLevel::health, currentLevel, _l));
4.古典的Strategy模式
传统的Strategy做法会将健康计算函数做成一个分离的继承体系中的virtual成员函数
1 class GameCharacter; 2 class HealthCalcFunc { 3 public: 4 //... 5 virtual int calc(const GameCharacter& gc) const 6 { 7 //... 8 } 9 //... 10 }; 11 12 HealthCalcFunc defaultHealthCalc; 13 class GameCharacter { 14 public: 15 explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc) 16 :pHealthCalc(phcf) 17 {} 18 19 int healthValue() const 20 { 21 return pHealthCalc->calc(*this); 22 } 23 24 //... 25 private: 26 HealthCalcFunc *pHealthCalc; 27 };
5.总结
当你为解决问题而寻找某个设计方法时,不妨考虑virtual函数的替代方案。
(1)使用non-vvirtual interface(NVI)手法,它以public non-virtual 成员函数包裹较低
访问性(private或protected)的virtual函数。
(2)将virtual函数替换为“函数指针成员变量”。
(3)以tr1::function 成员变量替换virtual,因而允许使用任何可调用物搭配一个兼容与需求的签名式。
(4)将继承体系内的virtual函数替换为另一个继承体系的virtual函数。
注明:全文文字都是来源《Effective C++》 3th。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。