继承与多态-易用性与纯度

时间:2020-03-05 18:39:48  来源:igfitidea点击:

在一个项目中,我们的团队使用对象列表对应该以相似方式处理的所有数据集执行批量操作。尤其是,不同的对象理想情况下将发挥相同的作用,而使用多态性很容易实现。我有一个问题,就是继承暗示了是关系,而不是有关系。例如,几个对象都有一个损坏计数器,但是为了使它易于在对象列表中使用,可以使用多态,除非那意味着一个关系是不正确的。 (人不是损坏计数器。)

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

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

解决方案

回答

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

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

如果我们想走模板路线(假设使用C ++或者类似的东西),则可以使用mixins来做到这一点,但是如果做得不好的话,这会很快变得很丑陋。

回答

通常,当我们谈论"是一个"与"有一个"时,我们在谈论继承与组合。

嗯...损坏计数器只是我们派生类之一的属性,对于问题,实际上不会以"一个人是一个损坏计数器"的形式进行讨论。

看到这个:

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

这可能会在整个过程中对我们有所帮助。

@Derek:从措辞上来说,我假设有一个基本的说法,重新阅读了我现在有点想知道他在说什么的问题。

回答

这个问题确实令人困惑:/

粗体问题非常开放,回答为"取决于",但是示例并未真正提供有关所询问的上下文的太多信息。这些话使我感到困惑。

sets of data that should all be processed in a similar way

有什么办法?集合是否由函数处理?另一堂课?通过虚拟功能对数据进行处理?

In particular, different objects would ideally act the same, which would be very easily achieved with polymorphism

"相同"的理想和多态性是绝对无关的。多态如何使其易于实现?

回答

@凯文

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 <Person | Car>或者vector <with :: getDamage()>或者类似的东西。如果我们有一个通用的Object基类,则可以用这种方式推销它们,但是我们将无法通用地访问getDamage()方法。

在我阅读时,这就是他问题的实质。 "为了将某些对象视为相同,即使它们不是相同的,我是否应该违反" is-a"和" has-a"?"

回答

从长远来看,"做对了"将有好处,只要是因为以后维护该系统的人会发现,如果从一开始就做对了,就会更容易理解。

根据语言的不同,我们可能会选择多重继承,但是通常,简单的接口才最有意义。 "简单"是指制作一个不会尝试过多的界面。最好有许多简单的接口和一些整体的接口。当然,总会有一个权衡取舍,太多的接口可能会导致那些接口"被遗忘"……

回答

@安德鲁

The ideal of "acting the same" and polymorphism are absolutely unrelated. How does polymorphism make it easy to achieve?

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

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

然后,我们需要动态语言,或者需要它们从公共父类(或者接口)扩展。例如。:

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

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

然后,我们可以在某些非常有用的情况下将"人"和"汽车"视为相同。

回答

我认为我们应该实现接口以能够实现与关系(在C#中这样做):

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

我们可以在对象中实现这一点:

public class Person : IDamageable

public class House : IDamageable

并且我们将确保DamageCount属性并具有一种允许我们添加损害的方法,而并不意味着人和房屋在某种层次结构中彼此相关。

回答

这可以使用多重继承来实现。在特定情况下(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接口。纯虚拟类的使用(使它们像接口一样)是关键,应经常使用。它使将来的更改与更改整个系统保持隔离。阅读有关开放式原则的更多信息。

回答

我同意乔恩的观点,但是假设我们仍然需要单独的损坏计数器类,则可以执行以下操作:

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

但是,当多个父项具有成员变量时,许多人就不容易拥有多重继承。

回答

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