遗传和多态性 - 易用性和纯度

时间:2008-08-15 01:00:48

标签: c++ inheritance oop polymorphism

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

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

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

10 个答案:

答案 0 :(得分:5)

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

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

您可以在对象中实现此功能:

public class Person : IDamageable

public class House : IDamageable

你确定DamageCount属性并且有一种方法可以让你增加伤害,而不会暗示一个人和一个房子在某种层面上彼此相关。

答案 1 :(得分:3)

这可以使用多重继承来完成。在特定情况下(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'都是'伤害',意思是,他们实现了Damage界面。使用纯虚拟类(使它们像接口一样)是关键,应该经常使用。它将未来的变化与改变整个系统隔离开来。阅读开放封闭原则以获取更多信息。

答案 2 :(得分:1)

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

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_;
};

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

答案 3 :(得分:0)

有时值得放弃现实的理想。如果它会导致一个大问题“做对了”没有真正的好处,那么我会做错了。话虽如此,我经常认为值得花时间去做正确的事情,因为不必要的多重继承会增加复杂性,并且可能会导致系统的可维护性降低。你真的必须决定什么是最适合你的情况。

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

如果你想去模板路线(假设是C ++或者类似的),你可以用mixins来做这件事,但如果做得不好,这会很快变得难看。

答案 4 :(得分:0)

通常当我们谈论'是'是'vs'有'时,我们谈的是继承与组合。

嗯...损坏计数器只是你派生类之一的属性,并且就你的问题而言,不会真正讨论“一个人是一个损害计数器”。

见:

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

这可能对你有所帮助。

@ Derek :根据措辞,我认为 是一个基础分支,重新阅读了我现在看到他正在看到的问题。

答案 5 :(得分:0)

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

你的粗体问题是非常开放的,并且答案是“它取决于”,但你的例子并没有真正提供有关你所要求的背景的大量信息。这些线条让我感到困惑;

  
    

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

  

什么方式?这些集是由函数处理的吗?另一堂课?通过数据上的虚函数?

  
    

特别是,不同的对象在理想情况下的行为相同,这可以通过多态实现很容易实现

  

“行为相同”和多态的理想绝对无关。多态如何使其易于实现?

答案 6 :(得分:0)

@Kevin

  

通常当我们谈论'是'是'vs'有'时,我们谈的是继承与组合。

     

嗯...损坏计数器只是你派生类之一的属性,并且就你的问题而言,不会真正讨论“一个人是一个损害计数器”。

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

正如我所读到的那样,这就是他的问题的本质。 “我是否应该违反is-ahas-a,以便将某些对象视为相同,即使它们不相同?”

答案 7 :(得分:0)

“正确行事”从长远来看会有好处,只是因为稍后维护系统的人会更容易理解是否正确地开始使用。

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

答案 8 :(得分:0)

@Andrew

  

“行为相同”和多态的理想绝对无关。多态如何使其易于实现?

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

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

然后您需要动态语言,或者需要它们从公共父类(或接口)扩展。 e.g:

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

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

然后,您可以在某些非常有用的情况下同等对待PersonCar

答案 9 :(得分:0)

多态性不需要继承。当多个对象实现相同的消息签名(方法)时,就会获得多态性。