2009-04-23 122 views
27

接口+扩展方法(mixin)更适合抽象类吗?接口+扩展(mixin)与基类

如果你的答案是“取决于”,它依赖于什么?

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

  • 接口是可以继承的,类不是。
  • 您可以使用扩展方法以非破坏方式扩展接口。 (实现您的接口的客户端将获得您的新基本实现,但仍可以覆盖它。)

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

两个有用的物品

+1

我还没有看到明确的答案。到目前为止,我相信Jon的回应是最有帮助的,但也一定要读Stefan Steinegger的回答。在选择方法之前,它们都提出了应该考虑的重要问题。我的理解是现在“需要使用继承来覆盖方法?使用抽象基础,否则使用接口+扩展。” – dss539 2009-04-29 15:50:37

回答

21

的扩展方法缺点:客户预先C#3/VB9将无法使用它作为容易。

这就是我所关心的 - 我认为基于接口的方法明显更好。然后你可以很好地嘲笑你的依赖关系,而且一切都基本上不太紧密。我不是一个类继承的巨大粉丝,除非它真的是专业化:)

编辑:我刚刚想到了其他的好处,其中可能是相关的。有些具体实现可能会提供某些常规方法的更优化版本。

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

+0

谢谢,乔恩。 “具体实现可以提供更多优化版本” 具体版本不能在两种方法中做到这一点吗? “如果扩展方法与类型中定义的方法具有相同的签名,将永远不会调用”http://msdn.microsoft.com/zh-cn/library/bb383977.aspx 声音就像您可以覆盖无论如何,在具体类的扩展方法。我想如果客户把它投给IBase,那么它就不会,但抽象类方法也是这样,不是吗? – dss539 2009-04-23 21:25:04

+3

不,你不能真的重写它们。你可以提供具有相同签名的方法,然后如果调用者知道它们,它们将被调用*,但它们不会基于执行时类型被调用*多态*。 – 2009-04-23 21:30:48

+1

如果你有扩展方法的逻辑,你不能嘲笑。当然,你嘲笑界面,但静态扩展方法仍然存在。它不是界面的成员。所以你并不真正使用接口,而是使用静态方法。我没有说扩展方法是不好的,但是你关于耦合的论点是相当薄弱的。 – 2009-04-23 22:42:27

1

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

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

只是把东西了 一个有有加,减,乘,除运算,然后我有一个叫IntMAth类,它实现数学是整数运算进行了优化,我有叫FloatMath的另一个类称为数学的界面实现了针对浮动数学优化的数学运算,并且我有一个实现了Math处理其他所有内容的generalMath。

当我需要添加一些浮动我可以打电话给我的工厂MathFactory.getMath(typeof(浮动)),它有一些逻辑知道,如果我传入的类型是一个浮动然后它返回FloatMath类。

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

23

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

你应该使用所有的东西来设计它。

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

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

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

  • 无论何时您需要合同(并且始终发生这种情况),都会使用接口。
  • 使用(抽象)基类,当你有一个真正的继承情况(你可以写一本关于如何判断的书,所以我就这样离开它)。大多数接口也同时实现。
  • 使用扩展方法的逻辑实际上不应该是该类型的成员,因为它不是该类型的责任来实现它 - 但是你想让它容易找到,并且感觉它很自然成员。

编辑:

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

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

一般而言,我会说扩展方法是业务逻辑的错误地方,除了特殊情况或特殊的设计决策。

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