一尘不染

设计C#类库时,何时应选择通过接口继承?

c#

我有一个数字Processor类,可以做两种非常不同的事情,但是是从通用代码中调用的(“控制反转”情况)。

我想知道在决定是否应该全部继承自BaseProcessor或实现IProcessor为接口时,我应该了解哪些设计考虑因素(或者对您来说是对用户有用的认识)。


阅读 310

收藏
2020-05-19

共1个答案

一尘不染

通常,规则如下所示:

  • 继承描述了 is-a 关系。
  • 实施接口描述了 可以做的 关系。

为了更具体地说明这一点,让我们看一个例子。的System.Drawing.Bitmap是-一个 图像(并且同样地,其从继承Image类),但它也
可以做的 配置,所以它实现了IDisposable接口。它还 可以进行
序列化,因此可以从ISerializable接口实现。

但实际上,接口通常用于模拟C#中的多重继承。如果您的Processor类需要继承自System.ComponentModel.Component,则您别无选择,只能实现一个IProcessor接口。

事实是,接口和抽象基类都提供了指定特定类可以做什么的协定。众所周知,接口必须声明此合同,但这是不正确的。在我看来,最大的好处是抽象基类允许您为子类提供默认功能。但是,如果没有有意义的默认功能,则没有什么可以阻止您将方法本身标记为abstract,要求派生类自己实现它,就像它们要实现一个接口一样。

对于此类问题的答案,我经常转向.NET
Framework设计指南
,其中有关于在类和接口之间进行选择的说明:

通常,类是公开抽象的首选构造。

接口的主要缺点是,在允许API演变时,它们不如类灵活。交付接口后,其成员集合将永久固定。接口的任何添加都会破坏实现接口的现有类型。

一个类提供了更大的灵活性。您可以将成员添加到已经交付的类中。只要该方法不是抽象的(即,只要您提供该方法的默认实现),任何现有的派生类都将继续保持不变。

[。 。。]

支持接口的最常见论点之一是,它们允许将协定与实现分开。但是,该参数错误地假定您无法使用类将合同与实现分开。驻留在独立于其具体实现的单独程序集中的抽象类是实现此类分离的好方法。

他们的一般建议如下:

  • 不要 在接口上偏爱定义类。
  • 不要 使用抽象类而不是接口来将契约与实现分离。如果正确定义了抽象类,则可以在契约和实现之间实现相同程度的解耦。
  • 不要 ,如果你需要提供一个值类型的多态层次定义一个接口。
  • 考虑 定义接口以实现与多重继承相似的效果。

克里斯·安德森(Chris Anderson)对此最后一个原则表示特别同意,认为:

抽象类型的版本要好得多,并允许将来扩展,但它们也会消耗您唯一的一种基本类型。当您真正定义两个对象之间随时间变化的契约时,接口是合适的。抽象基础类型更适合为一系列类型定义公共基础。

2020-05-19