两个相同的功能(一个使用模板模式,另一个不使用)

时间:2014-12-21 20:32:25

标签: c++ templates design-patterns

#include <iostream>

void doSomething (int x) {std::cout << "Do something with " << x << std::endl;}

struct Base {
    virtual int foo() const {return 5;}
    virtual int goo() const {return 6;}
    virtual int hoo() const {return 7;}
    void noTemplatePattern() const {
        // Code A
        if (Base::foo() < 6) {
            // Code B
        }
        doSomething (Base::goo());
        // Code C
        if (Base::hoo() > 10) {
            // Code D
        }
    }
    void templatePattern() const {
        // Code A
        if (foo() < 6) {
            // Code B
        }
        doSomething (goo());
        // Code C
        if (hoo() > 10) {
            // Code D
        }
    }
};

struct Derived : Base {
    virtual int foo() const override {return 12;}
    virtual int goo() const override {return 13;}
    virtual int hoo() const override {return 14;}
};

int main() {
    Derived d;
    d.noTemplatePattern();
    d.templatePattern();
}

除了为每个代码创建帮助函数之外,如何最好避免重复代码A,代码B,代码C,代码D等中包含的代码?有更通用的方式吗?除了一个使用模板模式,我有相同的功能,而另一个没有。虚函数之间的代码体是相同的。如果我为每个相同的部分定义一个辅助函数,它会变得非常混乱,并且它们也会有太多。

如果您需要更多说明,请参阅我的生产代码片段,说明这一点。 SpellCaster源自LivingBeingLivingBeing::cannotAttackLongRange(int)覆盖SpellCaster::cannotAttackLongRange(int)

inline std::set<LivingBeingProxy*> LivingBeing::unattackableTargets() const {
    std::set<LivingBeingProxy*> nonTargets;
    if (isCharmed()) {
        for (auto it = std::next(getStatesList(CHARM_SPELL).begin(), 1);  it != getStatesList(CHARM_SPELL).end();  ++it)
            nonTargets.emplace (std::dynamic_pointer_cast<CharmedStateBase>(*it)->getCharmer());
    }
    for (LivingBeingProxy* x : getLocation()->allBeingsAlive()) {
        if ( (x->heightAboveGround() > damageInflictor(0)->getReach()) && !canFly()
    && LivingBeing::cannotAttackLongRange(distanceBetween(this, x->getActual()))) //*** virtual method here!
            {nonTargets.emplace(x);  continue;}
        if ( (x->heightAboveGround()) < 0 && (x->getTerrain() == InWater) && !canSwim() )
            {nonTargets.emplace(x);  continue;}
    }
    // ...
    return nonTargets;
}

inline std::set<LivingBeingProxy*> LivingBeing::unattackableTargetsIncludingBySpells() const {
    std::set<LivingBeingProxy*> nonTargets;
    if (isCharmed()) {
        for (auto it = std::next(getStatesList(CHARM_SPELL).begin(), 1);  it != getStatesList(CHARM_SPELL).end();  ++it)
            nonTargets.emplace (std::dynamic_pointer_cast<CharmedStateBase>(*it)->getCharmer());
    }
    for (LivingBeingProxy* x : getLocation()->allBeingsAlive()) {
        if ( (x->heightAboveGround() > damageInflictor(0)->getReach()) && !canFly()
    && cannotAttackLongRange (distanceBetween(this, x->getActual()))) //*** virtual method here!
            {nonTargets.emplace(x);  continue;}
        if ( (x->heightAboveGround()) < 0 && (x->getTerrain() == InWater) && !canSwim() )
            {nonTargets.emplace(x);  continue;}
    }
    // ...
    return nonTargets;
}

LivingBeing::unattackableTargets()计算所有不能被普通武器攻击的敌人,而LivingBeing::unattackableTargetsIncludingBySpells()计算所有不能被普通武器和法术攻击的敌人。一个SpellCaster想要在使用普通武器进行攻击时调用第一个,并且在使用咒语攻击时想要调用第二个。

6 个答案:

答案 0 :(得分:1)

如果templatePattern / noTemplatePattern冗长且复杂且boogoohoo很简单,您可以执行以下操作:

struct Base {
    virtual int foo(bool = false) const {return 5;}
    virtual int goo(bool = false) const {return 6;}
    virtual int hoo(bool = false) const {return 7;}
    void Pattern(bool base) const {
        // Code A
        if (foo(base) < 6) {
            // Code B
        }
        doSomething (goo(base));
        // Code C
        if (hoo(base) > 10) {
            // Code D
        }
    }
};

struct Derived : Base {
    int foo(bool base = false) const override {return base ? Base::foo() : 12;}
    int goo(bool base = false) const override {return base ? Base::goo() : 13;}
    int hoo(bool base = false) const override {return base ? Base::hoo() : 14;}
};

int main() {
    Derived d;
    d.Pattern(true);  // d.noTemplatePattern();
    d.Pattern(false); // d.templatePattern();
}

不完全优雅,但可能在特定情况下有效。

注意:如果您使用override关键字,则无需重复virtual个关键字。

答案 1 :(得分:1)

使用模板和CRTP,如果合适,您可以执行以下操作:

template <typename T, typename D>
void helper(const D& base)
 {
    // Code A
    if (base.T::foo() < 6) {
        // Code B
    }
    doSomething (base.T::goo());
    // Code C
    if (base.T::hoo() > 10) {
        // Code D
    }
}

struct Base {
    virtual ~Base() = default;
    virtual int foo() const {return 5;}
    virtual int goo() const {return 6;}
    virtual int hoo() const {return 7;}

    void noTemplatePattern() const
    {
        // use Base::foo, Base::goo and Base::hoo
        helper<Base>(*this);
    }
#if 0
    virtual void templatePattern() const = 0;
#endif
};

template <typename Derived>
struct BaseImpl : Base {
    template <typename Derived>
    void BaseImpl<Derived>::templatePattern() const {
        // use Derived::foo, Derived::goo and Derived::hoo
        helper<Derived>(static_cast<const Derived&>(*this));
    }
};

Live example

答案 2 :(得分:1)

使用标签调度的一种解决方案(但需要更多代码foogoohoo

struct Base {
    virtual int foo() const {return foo(std::false_type());}
    virtual int goo() const {return goo(std::false_type());}
    virtual int hoo() const {return hoo(std::false_type());}

    void noTemplatePattern() const { doIt (std::false_type()); }
    void templatePattern() const { doIt (std::true_type()); }

private:
    template <typename T>
    void doIt (T t) const {
        // Code A
        if (foo(t) < 6) {
            // Code B
        }
        doSomething (goo(t));
        // Code C
        if (hoo(t) > 10) {
            // Code D
        }
    }
    // tag dispatching between virtual call and Base::call
    int foo(std::false_type) const {return 5;}
    int goo(std::false_type) const {return 6;}
    int hoo(std::false_type) const {return 7;}
    int foo(std::true_type) const {return foo();}
    int goo(std::true_type) const {return goo();}
    int hoo(std::true_type) const {return hoo();}
};

Live example

答案 3 :(得分:0)

好的,这是我想到的一个解决方案,但是虽然它有效但我不知道它是否被认为是好的(需要一些意见)。但至少避免了所有重复的代码,因此对这些部分的任何更改只需要进行一次:

#include <iostream>

void doSomething (int x) {std::cout << "Do something with " << x << std::endl;}

struct Base {
    virtual int foo() const {return fooBase();}
    virtual int goo() const {return gooBase();}
    virtual int hoo() const {return hooBase();}
    virtual void voidFunction() const {voidFunctionBase();}

    void noTemplatePattern() const {
        doIt (&Base::fooBase, &Base::gooBase, &Base::hooBase, &Base::voidFunctionBase);
    }
    void templatePattern() const {
        doIt (&Base::foo, &Base::goo, &Base::hoo, &Base::voidFunction);
    }

private:
    void doIt (int(Base::*a)()const, int(Base::*b)()const, int(Base::*c)()const,
        void(Base::*d)()const) const {
        // Code A
        if ((this->*a)() < 6) {
            // Code B
        }
        doSomething((this->*b)());
        // Code C
        if ((this->*c)() > 10) {
            // Code D
        }
        (this->*d)();
        // Code E
    }
    int fooBase() const {return 5;}
    int gooBase() const {return 6;}
    int hooBase() const {return 7;}
    void voidFunctionBase() const {std::cout << "Base::voidFunction() called.\n";}
};

struct Derived : Base {
    virtual int foo() const override {return 12;}
    virtual int goo() const override {return 13;}
    virtual int hoo() const override {return 14;}
    virtual void voidFunction() const override {std::cout << "Derived::voidFunction() called.\n";}
};

int main() {
    Derived d;
    d.noTemplatePattern();
    d.templatePattern();
}

输出:

Do something with 6
Base::voidFunction() called.

Do something with 13
Derived::voidFunction() called.

评论?一个更好的解决方案?

答案 4 :(得分:0)

如果适用,可能的方法是切割对象:

void noTemplatePattern() const {
    // copy only Base part (slicing). Require that Base is constructible
    Base(*this).templatePattern();
}

Live example

答案 5 :(得分:0)

对此有一个非常简单,毫不含糊的解决方案:只需将两个函数替换为带有参数bool includeSpells的函数。然后你可以在函数中检查这个参数并执行相应的函数调用。功能的其余部分保持不变。