条款35:考虑virtual函数以外的其他选择

1、考虑virtual函数以外的其他选择是什么意思?

大多时候,我们会自然而然的想到使用virtual手法来塑模现实中的类。但是,实际上也有别的方案可以替代virtual手法的,即:考虑virtual函数以外的其他选择。下面介绍的便是几种可以替代virtual的方案。

2、第一种方案:通过Non-Virtual Interface (NVI)手法实现Template Method 模式

(1)Non-Virtual Interface 是Template Method 设计模式的一个独特的表现形式。
class GameCharacter {
public:
    //1、派生类不可改变的算法骨架
    int healthValue() const
    {
        ...//做一些事前工作
        int retVal = doHealthValue();//核心点
        ...//做一些事后工作
        return retVal;
    }
private:
	//2、派生类可以改变的算法细节
    virtual int doHealthValue() const //derived class 可以重新定义它。
    {
        ...//缺省计算,计算健康指数
    }
};
(2)这里把non-virtual的函数 healthValue()称为doHealthValue()函数的外覆器。

这个实现方法的优点:可以在核心点前做准备工作,和核心点后做善后工作。

(3)关于对派生类对象中重新定义 private virtual函数的担心
  • 派生类有权利决定实算法的某一步的具体细节
  • 派生类无权决定派生类实现的步骤和时机(这些由基类决定。)
  • 这是合情合理的,因此不必担心。
(4)virtual函数的访问控制
  • NVI手法中,virtual函数可以是private的
  • NVI手法中,virtual函数也可以是protected的。这样的写法使得派生类可以调用其父类中的对应的函数。
  • NVI手法中,virtual函数不可以是public的。

注意:准备工作可以是:锁定互斥器、验证class约束条件、验证函数先决条件等等。善后工作可以是:解除互斥器锁定、验证函数的事后条件、再次验证class约束条件等等。

3、第二种方案:通过Function Pointers完成Strategy模式

(1)具体做法

在游戏角色类中添加一个指针,指向一个计算健康指数的函数。

(2)优点

通过Funtion Pointers完成Strategy与使用virtual函数实现的比较:

  • 同一人物类型的不同实体可以有不同的健康计算函数。
  • 某个已知人物的健康指数极端函数可以在运行期变更。

4、第三种方案:通过tr1::function完成Strategy模式(使用函数对象,而不是使用函数指针)

(1)实现方法:

上面的方法是让游戏角色类拥有一个指向函数的指针。而这个方案是让游戏角色类拥有一个像函数一样的函数对象。

(2)优点:
  • 这种方案比上述方案的效果更好,因为函数对象允许我们做的事更多。可以调用任何兼容函数计算健康指数。

5、第四种方案:传统的Strategy模式

(1)传统的Strategy的做法是:
  • 设计一个游戏角色类的继承体系,其中可以派生多个类。并且在其基类中包含一个指向计算健康指数类的指针。
  • 将计算健康指数设计成一个继承体系,其中可以派生多个类。
(2)优点:
  • 设计模式容易辨认。
  • 增加新的健康指数计算类比较容易,只需要在健康指数继承体系中派生一个新的计算类即可。

6、二、三、四方案存在的问题

(1)实现方案存在的问题

实际上也就是说,健康指数计算函数不再是游戏角色类的成员函数了,这也意味着他们不能直接访问游戏角色类的成员了。

(2)解决上述问题的方法:

弱化游戏角色类的封装,这也是这种替代方案的缺点,这是下面Strategy设计模式都要需要面临的问题。
方案一:游戏角色类可以将该函数声明为友元。
方案二:将游戏角色类的某些成员声明为public。

(3)三种方案的缺点

弱化了封装。

原文地址:https://www.cnblogs.com/lasnitch/p/12764189.html