在一个项目中,我们的团队使用对象列表对应该以相似方式处理的所有数据集执行批量操作。尤其是,不同的对象在理想情况下将发挥相同的作用,而使用多态性很容易实现。我有一个问题,就是继承暗示了是关系,而不是有关系。例如,多个对象都有一个损坏计数器,但是为了使它易于在对象列表中使用,可以使用多态性-除非那意味着a是不正确的关系。 (人不是损坏计数器。)
我能想到的唯一解决方案是让类的成员在隐式转换时返回正确的对象类型,而不是依赖于继承。放弃一个/理想的选择以换取易于编程的方法会更好吗?
编辑:
更具体地说,我正在使用C ++,因此使用多态性将使不同的对象"发挥相同的作用",即派生的类可以驻留在单个列表中,并可以由基类的虚函数进行操作。接口的使用(或通过继承模仿它们)似乎是我愿意使用的解决方案。
我认为您应该实现接口以能够实现与您的关系(在C#中这样做):
1 2 3 4 5
| public interface IDamageable
{
void AddDamage(int i);
int DamageCount {get;}
} |
您可以在您的对象中实现此目的:
1 2 3
| public class Person : IDamageable
public class House : IDamageable |
而且,您将确保DamageCount属性并具有一种允许您添加损坏的方法,而并不意味着人和房屋在某种层次结构中彼此相关。
这可以使用多重继承来完成。在特定情况下(C ++),可以将纯虚拟类用作接口。这使您可以进行多重继承而不会产生范围/模糊性问题。例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Damage {
virtual void addDamage(int d) = 0;
virtual int getDamage() = 0;
};
class Person : public virtual Damage {
void addDamage(int d) {
// ...
damage += d * 2;
}
int getDamage() {
return damage;
}
};
class Car : public virtual Damage {
void addDamage(int d) {
// ...
damage += d;
}
int getDamage() {
return damage;
}
}; |
现在,Person和Car'is-a'Damage都意味着实现了Damage接口。纯虚拟类的使用(使它们像接口一样)是关键,应经常使用。它使将来的更改与更改整个系统保持隔离。阅读有关开放式原则的更多信息。
我同意乔恩的观点,但是假设您仍然需要单独的损坏计数器类,则可以执行以下操作:
1 2 3 4 5 6
| class IDamageable {
virtual DamageCounter* damage_counter() = 0;
};
class DamageCounter {
...
}; |
然后,每个可损坏的类都需要提供自己的Damage_counter()成员函数。这样做的缺点是,它为每个可损坏的类创建一个vtable。您可以改用:
1 2 3 4 5 6
| class Damageable {
public:
DamageCounter damage_counter() { return damage_counter_; }
private:
DamageCounter damage_counter_;
}; |
但是,当多个父项具有成员变量时,许多人就不容易拥有多重继承。
多态不需要继承。多态是当多个对象实现相同的消息签名(方法)时得到的结果。
@安德鲁
The ideal of"acting the same" and polymorphism are absolutely unrelated. How does polymorphism make it easy to achieve?
它们都具有例如共同的功能。我们称之为addDamage()。如果您想做这样的事情:
1 2
| foreach (obj in mylist)
obj.addDamage(1) |
然后,您需要动态语言,或者需要它们从公共父类(或接口)扩展。例如。:
1 2 3 4 5
| class Person : DamageCounter {}
class Car : DamageCounter {}
foreach (DamageCounter d in mylist)
d.addDamage(1) |
然后,您可以在某些非常有用的情况下将Person和Car视为相同。
从长远来看,"做对了"将有好处,只要是因为以后维护该系统的人会发现,如果从一开始就做对了,就会更容易理解。
根据语言的不同,您可能会选择多重继承,但是通常简单的接口最有意义。"简单"是指制作一个不会过多尝试的界面。最好有许多简单的接口和一些整体的接口。当然,总会有一个权衡取舍,太多的接口可能会导致有关"接口"被"遗忘" ...
@凯文
Normally when we talk about 'is a' vs 'has a' we're talking about Inheritance vs Composition.
Um...damage counter would just be attribute of one of your derived classes and wouldn't really be discussed in terms of 'A person is a damage counter' with respect to your question.
将损坏计数器作为属性不能使他将带有损坏计数器的对象分类为一个集合。例如,一个人和一辆汽车可能都具有损坏计数器,但是大多数语言中不能包含vector或vector或类似内容。如果您有一个通用的Object基类,则可以以这种方式推入它们,但是您将无法通用地访问getDamage()方法。
在我阅读时,这是他问题的实质。"为了将某些对象视为相同,即使它们不是相同的,我是否应该违反is-a和has-a?"
这个问题确实令人困惑:/
您的粗体问题非常开放,回答为"取决于",但是您的示例并未真正提供有关所询问的上下文的太多信息。这些话使我感到困惑。
sets of data that should all be processed in a similar way
blockquote>
有什么办法?集合是否由函数处理?另一个班?通过虚拟功能对数据进行处理?
In particular, different objects would ideally act the same, which would be very easily achieved with polymorphism
blockquote>
"相同"的理想和多态性是绝对无关的。多态如何使其易于实现?
通常,当我们谈论"是一个"与"有一个"时,我们是在谈论继承与组合。
嗯……损坏计数器只是您派生类之一的属性,对于您的问题,实际上不会以"一个人是一个损坏计数器"的形式进行讨论。
看到这个:
http://www.artima.com/designtechniques/compoinh.html
这可能会对您有帮助。
@Derek:从措辞上来说,我假设有一个基本的说法,重新阅读了我现在有点想知道他在说什么的问题。
有时值得放弃现实的理想。如果这将导致一个巨大的问题,即"做对了"却没有真正的好处,那么我会做错了。话虽如此,我经常认为花点时间做对是值得的,因为不必要的多重继承会增加复杂性,并且可能导致系统的可维护性降低。您确实必须决定哪种情况最适合您的情况。
一种选择是让这些对象实现Damageable接口,而不是从DamageCounter继承。这样,一个人有一个损坏计数器,但是很容易损坏。 (我经常发现接口比名词更容易形容词。)然后,您可以在Damageable对象上具有一致的损伤接口,而不必暴露损伤计数器是基础实现(除非您需要)。
如果您想走模板路线(假设使用C ++或类似的语言),则可以使用mixins来做到这一点,但是如果做得不好,那会很快变得很丑陋。