zoukankan      html  css  js  c++  java
  • Effective C++ Item 35 Consider alternatives to virtual functions

    考虑你正在为游戏人物设计一个继承体系, 人物有一个函数叫做 healthValue, 他会返回一个整数, 表示人物的健康程度. 由于不同的人物拥有不同的方式计算他们的健康指数, 将 healthValue 声明成一个 virtual 似乎是再合适不过的了

    class GameCharacter {
    public:
        virtual int healthValue() const;
        ...
    };

    那么, 它还有没有其他的实现形式呢?

    1. 藉由 Non-Virtual Interface 手法实现 Template Method 模式

    class GameCharacter {
    public:
        virtual int healthValue() const {
            ...
            int retVal = doHealthValue();
            ...
            return retVal;
        }
        
    private:
        virtual int doHealthValue() {
            ...
        }
    };

    NVI 手法的一个隐身优点是上述代码注释"做一些事前工作", "做一些事后工作". 这意味着 wrapper 确保得以在一个 virtual 函数被调用之前设置好适合场景, 并在调用之后清理场景.

    另外, doHealthValue 没必要是 private

    2. 藉由 Function Pointer 实现 Strategy 模式

    具体实现是

    class GameCharacter {
    public:
        typedef int (*HealthCalcFunc)(const GameCharacter&);
    
        explicit GameCharacter(HealthCalcFunc hcf) {
            healthFunc = hcf;
        }
    
        int healthValue() const {
            return healthFunc(*this);
        }
        ...
    private:
        HealthCalcFunc healthFunc;
    };

    从上面的代码可以看出 内部策略函数的框架是 int (*pf) (const GameCharacter)

    函数唯一的参数是 GameCharacter, 这意味着血量的计算通过 GC(GameCharacter) 的 public 借口即可完成计算, 不需要设计其 private 的部分. 而假如不得不引用 private 部分, 那么 "弱化封装" 是唯一的解法.

    3. 藉由 tr1::function 完成 Strategy 模式

    从 Java 转过来的程序员可能对函数指针这个东西会产生莫名的恐惧, 额, 我就是其中之一, 能不能将策略模式的核心转换成类似对象的东西呢? 答案是肯定的, 这也构成了第(3), (4) 小节的题目

    class GameCharacter {
    public:
        typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
        ... // 其他部分完全与函数指针方法兼容
    }

    tr1::function 充当了一个泛化的函数, 其依然能够接受函数指针, 能够接受函数对象, 甚至使用 bind

    short calcHealth(const GameCharacter&);
    struct HealtheCalculator {
        int operator()(const GameCharacter&) const {
            ...
        }
    };
    
    class GameLevel {
    public:
        float health(const GameCharacter&) const;
        ...
    };
    
    class EvilBadGuy:public GameCharacter {...};
    class EyeCandyCharater: public GameCharacter{...};
    
    EvilBadGuy ebg1(calcHealth); // 接受一个函数
    EyeCandyCharater ecc1(HealtheCalculator());// 接受函数对象
    GameLevel currentLevel;
    EvilBadGuy(    // 接受某一个成员函数
        std::tr1::bind(&GameLevel::health, currentLevel, _1)
        );

    GameLevel::health 函数有两个参数, bind 则指定 currentLevel 作为调用 health 的对象

    4. 古典 Strategy 模式

    正如上面 策略模式的图解一样, 古典策略模式会将健康计算函数成为一个分离继承体系中的 virtual 函数

  • 相关阅读:
    函数的扩展
    数组的扩展
    event(1)
    面试
    iframes
    浏览器CSS兼容
    BFC
    简单的一个轮播效果
    asp.net identity 2.2.0 在WebForm下的角色启用和基本使用(二)
    我的web框架设计
  • 原文地址:https://www.cnblogs.com/xinsheng/p/3575080.html
Copyright © 2011-2022 走看看