【问题标题】:Interface inheritance: what do you think of this: [closed]接口继承:你怎么看这个:[关闭]
【发布时间】:2008-11-21 08:09:36
【问题描述】:

在查看我们的代码库时,我发现了一个类似于以下模式的继承结构:

interface IBase
{
    void Method1();
    void Method2();
}

interface IInterface2 : IBase
{
    void Method3();
}

class Class1 : IInterface2
{
    ...
}

class Class2 : IInterface2
{
    ...
}

class Class3 : IInterface2
{ 
    ...
}

Class2Method1 抛出NotImplementedException

问题:

  • 您一般如何看待继承接口?
  • IBaseClass2 之间的关系是否违反里氏替换原则?

【问题讨论】:

  • 也许 IInterfaces 应该扩展/实现 IBase...
  • ...对于编辑后的版本,也许您可​​能会丢失 Class1 和 Class3,因为它们与 Class2 相同。

标签: c# oop inheritance liskov-substitution-principle


【解决方案1】:

嗯,首先,我通常反对通过抛出 NotImplementedException 异常来实现接口。基本上就像在说“嗯,这个类也可以用作计算器,呃,差不多”。

但在某些情况下,它确实是做某事“正确方式”的唯一方法,所以我不是 100% 反对它。

只是需要注意的事情。

接口就是契约,实现了接口就表示你遵守契约。如果您随后开始否定合同的某些部分,在我看来,合同或合同的实施是经过深思熟虑的。


编辑:看到Greg Beech的回答后:如果一个接口明确说实现应该抛出这些异常,那么它是合同的一部分,那么我同意这个类是完全允许的去做吧。


至于代换原则,states表示:

令 q(x) 是关于 T 类型的对象 x 的可证明性质。那么 q(y) 对于 S 类型的对象 y 应该为真,其中 S 是 T 的子类型。

在这种情况下,如果您更改该方法在后代类型中的作用,它与从基类覆盖方法一样违反原则。

原理在wikipedia page上比较详细,比如以下几点(括号和强调我的cmets):

  • 不能在子类中加强先决条件。 (前提条件可能是“此时该类已准备好调用此方法”)
  • 不能在子类中削弱后置条件。 (后置条件可能是调用方法后,类的状态为真)

由于你没有展示你的接口的完整契约,只有编译器可以检查的声明部分,所以不可能知道这个原则是否适用于你的实现。

例如,如果您的 Method2 附加了以下条件:

  • 可以随时调用
  • 修改对象的状态,为事件链中的下一个事件做好准备

然后抛出 NotImplementedException 违反了原则。

但是,如果合同还规定:

  • 对于不支持事件链的类,此方法应抛出 NotImplementedException 或 NotSupportedException

那么它可能不会。

【讨论】:

    【解决方案2】:

    所以,我假设您要问的问题是:

    如果派生类型抛出一个 NotImplementedException 一个方法 在基类型没有的地方,这样做 违反 Liskov 替换 原则。

    我会说这取决于接口文档是否说方法可能会抛出此异常以履行其合同。如果是,则不违反原则,否则就违反了。

    .NET 框架中 this 的经典示例是 Stream 类,它有一堆操作,例如 ReadWriteSeek,但不需要流来支持所有这些操作,并记录为允许抛出NotSupportedException

    【讨论】:

    • 澄清一下,你提到的方法ReadWriteSeek是一个类的abstract成员,因此它们与接口相当方法(在某种意义上也是抽象的)。
    【解决方案3】:

    假设我明白你的意思,那么我认为答案是肯定的,继承接口就可以,不,它不违反里氏替换原则。

    考虑接口的方式是“表现得像一个”运算符。它描述了类承诺遵守的一组行为,通过方法等表示。因此,从 IEatsMice 接口继承的 IBehavesLikeACat 接口没有问题:如果它的行为像猫,那么它显然会吃老鼠。因此,Cat 会同时实现这两者,而只有 IEatsMice 的雪貂。

    【讨论】:

      【解决方案4】:

      首先,接口中的继承是可以的,它的应用方式与类继承相同,是一个非常强大的工具。

      接口描述行为,假设一个接口定义了一个类“可以做什么”,所以如果你实现了一个接口,你就声明你可以做那个接口指定的事情。例如:

      interface ISubmergible
      {
        void Submerge();
      }
      

      很明显,如果类实现了接口,那么它就可以被淹没。一些接口仍然隐含着其他接口,例如,想象一下这个接口

      interface IRunner
      {
        void Run();
      }
      

      这定义了一个接口,表明实现它的类可以运行......但是,在我们程序的上下文中,如果有东西可以运行,它显然可以行走,所以你要确保它是满足的:

      interface IWalker
      {
        void Walk();
      }
      
      interface IRunner : IWalker
      {
        void Run();
      }
      

      最后,关于整个 NotImplementedException 事情……我对其中的一些建议感到惊讶。 NotImplementedException 应该永远不会由类的接口方法引发。如果你实现了一个接口,你就明确地接受了接口建立的契约,如果你提出一个 NotImplementedException,你基本上是在说“好吧,我撒谎了,我告诉过你我支持这个接口,但我真的不支持”。

      接口旨在避免担心类实现了什么,什么没有实现。如果你把它拿走,它们就没用了。更重要的是,您的同事会期待这样的行为,即使您了解自己在做什么,其他团队成员也不会,甚至 6 个月后的您也不会理解下面的逻辑它。 所以 class2 违反了常识和接口目的。 Class2 没有实现 IBase,所以不要声称它实现了。

      【讨论】:

      • 如果一个接口的某些实现能够做某事而有些则不能,并且许多客户希望在它存在但可以解决它的缺席时利用该功能,我建议将方法包含在接口中但要求客户在使用它之前询问其可用性或准备失败通常比将方法放入不同的接口并要求想要使用该方法的客户必须首先转换到该接口。在我看来,广泛存在的客户端代码接收...
      • ...对Interface1 的引用,测试它是否实现了Interface2,如果是,则转换为该类型以便它可以使用Interface2.someMethod(),这表明someMethod() 应该是Interface1 的“可选”部分。在我看来,ISP 表示如果某些客户端能够支持method1() 但不支持method2(),那么应该有一种方法可以让一个类承诺支持method1 而无需承诺method2(),但这并不暗示它们应该是不同的类型。事实上,运行时特征查询可以使 Promise 的分离比单独的类型更精细。
      【解决方案5】:

      抛出 NotSupportedException 是可以的:这是在某些接口功能未按设计实现时抛出的异常。

      NotImplementedException 不太清楚:这通常用于尚未实现但将会实现的代码。例如,Visual Studio 2008 生成的接口实现存根包含代码“throw new NotImplementedException()”。

      请参阅Brad Abram's blog 了解有关此问题的讨论。

      【讨论】:

        【解决方案6】:

        .Net 框架中的多个地方使用了接口中的继承。所以虽然不是那里的所有东西都是完美的,但我想这被认为是可以的。以 IEnumerable 接口为例。

        至于抛出 NotImplementedExceptions,我想说这取决于您在代码中如何使用它们。例如,您可以使用多种方法定义接口,其中一些方法是可选的。在这种情况下,您应该检查该方法是否可用,并且仅在可用时才使用它。

        【讨论】:

          猜你喜欢
          • 2010-10-22
          • 2013-08-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多