在.NET:接口+扩展(mixin)vs base class

Interface + Extension (mixin) vs Base Class

接口+扩展方法(mixin)比抽象类更可取吗?

如果你的答案是"它取决于",它取决于什么?

我看到了接口+扩展方法的两个可能优势。

  • 接口是多重可继承的,类不是。
  • 可以使用扩展方法以非中断方式扩展接口。(实现接口的客户机将获得新的基本实现,但仍然能够覆盖它。)

我还没有想到这种方法会有什么缺点。接口+扩展方法失败的原因可能非常简单。

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

  • 使用接口和扩展方法创建混合函数
  • 抽象基类也有版本控制问题


扩展方法的缺点:pre-c 3/vb9客户机将无法如此轻松地使用它。

就我而言,这就是问题所在——我认为基于接口的方法明显更好。然后您可以很好地模拟您的依赖关系,基本上一切都不那么紧密地耦合在一起。我不太喜欢类继承,除非它是专门化的。)

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

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


嗯,这是个错误的问题。

你应该把一切都用在它的设计上。

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

  • 抽象基类经常被错误地用于"重用代码"(而不是真正的继承)。这一般适用于继承。

问题应该是:"什么时候应该使用接口、扩展方法或基类?"

  • 在需要合同时使用接口(这种情况一直都会发生)。
  • 当您有实际继承的情况时使用(抽象的)基类(您可以编写一本关于如何判断的书,所以我就这样离开它)。一个接口也大多是同时实现的。
  • 对不应实际是类型成员的逻辑使用扩展方法,因为实现它不是类型的责任-但您希望使其易于查找,并且像成员一样调用它感觉很自然。

编辑:

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

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

通常我会说,扩展方法对于业务逻辑来说是错误的地方,除了特殊情况或特殊设计决策。

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


接口会使代码更干净一些,我觉得测试起来更容易一些。当您添加扩展时,在保持干净的可测试代码的同时添加了更多的灵活性。

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

只是在编些东西a有一个名为math的接口,它有加法、减法、除法和乘法,然后我有一个名为intmath的类,它实现了为整数数学而优化的数学,还有一个名为floatmath的类,它实现了为浮动数学而优化的数学,我有一个通用数学,它实现了处理其他一切的数学。

当我需要添加一些float时,我可以调用我的工厂mathfactory.getmath(typeof(float)),它有一些逻辑可以知道,如果我传递的类型是float,那么它将返回floatmath类。

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