接口+扩展(mixin)与基类

时间:2009-04-23 20:04:03

标签: .net interface extension-methods abstract-class

接口+扩展方法(mixin)是否优于抽象类?

如果您的回答是“取决于”,那么它取决于什么?

我认为接口+扩展方法有两个可能的优点。

  • 接口是多重可继承的,而类则不是。
  • 您可以使用扩展方法以不间断的方式扩展接口。 (实现您的界面的客户端将获得新的基础实现,但仍然可以覆盖它。)

我还没有想到这种方法的缺点。接口+扩展方法失败可能有一个明显的简单原因。

关于此主题的两篇有用的文章是

3 个答案:

答案 0 :(得分:24)

扩展方法的缺点:C#3 / VB9之前的客户端将无法轻松使用它。

就我而言,这就是它 - 我认为基于接口的方法非常好。然后,您可以很好地模拟您的依赖关系,并且所有内容基本上都不那么紧密耦合。我不是类继承的忠实粉丝,除非它真的与专业化有关:)

编辑:我刚刚想到了可能相关的另一个好处。某些具体实现可能会提供某些常规方法的更优化版本。

Enumerable.Count就是一个很好的例子 - 它显式检查序列是否实现IList,因为如果它实现了它可以在列表上调用Count而不是迭代通过整个序列。如果IEnumerable<T>是具有虚拟Count()方法的抽象类,则可以在List<T>中覆盖它,而不是明确知道IList的单个实现。我并不是说这总是相关的,也不是IEnumerable<T>应该是一个抽象类(绝对不是!) - 只是指出它是一个可能的小缺点。通过专门化现有的行为(确实只会影响性能而不是结果),这就是多态性真正合适的地方。

答案 1 :(得分:23)

恕我直言,这是一个错误的问题。

您应该将所有内容用于设计目标。

  • 扩展方法不是成员。它们在语法上看起来像成员,所以你可以更容易地找到它们。
  • 扩展方法只能使用公共(或内部)接口。许多其他类也可以这样做。因此,扩展方法不是一种真正的封装方式。
  • 它们是静态方法,不能被覆盖,也不能在单元测试中被模拟。是非OO语言功能,并且调用者静态绑定它。

  • 抽象基类实际上经常被误用于“重用代码”(而不是真正的继承)。这通常适用于继承。

问题应该是:“我应该何时使用接口,扩展方法或基类?”

  • 在需要合同时使用接口(这种情况一直都在发生)。
  • 当你有真正的继承情况时使用(抽象)基类(你可以写一本关于如何判断它的书,所以我就这样离开)。接口大部分也是同时实现的。
  • 使用不应该实际上属于该类型成员的逻辑的扩展方法,因为它不是实现它的类型的责任 - 但是你想让它易于查找并且自然而然地把它称为会员。

编辑:

或问题应该是:“我如何编写不属于基类的可重用功能?”

  • 编写一个公开功能的界面
  • 编写一个实现功能的可重用库类
  • 编写一个实现接口的类,并通过聚合可重用的类来重用该功能。

总的来说,我会说,除特殊情况或特殊设计决策外,扩展方法是业务逻辑的错误位置。

基类只在极少数情况下才是正确的决定。有疑问,事实并非如此。毫无疑问,你应该再考虑一下。

答案 2 :(得分:1)

界面倾向于使代码更清洁我觉得它更容易测试。添加扩展时,您可以在保持干净的可测试代码的同时增加更多的灵活性。

对我来说,抽象类总是显得笨拙,使用接口我可以拥有一个对象工厂,它返回一个特定于我想要完成的对象(关注点分离)。

只是做点什么 A有一个名为Math的接口,它有加,减,除和乘,然后我有一个名为IntMAth的类,它实现了针对整数数学优化的Math,我有另一个名为FloatMath的类,这是针对Floating Math优化的implements Math,我有一个使用Math的GeneralMath来处理其他所有事情。

当我需要添加一些浮点数时,我可以调用我的工厂MathFactory.getMath(typeof(float))并且它有一些逻辑可以知道如果我传入的类型是float,那么它返回FloatMath类。

这样我的所有类都更小,更易于维护,调用类的代码更小等等。

相关问题