【问题标题】:Looking for examples/cases where mutiple interface inheritance (not implementation) is necessary寻找需要多接口继承(不是实现)的示例/案例
【发布时间】:2012-03-20 16:56:04
【问题描述】:

C# 和 Java 都支持从多个父级继承的接口:

public interface IMyInterface : IInterface1, IInterface2

public interface MyInterface
extends Interface1, Interface2

我一直在 .NET Framework 和 JavaAPI 文档中查找使用多接口继承的示例。

在 JavaAPI 中,我找到了扩展超级接口和一个或多个“标记”接口的子接口示例(即它们没有定义的方法)。所以“标记”接口并没有真正在实现上强制执行任何东西。我可以看到这样做的好处,尤其是在引入注释之前。

在 .NET 中,我发现接口继承自多个已经相关的父级。例如IList 继承自ICollectionIEnumerableICollection 已经是IEnumerable 的后代。因此,在实现上强制执行 IEnumerable 已经通过单继承完成。

在任何一种情况下,多接口继承看起来要么是多余的,要么可以被更好的替代方案取代。

如果没有多重接口继承,有没有什么例子很难或不可能完成?

【问题讨论】:

  • MySepcialInterface 应该是 CloneableComparable?
  • 嗯,是的,但我一直在寻找一个真正的理由来扩展两个不相关的接口。正如我在上面提到的,我发现的 Java 示例是“标记”,而我发现的 .NET 示例已经通过单继承来实施。
  • 如果没有多重接口继承,任何设计都不可能实现,甚至难以实现。这种做法只是促进了关注点的解耦和分离,这当然不是强制性的(只是让它更干净)

标签: c# java interface multiple-inheritance


【解决方案1】:

如果我理解您描述 .NET 接口继承链的方式,那么您就错了。这是一个容易犯的错误,因为文档使它看起来如此。 IList<T> 仅显式继承自 ICollection<T>;它继承自ICollection<T>IEnumerable<T>IEnumerable。以下是参考来源的实际定义:

public interface IList<T> : ICollection<T>

ICollection&lt;T&gt; 也是如此,它只继承自 IEnumerable&lt;T&gt;。在编译过程中,继承树在某种意义上被扁平化和去重,用于接口。因此,继承ICollection 的接口不仅仅继承了该接口,而后者又继承了IEnumerable。相反,接口将继承ICollectionIEnumerable 本身。

视觉示例:

IMine : ICollection<T> != IMine : (ICollection<T>:(IEnumerable<T>:IEnumerable))

相反,它看起来像这样编译:

IMine: ICollection<T>, IEnumerable<T>, IEnumerable

这解释了为什么你会得出这样的结论。


关于多接口继承的真实世界示例,请查看IQueryableIOrderedQueryable 示例。这些接口的实际源码声明如下:

public interface IQueryable<out T> : IEnumerable<T>, IQueryable {}
public interface IOrderedQueryable<out T> : IQueryable<T>, IOrderedQueryable {}

IQueryable 需要有一个约定,指定您可以迭代每个元素,而且还需要能够与简单的IEnumerable 区分开来,因为它单独,不是所有IEnumerable 类型都可以用于IQueryProvider 和静态 Queryable 类。


另一个有趣的是在 System.Collections.Concurrent 命名空间中。

public interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection

这很有趣,因为它继承自 ICollection(非泛型)和 IEnumerable&lt;T&gt;,它们都继承自 IEnumerable。当然,这不是问题,但这是另一个很好的例子。如果IProducerConsumerCollection&lt;T&gt; 继承自ICollection&lt;T&gt; 而不是其非泛型对应物,我确信它不会显式继承自IEnumerable&lt;T&gt;。它具有ICollection 中定义的大小、(旧)枚举器和同步方法行为,以及IEnumerable&lt;T&gt; 中定义的通用枚举行为

有两个例子。我发现了更多,但我认为这些可能就足够了。

【讨论】:

  • 现在这更接近我正在寻找的内容。 IQueryable&lt;T&gt; 继承自两个同级接口,它们有一个共同的父级 (IEnumerable),但都不从另一个继承。你是对的,我的假设是基于 msdn 文档和 Redgate Reflector。两者都使每个接口看起来都使用多重继承直接从其每个祖先继承。
  • 因此,多接口继承可以用作将设计约束强制到实现对象上的一种方式。如果 C# 或 Java 仅支持接口的单一继承,则无法强制一个对象必须实现两个接口。
  • 嗯,您的第一句话概括地描述了接口的用途。接口是表示类以“这种方式”行为的方式。我不喜欢这样考虑接口继承,我喜欢认为它更接近于组合,你本质上是在类中包含这些契约。您仍然可以通过分别提供两个接口来强制单个类具有两个接口,但是您必须对所有接口都这样做。除了多重继承之外,您还可以通过不同的方式完成相同的事情,但它的可维护性和丑陋性较差。
【解决方案2】:

您应该从SOLID principles 中查看the Interface Segregation Principle (ISP)。基本上,您应该创建可以连接在一起的小型接口,而不是试图寻找一种组合它们的方法。为什么强制使用对某些类没有用的方法?这通常是多重继承的原因。

至于设计是否不可能,我会说不,因为您可以创建一个巨大的界面。但是,ISP 指出了这种方法的消极性,会使维护成为潜在的困难,但在初始设计中,我会说不,您不需要多重继承来完成实现。它可能看起来很乱就是了

【讨论】:

  • 我熟悉 ISP 和 SOLID 原则,总的来说我同意它们。我正在寻找如果没有多个接口继承,设计将变得困难或不可能的示例。
【解决方案3】:

如果您希望实现您的接口的所有对象都是 IDisposableIComparable

例如,如果您有一组资源,您可能希望根据它们的某些属性对它们进行排序或过滤(使用 IEquatable)。这意味着所有对象都必须实现这两个接口,您可以使用一个通用接口来强制实现这一点

IResource<T>: IDisposable, IComparable<T>, IEquatable<T>

【讨论】:

  • 您能举例说明您何时真正想要这样做吗?似乎可比较的将是一个价值,而一次性的资源参考。
【解决方案4】:

在 Java 中:

Comparable 通常是您希望与主要类型分开的东西。

AbstractListAbstractCollectionList 的子类型。

StringBuilder 既是CharSequence(读取接口)又是Appendable(写入接口)。我不太热衷于像那样将读取和写入合并在一起 - 不可变的值和告诉-不询问的接口。

显然有(主要是邪恶的)标记接口:SerializableCloneableRandomAccess

特别是 GUI 代码往往会将回调接口完全不必要地实现到一个 bug 类中,这通常会扩展一个它没有理由的类。

【讨论】:

  • 你给的例子都是实现了多个接口的类。我一直在寻找扩展多个接口的接口示例。我终于在 java.nio.channels.ByteChannel 中找到了一个示例,但是没有理由将 ReadableByteChannel 和 WritableByteChannel 组合到一个接口中。
猜你喜欢
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
相关资源
最近更新 更多