多重继承什么时候派上用场?

时间:2015-07-17 14:46:16

标签: inheritance architecture multiple-inheritance

如果使用多重继承而不是使用合成或其他替代方案更容易解决问题,您能否提供一些实际示例?

什么时候应该使用多重继承?

为什么有些语言支持多重继承(C ++,Python)而其他语言不支持Java(Ruby,Ruby)?我的意思是 - 基于什么因素,编程语言的创建者决定是否包括对MI的支持。

5 个答案:

答案 0 :(得分:12)

多重继承主要用于集成目的,即当您需要一个实现其派生类所需的所有功能的对象时。这是否等于更好的"解决方案比可能的替代方案,是一个争论或品味的问题,所以你可能很幸运,这个问题并未以主要基于意见的方式结束。

关于例子,

从个人经验来看,多重继承可能非常有用,但它也很容易成为一个大问题。维基百科页面在某种程度上讨论了 Diamond 问题:它归结为如果两个或多个基类提供某种方法的实现会发生什么的问题。语言需要定义/实现一种处理方法,通常是通过定义一些方法解析顺序(例如Python's mro)。

当然,冲突的可能性会随着基类的数量和方法的数量而增加。我曾经遇到过一个案例,我们使用的框架(在Python中实现)使用了一些基类的多重继承,这些类是我们派生出来的。然后,我们可以愉快地覆盖继承的方法,而不会意识到它。

多重继承虽然有时很有用,但可以看作违反Single responsibility principle:根据定义,从多个基类派生的类将表现为类。因此,从数据建模的角度来看,Liskov substitution principle也很可能也被违反了。

所以,我相信编程语言的创造者可能会认识到多重继承被认为不是没有概念问题,可能需要大量的实施工作,同时只提供有限的附加值而不是其他解决方案 - 但那只是我的个人猜测。

答案 1 :(得分:8)

首先回答你的上一个问题:某些语言不支持多重继承的原因是因为它很少是必需的,并且与组合或多接口继承相比,往往会使代码变得更加复杂。

这与前两个问题有关,因为很难找到一个真实世界的例子(至少是一个简单的例子),其中M.I.可以轻松解决问题。显然,M.I.的语言的创造者。不支持认为你永远不应该使用它。我猜想某些语言支持它的原因是因为创作者没有理由不这样做。

Here是MSDN上常见问题的链接,解释了为什么C#不支持多重继承。

答案 2 :(得分:3)

真实世界的例子:当多重继承确实很有意义时,我遇到过一种情况,那就是Vector Animation Complex的C ++实现。

这是一个沉浸在时空中的拓扑数据结构。从概念上讲,它类似于图形,除了代替"节点+边缘",它具有:

  • 空间域中的顶点+边+面
  • 时间域中的键+中间段

因为数据结构是空间和时间之间的交叉积,所以总共有6个类:

  1. KeyVertex
  2. KeyEdge
  3. KeyFace
  4. InbetweenVertex
  5. InbetweenEdge
  6. InbetweenFace
  7. 我实现它的方式是通过多重继承。有一个虚拟基类:

    class Cell; 
    

    然后,有三个虚拟类来实现"空间行为"这些单元格和两个虚拟类来实现"时间行为":

    class VertexCell:    virtual public Cell; 
    class EdgeCell:      virtual public Cell; 
    class FaceCell:      virtual public Cell; 
    
    class KeyCell:       virtual public Cell; 
    class InbetweenCell: virtual public Cell; 
    

    最后,实际的非虚拟类是使用多重继承定义的:

    class KeyVertex:       public KeyCell,       public VertexCell; 
    class KeyEdge:         public KeyCell,       public EdgeCell; 
    class KeyFace:         public KeyCell,       public FaceCell; 
    class InbetweenVertex: public InbetweenCell, public VertexCell; 
    class InbetweenEdge:   public InbetweenCell, public EdgeCell; 
    class InbetweenFace:   public InbetweenCell, public FaceCell; 
    

    除了在数学和概念上有意义外,它还有两个实际优点:

    1. 最大化共享代码。代码共享也可以通过组合实现,但它需要更多的样板。

    2. 投射到基类。有时,我需要一个大容器来管理所有单元格,所以它们都继承Cell非常棒,因为我可以拥有vector<Cell*>并直接调用所有单元共享的虚拟方法(例如draw())。有时,我需要一个vector<VertexCell*>。有时,我需要一个vector<KeyCell*>

    3. 事实上,我甚至编写了方便的容器,以便我可以执行以下操作:

      CellPtrSet cells = getCells();
      KeyCellPtrSet keyCells = cells; // Cast and copy those which are actually key cells
      

      简而言之,多重继承使得有一个非常漂亮和干净的API可以编写使用这些单元格的算法。它很好地反映了潜在的数学结构。

答案 3 :(得分:1)

支持某种多重继承的现代语言示例是Java和Scala。 Java有默认的接口方法,因为Java8和Scala具有可以包含方法和数据的特性。

现在,用法示例:

  • 定义调用抽象方法的有用方法。例如,Java中的Collection.removeIf - 它根据提供的谓词删除元素。因此,实现Collection的每个类都将具有removeIf方法。
  • 将具有大量公共方法的类拆分为较小的代码单元。有些情况下聚合不起作用,我们确实需要在单个类中使用半个公共方法。多重继承允许更好地管理此类代码。
  • 的DSL。您可以通过继承实现该DSL的类来将DSL添加到您的代码中。
  • 向对象添加一些行为。在C ++中,您可以使用多重继承为每个对象添加引用计数器以进行内存管理。

简而言之,代码的多重继承非常有用。多重数据继承更值得怀疑,应谨慎使用。

答案 4 :(得分:1)

MI或多重继承是一个非常常见的争论话题,要小心你所要求的...... 我会尝试给你一个简短的答案,因为我的教授曾经说过“继承模拟现实生活,当你的现实生活中的物体继承了你的物体也是如此”。 在一天结束时,你必须考虑使用你的类的程序员,他是否需要知道你的复合对象?或者他最好只调用“封闭盒子”的功能。最好的例子是洗衣机,它有按钮和许多洗涤功能,但你真的不在乎里面发生了什么,所以你的课程,如果我使用它,我不需要知道使它成为它的对象功能。如果我只知道界面,那就更好了。

当您想避免复制代码以及必须在多个地方修改更改时,您应该使用多重继承。

关于你的上一个问题,语言的设计因许多原因而有所不同,如果我们开始比较,这将是一个很长的答案。