在必要时违反“合成继承”是否可以?

时间:2019-04-13 23:27:23

标签: c++ inheritance composition

我有一组课程:


// This is #included from another header file
// I don't want to inherit this, because it ain't my code
class DrawableObject;

class Animal {
    DrawableObject obj;
    // Does not define run() or swim()
};

class Cat : public Animal {
    void run() { /* Calls obj.setPosition() and other stuff */ }
};

class Dog : public Animal {
    void run() { /* Calls obj.setPosition() and other stuff */ }
    void swim() { /* Calls obj.setPosition() and other stuff */ }
};

class Dolphin : public Animal {
    void swim() { /* Calls obj.setPosition() and other stuff */ }
};

在这里,Dog::run()Cat::run()恰好使用完全相同的代码,而Dog::swim()Dolphin::swim()也使用相同的代码。我不想在各处复制粘贴代码,而是要重用它。明智的解决方案似乎是在基类(Animal)和具体类(Cat/Dog/Dolphin)之间添加中间子类:

       /-> RunnableAnimal --> Cat
       |                  \
Animal-|                  |-> Dog
       |                  /
       \-> SwimmableAnimal -> Dolphin

问题是:我违背“继承构成”规则吗?如果是这样,这是否完美,还是在实现代码重用时遵守CoI的方法?

注意:我不需要,当我使用run()时,我总是使用具体的(Cat/Dog/Sloth)类来调用它,而不是Animal基类的成员。

1 个答案:

答案 0 :(得分:4)

更好的继承模式:

          /–––––––––– Cat
         /          /
        /    Runner
       /            \
Animal –––––––––––––– Dog
       \            /
        \    Swimmer
         \          \
          \–––––––––– Dolphin

您避免在方法中引入菱形图案。

代替继承,您可以在需要的动物内聚合一个Runner / Swimmer实例,并使动物的功能仅委派给成员。

关于您的模型的一个小问题:它并不能真正反映现实,实际上,尽管不喜欢水,但是猫还是很好的游泳者...

编辑:由于RunnerSwimmer需要访问Animal的成员:您可以通过curiously recurring template pattern提供此权限;在下面添加了一个演示:

class Animal
{
protected:
    int n = 7;
};

template <typename T>
class Swimmer
{
public:
    void swim()
    {
        std::cout << static_cast<T*>(this)->n << std::endl;
    }
};

class Dolphin : public Animal, public Swimmer<Dolphin>
{
    friend class Swimmer; // n is protected!
    // (alternatively, Swimmer might already be a friend of Animal)
};


int main(int argc, char* argv[])
{
    Dolphin d;
    d.swim();

    return 0;
}