多重继承用例


15

Java忽略了多重继承,因为它消除了保持语言简单性的设计目标。

我想知道Java(及其生态系统)是否真的“简单”。Python并不复杂,具有多重继承。因此,我的问题不是太主观,而是...

从旨在大量使用多重继承的代码中受益的典型问题模式有哪些?


8
Java出于非常少的原因省略了很多非常好的东西。我不希望出现MI的正当理由。
DeadMG

2
Python的多重继承绝对是龙的领地。它使用深度优先,从左到右的名称解析的事实对于可维护性和理解力均存在重大问题。虽然它在浅层层次结构中很有用,但在深层层次结构中却可能难以置信。
Mark Booth,

我认为Java不包含多重继承的原因是Java开发人员希望他们的语言易于学习。多重继承虽然在某些情况下功能强大,但难以掌握,甚至难以发挥良好作用;这不是您要面对编程大一新生的事情。我的意思是:您如何向苦于继承概念本身的人解释虚拟继承?而且,由于在实现者方面多重继承也不是那么简单,因此Java开发人员很可能忽略了它是双赢的。
cmaster-恢复莫妮卡

Java是名义上的类型。Python不是。这使得多重继承在Python中更容易实现和理解。
Jules

Answers:


11

优点:

  1. 与其他建模方法相比,它有时可以使问题建模更为明显。
  2. 如果不同的对象具有正交的目的,则可以允许某种合成

缺点:

  1. 如果不同的父母没有正交的目的,这会使类型难以理解。
  2. 要理解以一种语言(任何语言)如何实现它并不容易。

在C ++中,用于组合正交特征的多重继承的一个很好的例子是,当您使用CRTP来为游戏设置组件系统时。

我已经开始写一个例子,但我认为更值得一看的是真实世界的例子。Ogre3D的某些代码以一种非常直观的方式使用了多重继承。例如,Mesh类从Resources和AnimationContainer继承。资源公开了所有资源共有的接口,而AnimationContainer公开了特定于操纵一组动画的接口。它们不相关,因此很容易将网格视为一种资源,并且可以包含一组动画。感觉自然吗?

您可以查看该库中的其他示例,例如通过使类继承自CRTP类的变体(使 new和delete重载)继承来以细粒度的方式管理内存分配的方式

如前所述,多重继承的主要问题来自混合相关概念。这使得该语言必须设置复杂的实现(请参见C ++允许解决钻石问题的方式...),并且用户不确定该实现中发生了什么。例如,阅读本文解释如何在C ++中实现它

从语言中删除它有助于避免不知道该如何使这种语言变得糟糕的人们。但这迫使人们以一种有时不自然的方式进行思考,即使是极端情况,您也可能会想起来。


如果您用示例问题来
修饰

好吧,让我尝试添加一些东西。
克莱姆2011年

我不会在Ogre3D寻求设计灵感的任何地方-您是否看到了他们的Singleton感染?
DeadMG

首先,继承人单身并不是真正的单身,其构造和破坏是明确的。接下来,Ogre是硬件系统(或图形驱动程序,如果您愿意的话)上的一层。这意味着系统接口(如Root或其他接口)应该只有一个唯一的表示形式。他们可以删除一些单身人士,但这不是重点。我自愿避免指出这一点,以免进行讨论,因此,请查看我指出的示例。他们对Singleton的使用可能不是完美的,但在实践中显然很有用(但仅对于他们的系统而言,并不是所有东西)。
克莱姆2011年

4

有一个叫做mixin的概念,在更动态的语言中大量使用。多重继承是语言支持混合的一种方式。Mixins通常用作类累积不同功能的一种方式。如果没有多重继承,则必须使用聚合/委托来获得类的mixin类型行为,这在语法上会更加繁琐。


+1这实际上是拥有多重继承的一个很好的理由。Mixins带有其他含义(“不应将此类作为独立类使用”)
ashes999 2013年

2

我认为选择主要是基于钻石问题

而且,通常可以通过委托或其他方式来规避多重继承的使用。

我不确定你最后一个问题的含义。但是,如果“在哪种情况下多重继承有用?”,那么在所有情况下,您都希望对象A基本上具有对象B和C的功能。


2

我在这里不会做过多的工作,但是您可以通过以下链接http://docs.python.org/release/1.5.1p1/tut/multiple.html一定可以理解python中的多重继承:

解释语义的唯一必要规则是用于类属性引用的解析规则。这是深度优先,从左到右。因此,如果在DerivedClassName中找不到属性,则在Base1中搜索该属性,然后(递归)在Base1的基类中,并且只有在该属性中找不到该属性时,才在Base2中搜索它,依此类推。

...

显然,考虑到Python依赖于约定来避免意外的名称冲突,因此不加选择地使用多重继承是一个维护噩梦。多重继承的一个众所周知的问题是一个派生自两个类的类,而这两个类恰好具有相同的基类。尽管很容易弄清楚在这种情况下会发生什么(实例将具有一个``实例变量''或通用基类使用的数据属性的单个副本),但尚不清楚这些语义是否以任何方式有用。

这只是一小段,但足够大,可以消除我的猜疑。


1

多重继承有用的一个地方是类实现多个接口,但是您希望在每个接口中内置一些默认功能。如果实现某些接口的大多数类都希望以相同的方式执行某些操作,但有时您需要执行一些不同的操作,则这很有用。您可以使每个类具有相同的实现,但是将其推到一个位置更有意义。


1
是否需要通用的多重继承,还是只是接口可以为未实现的方法指定默认行为的方法?如果接口只能为它们自己实现的方法指定默认的实现(与从其他接口继承的方法相反),则此功能将完全避免双重钻石问题,从而使多重继承变得困难。
2013年

1

从旨在大量使用多重继承的代码中受益的典型问题模式有哪些?

这只是一个例子,但我发现一个例子对于提高安全性和减轻在调用者或子类上应用级联更改的诱惑是无价的。

我发现即使对于最抽象的无状态接口,多重继承也非常有用,它是C ++中的非虚拟接口习惯用法(NVI)。

他们甚至没有真正的抽象基类这么多的接口有实现向他们保证合同的通用方面只是一点点,因为他们没有真正缩小合同的一般性这么多,因为更好的执行它。

一个简单的例子(有些人可能会检查传入的文件句柄是否打开或类似的东西):

// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
    // Pre: x should always be greater than or equal to zero.
    void f(int x) /*final*/
    {
        // Make sure x is actually greater than or equal to zero
        // to meet the necessary pre-conditions of this function.
        assert(x >= 0);

        // Call the overridden function in the subtype.
        f_impl(x);
    }

protected:
    // Overridden by a boatload of subtypes which implement
    // this non-virtual interface.
    virtual void f_impl(int x) = 0;
};

在这种情况下,也许f被代码库中的一千个位置调用,而f_impl被一百个子类覆盖。

在所有呼叫的1000个位置f或所有覆盖的100个位置中很难进行这种安全检查f_impl

通过仅使此入口指向非虚拟功能,它为我提供了进行此检查的中心位置。这种检查并没有丝毫减少抽象,因为它只是断言了调用此函数所需的前提条件。从某种意义上讲,它可以说是增强了接口提供的协定,并减轻了检查x输入以确保其在覆盖它的所有100个地方都符合有效前提条件的负担。

我希望每种语言都具有,甚至在C ++中,也希望它是一个本机概念(例如:不需要我们定义一个单独的函数来覆盖)。

如果您没有assert事先这样做,这非常有用,并且意识到以后在代码库中的某些随机位置遇到负值传递给时需要使用它f


0

第一:基类的多个副本(C ++问题)以及基类和派生类之间的紧密耦合。

第二:抽象接口的多重继承


您是否暗示它在任何情况下都没有用?没有它,一切都可以方便地进行设计/编码吗?另外请详细说明第二点。
树编码器
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.