细读《Effective C++》之八

Chapter 6. Inheritance and Object-Oriented Design

Item 35

条款35:Consider alternatives to virtual functions

当我辛辛苦苦用virtual functions提供了接口和缺省实现之后,Scott却提出考虑其它解法……

需求是什么来着?

GoF认为类行为模式Template Method模式的意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

这 和以前使用virtual functions的最大区别在于,我们在derived classes中重载virtual functions来实现dereived classes所需要的功能即算法。而Template Method模式不去改变算法的框架和步骤,仅仅是在derived class中将特定的步骤重载。

说到底,二者最终都是通过 virtual functions实现算法,这有悖于GoF的第二条原则:优先使用对象组合,而不是类继承。需要注意的另外一个问题是:Template模式获得一种反 向控制结构效果,这也是面向对象系统的分析和设计中一个原则DIP(依赖倒置:Dependency Inversion Principles)。其含义就是父类调用子类的操作(高层模块调用低层模块的操作),低层模块实现高层模块声明的接口。这样控制权在父类(高层模 块),低层模块反而要依赖高层模块。

继承的强制性约束关系也让Template模式有不足的地方,我们可以看到对于DeriveClass 类中的实现的原语方法Primitive1(),是不能被别的类复用。假设我们要创建一个BaseClass的变体 AnotherAbstractClass,并且两者只是通用算法不一样,其原语操作想复用BaseClass的子类的实现。但是这是不可能实现的,因为 DeriveClass继承自BaseClass,也就继承了BaseClass的通用算法,AnotherAbstractClass是复用不了 DeriveClass的实现,因为后者不是继承自前者。

这儿引出了另一种避开继承的对象行为模式:Strategy模式,其意图是定义一 系列的算法,把它们一个个封装起来,并且使它们可相互替换,本模式使得算法可独立于使用它的客户而变化。每一个算法被称为一个strategy。 Scott给出了三种Strategy模式的实现:

这儿是两种使用virtual functions & inheritance的对比实现:

1) virtual functions:

class CBase  {
public
:
  
virtual void
 Algorithm();
  ... 
}
;

class CDerived : public CBase 
{
public
:
  
virtual void
 Algorithm();
  ...
}
;

void CDerived::Algorithm()
{
  ...
}

2) Template Method Pattern:

//  having clients call private virtual functions indirectly
//
 through public non-virtual member functions
// is known as the non-virtual interface (NVI) idiom

class CBase  {
public
:
  
void Algorithm();             // wrapper

  ... 
  
private
:
  
virtual void
 DoAlgorithm();
  ... 
}
;

void CBase::Algorithm()
{
//
 do "before" stuff : locking a mutex, making a log entry, verifying
// that class invariants and function preconditions are satisfied, etc

  Primitive1();
  
  DoAlgorithm();
  
//
 do "after" stuff : unlocking a mutex, verifying
// function postconditions, reverifying class invariants, etc

  Primitive2();
}


class CDerived : public CBase  {
  ...
private
:
  
virtual void
 DoAlgorithm();
  ... 
}
;

void CDerived::DoAlgorithm()
{
  ...
}

以下是三种Strategy模式的对比实现:

1) The Strategy Pattern via Function Pointers:

a) Different instances of the same character type can have different health calculation functions.

b) Health calculation functions for a particular character may be changed at runtime. For example, GameCharacter might offer a member function, setHealthCalculator, that allowed replacement of the current health calculation function.

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

// 如果不传递任何函数名称作为实参,将以defaultHealthCalc构造EvilBadGuy对象

EvilBadGuy ebg1(loseHealthQuickly);             // same-type characters
EvilBadGuy ebg2(loseHealthSlowly);              // with different health-related behavior

2) The Strategy Pattern via tr1::function:

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&);          //
 health calculation function;
                                                 
// note non-int return type


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
);

3) The "Classic" Strategy Pattern:

GoF的Strategy模式将接口类(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;
}
;

由于对BOOST和TR1知识的匮乏,除了用TR1实现的Strategy我无法更加深入地体会之外,其它理解和接受起来都并不困难。

Things to Remember

1) Alternatives to virtual functions include the NVI idiom and various forms of the Strategy design pattern. The NVI idiom is itself an example of the Template Method design pattern.

2) A disadvantage of moving functionality from a member function to a function outside the class is that the non-member function lacks access to the class's non-public members.

3) tr1::function objects act like generalized function pointers. Such objects support all callable entities compatible with a given target signature.

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值