当A(派生类)“是”B(基类)时,我们使用继承。 A“可以”B或C时我们该怎么办?

时间:2014-06-23 22:37:10

标签: c++ oop inheritance

抱歉这个丑陋的问题,但我不知道如何说出来。我将举例说明我的意思:

人类可以是法师或战士,因此法师和战士可以继承人类。但是,如果Orc也可以兼得并入呢?我们不能说“人类是战士”或“战士是人”。 Orc和Human(或父类,Humanoid)是否继承了所有技能,然后选择使用什么?

我不知道是否应该标记特定语言,因为它是关于oop的一般性问题,但由于不同的语言可以针对同一问题采用不同的方法,我更倾向于从C ++的角度给出答案。

6 个答案:

答案 0 :(得分:8)

改善您的建模

  1. 抽象类种族,具体类人类,兽人等......
  2. 抽象类,具体类Mage,Warrior等......
  3. 例如,在计算法师的统计数据时,请询问种族(非人类或兽人),例如int get_intelligence_bonus()种族中的抽象功能) )。如果可能的话,互动应该在种族之间,而不是具体的对应物。

    使用合成来实际构建你的角色:

    Character::Character( std::unique_ptr<Race> r, std::unique_ptr<Class> c ) 
    {
      // calculate stats, etc...
    }
    

    您可以使用纯OOP进行动态绑定,或者如果您愿意,可以使用模板进行静态时间绑定,但这是一个正交实现问题。无论哪种方式,您都需要使您的类层次结构正确。

    如果您不熟悉C ++,或者只需要在构造函数中使用种族和类,则可以使用const&代替std::unique_ptr<>。但是,如果您认真学习C ++,请阅读所有权语义变量生命周期,以便更好地理解std::unique_ptr<>

    http://en.cppreference.com/w/cpp/memory/unique_ptr

答案 1 :(得分:5)

那是我爆发的时候policy-based design。然后我可以Human<Warrior>Orc<Warrior>Orc<Mage>。 (或者如果它更有意义的话就转过来:Warrior<Human>等等。)但是,如果我知道在编译时发生了什么。

如果直到运行时我才知道,我会使用 Humanoid的课程Race(子类为Orc,{ {1}}等等, Human(子类RPGClassMage等。)

在其他语言中,有protocols之类的东西可以定义对象的接口,因此您不必定义充满抽象虚拟方法等的基类。因此,WarriorRPGClass将是用于分别与RaceMage等类接口的协议。策略只是在编译时解决的协议。

P.S。我不知道你的例子是多么隐喻(或不是)...

答案 2 :(得分:4)

在这个例子中,我会看到用于组合或多重继承。

也就是说,我会有一个法师和战士类以及一个人类和兽人类。如果我想要一个兽人法师,我会从法师和兽人类继承。

也就是说,在实践中,多重继承可能会变得棘手(参见此处:What is the exact problem with multiple inheritance?)。因此,我的实现中的orc mage类可能会有一个orc和mage私有对象,可以执行orc和mage特定的东西。

答案 3 :(得分:3)

存在职业(阶级)和种族。这里不需要'is-a'。

将会有类实例模式使用,但它们可能不会与C ++(或任何其他语言)继承规则完美排列,因此请手动运行,限制它或在用例出现时增强它,并愿意如果你遇到纠结就扔掉它。

答案 4 :(得分:2)

如果我关注,您正在尝试定义一个类Warrior可以 Human或Orc,但应该继承相关类。

如果这是你想要做的,那么正确的方法就是写一个通用的Warrior类:

template<typename Race>
class Warrior : public Race
{
    // Warrior attributes & methods
};

通过这样做,你将能够实现人类战士(Warrior<Human>)以及兽人战士(Warrior<Orc>),或任何其他种族的战士。

但是,Warrior<Human>Warrior<Orc>将被视为完全不同的类,它们不属于同一继承树。


现在,也许这不是你想要做的。也许你希望能够操纵像vector<Warrior*>这样的容器来有效地处理人类和兽人战士,并利用多态性。在这种情况下,反转继承并拥有继承自Human<Class>的模板类Class可能是有意义的。

但是,如果您还希望能够操纵像vector<Human*>这样的容器呢?那么,在这种情况下,你想要根据它们的种族或类来多态地处理字符,所以它应该从两者继承!

template<typename Race, typename Class>
class Character : public Race, public Class
{
    // Character attributes & methods
};

然后你可以安全地为角色类设置一个继承树,并为竞赛提供一个继承树。


然后,如果您希望能够操纵vector<Character*>之类的内容来同时处理所有字符,那就无法做到。最简单的方法是创建一个类AbstractCharacter,所有字符都将从该类继承。但是,如果您在此期间,您希望您的角色能够改变课程吗?然后你必须改变你的设计:也许你的角色不是一个也是战士的人,而是一个拥有“人类”种族的角色。还有一个战士&#39;。

在这种情况下,请为角色制作种族和阶级属性:

class Character
{
    Race* characterRace;
    Class* characterClass;

    // Character attributes & methods
};

其中RaceClass分别是种族和类的继承树的根。你仍然可以使用多态。

(注意:虽然我这样写它是为了更加可读性,但在实际实现中使用智能指针而不是常规指针会更好)

编辑:当我谈到改变角色的类时,我的意思是在运行时。这也意味着你可以在运行时简单地创建你想要的类和种族的角色。

答案 5 :(得分:1)

传统的OOP术语包括&#34;是&#34;并且&#34;有一个&#34;。但是没有&#34;可以&#34;。

您提供的有关特定编码需求的细节很少。但如果我需要这样的东西,我仍然会使用继承。我会创建一个基类,它共享两个可能项的共同特质。然后我会为每个可能的特定情况声明专门的派生类。

然后我会为每个特定情况创建适当的数据类型,这些数据来自我的常见情况。我会根据项目的类型派生特定类型。然后我可以创建一个可以包含不同特定类型的基类集合。

如果没有公共基类,我可能会使用接口而不是基类。

最后,如果您没有提供您要完成的具体示例,则无法提供具体示例。