在一个项目中,我们的团队正在使用对象列表对数据集执行大量操作,这些数据都应该以类似的方式进行处理。特别是,理想情况下,不同的对象具有相同的行为,这可以通过多态性轻松实现。我遇到的问题是继承意味着 是一个 关系,而不是 有一个 关系。例如,几个对象 有一个 损坏计数器,但为了使其易于在对象列表中使用,可以使用多态性 - 除非这意味着 是一个 关系,这不是真的。(一个人 不是一个 伤害计数器。)

我能想到的唯一解决方案是让类的成员在隐式转换时返回正确的对象类型,而不是依赖继承。放弃是不是更好 是一个 / 有一个 是为了换取易于编程的理想选择吗?

编辑:更具体地说,我使用的是 C++,因此使用多态性将允许不同的对象“行为相同”,即派生类可以驻留在单个列表中并由基类的虚拟函数进行操作。使用接口(或通过继承来模仿它们)似乎是我愿意使用的解决方案。

有帮助吗?

解决方案

这可以使用多重继承来完成。在您的特定情况(C++)中,您可以使用纯虚拟类作为接口。这允许您拥有多重继承,而不会产生范围/歧义问题。例子:

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 接口。纯虚拟类的使用(因此它们就像接口)是关键,应该经常使用。它将未来的变化与整个系统的改变隔离开来。阅读开闭原则以获取更多信息。

其他提示

我认为你应该实现接口以便能够强制执行你的 有一个 关系(我在 C# 中这样做):

public interface IDamageable
{
    void AddDamage(int i);
    int DamageCount {get;}
}

您可以在您的对象中实现它:

public class Person : IDamageable

public class House : IDamageable

并且您可以确定 DamageCount 属性有一个方法可以让您增加伤害,而不意味着一个人和一个房子在某种等级制度中彼此相关。

我同意乔恩的观点,但假设你仍然需要一个单独的伤害计数器类,你可以这样做:

class IDamageable {
  virtual DamageCounter* damage_counter() = 0;
};
class DamageCounter {
  ...
};

每个可损坏类需要提供自己的 Damage_counter() 成员函数。这样做的缺点是它为每个可损坏类创建一个 vtable。您可以改为使用:

class Damageable {
 public:
  DamageCounter damage_counter() { return damage_counter_; }
 private:
  DamageCounter damage_counter_;
};

但很多人都是 不酷 当多个父对象有成员变量时,具有多重继承。

有时为了现实而放弃理想是值得的。如果“正确地做事”会带来严重的问题,而没有真正的好处,那么我就会做错事。话虽如此,我常常认为花时间把事情做好是值得的,因为不必要的多重继承会增加复杂性,而且 导致系统的可维护性较差。您确实必须决定什么最适合您的情况。

一种选择是让这些对象实现 Damageable 接口,而不是继承 DamageCounter. 。这样一来,一个人 有一个 伤害计数器,但是 易损坏的。(我经常发现接口作为形容词比名词更有意义。)然后你就可以在 Damageable 对象,并且不要暴露损坏计数器是底层实现(除非您需要)。

如果你想走模板路线(假设是 C++ 或类似的),你可以使用 mixin 来做到这一点,但如果做得不好,很快就会变得丑陋。

这个问题真的很令人困惑:/

您的粗体问题是非常开放式的,并且答案是“这取决于”,但是您的示例并没有真正提供有关您所询问的上下文的太多信息。这些线条让我感到困惑;

应该以类似的方式处理的数据集

有什么办法?这些集合是由函数处理的吗?又一堂课?通过虚函数获取数据?

特别是,理想情况下,不同的对象具有相同的行为,这可以通过多态性轻松实现

“行为相同”的理想与多态性是完全无关的。多态如何轻松实现?

@凯文

通常,当我们谈论“is a”与“has a”时,我们谈论的是继承与组合。

嗯......伤害计数器只是你的派生类之一的属性,并且不会真正根据你的问题以“一个人是伤害计数器”的方式进行讨论。

将伤害计数器作为属性不允许他将具有伤害计数器的不同对象放入集合中。例如,一个人和一辆车可能都有伤害计数器,但你不能有一个 vector<Person|Car> 或一个 vector<with::getDamage()> 或大多数语言中类似的内容。如果您有一个通用的 Object 基类,那么您可以以这种方式推送它们,但是您将无法访问 getDamage() 方法一般。

当我读到时,这就是他问题的本质。“我是否应该违反 is-ahas-a 为了将某些物体视为相同的,即使它们不是相同的?”

通常,当我们谈论“is a”与“has a”时,我们谈论的是继承与组合。

嗯......伤害计数器只是你的派生类之一的属性,并且不会真正根据你的问题以“一个人是伤害计数器”的方式进行讨论。

看看这个:

http://www.artima.com/designtechniques/compoinh.html

这可能会对您有所帮助。

@德里克: :从措辞来看,我假设有 曾是 基础课,重新阅读了这个问题后,我现在有点明白他的意思了。

从长远来看,“正确地做事”将会带来好处,哪怕只是因为以后维护系统的人会发现如果一开始就做对了就更容易理解。

根据语言的不同,您很可能可以选择多重继承,但通常简单的接口最有意义。我所说的“简单”是指创建一个不想要太多的界面。最好有很多简单的接口和一些单一的接口。当然,总是需要权衡,太多的接口可能会导致被“遗忘”......

@安德鲁

“行为相同”的理想与多态性是完全无关的。多态如何轻松实现?

例如,它们都具有一个共同的功能。我们就这样称呼它吧 addDamage(). 。如果你想做这样的事情:

foreach (obj in mylist)
    obj.addDamage(1)

那么您要么需要动态语言,要么需要它们从公共父类(或接口)扩展。例如。:

class Person : DamageCounter {}
class Car : DamageCounter {}

foreach (DamageCounter d in mylist)
    d.addDamage(1)

那么,你可以治疗 PersonCar 在某些非常有用的情况下也是如此。

多态性 不需要继承. 。多态性是当多个对象实现相同的消息签名(方法)时所得到的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top