抱歉这个丑陋的问题,但我不知道如何说出来。我将举例说明我的意思:
人类可以是法师或战士,因此法师和战士可以继承人类。但是,如果Orc也可以兼得并入呢?我们不能说“人类是战士”或“战士是人”。 Orc和Human(或父类,Humanoid)是否继承了所有技能,然后选择使用什么?
我不知道是否应该标记特定语言,因为它是关于oop的一般性问题,但由于不同的语言可以针对同一问题采用不同的方法,我更倾向于从C ++的角度给出答案。
答案 0 :(得分:8)
改善您的建模
例如,在计算法师的统计数据时,请询问种族(非人类或兽人),例如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<>
。
答案 1 :(得分:5)
那是我爆发的时候policy-based design。然后我可以Human<Warrior>
或Orc<Warrior>
或Orc<Mage>
。 (或者如果它更有意义的话就转过来:Warrior<Human>
等等。)但是,如果我知道在编译时发生了什么。
如果直到运行时我才知道,我会使用有 Humanoid
的课程Race
(子类为Orc
,{ {1}}等等,有 Human
(子类RPGClass
,Mage
等。)
在其他语言中,有protocols之类的东西可以定义对象的接口,因此您不必定义充满抽象虚拟方法等的基类。因此,Warrior
和RPGClass
将是用于分别与Race
和Mage
等类接口的协议。策略只是在编译时解决的协议。
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
};
其中Race
和Class
分别是种族和类的继承树的根。你仍然可以使用多态。
(注意:虽然我这样写它是为了更加可读性,但在实际实现中使用智能指针而不是常规指针会更好)
编辑:当我谈到改变角色的类时,我的意思是在运行时。这也意味着你可以在运行时简单地创建你想要的类和种族的角色。
答案 5 :(得分:1)
传统的OOP术语包括&#34;是&#34;并且&#34;有一个&#34;。但是没有&#34;可以&#34;。
您提供的有关特定编码需求的细节很少。但如果我需要这样的东西,我仍然会使用继承。我会创建一个基类,它共享两个可能项的共同特质。然后我会为每个可能的特定情况声明专门的派生类。
然后我会为每个特定情况创建适当的数据类型,这些数据来自我的常见情况。我会根据项目的类型派生特定类型。然后我可以创建一个可以包含不同特定类型的基类集合。
如果没有公共基类,我可能会使用接口而不是基类。
最后,如果您没有提供您要完成的具体示例,则无法提供具体示例。