使用 C#8 Microsoft引入了接口方法的默认实现。它仍然是一个相当新的功能,似乎有很多关注的博主写这个。
我想知道的是默认实现是否有可能成为依赖反转和 DI 的有用工具,或者它是否促进了糟糕的编程风格?它是否违反了任何众所周知的原则,如 SOLID?
默认接口实现有两个主要设计目标。更重要的可以追溯到有关设计界面的准则。特别是,一旦你发布了一个界面,它就应该一成不变,永远不会改变。问题是,这也是一个被忽略的规则......一直。
第一个也是主要的实用程序是默认接口实现允许您将新成员引入接口,而不会破坏与该(公共(接口的使用者的源代码或二进制兼容性。这仍然限制了您在更改公共接口时可以执行的更改类型,但也使客户端更容易使用新接口 - 升级是免费的,他们可以立即开始使用新功能。
第二个设计目标是扩展具有特征的类的方法 - 这在游戏开发中长期使用。基本思想是,只需让类实现接口,就可以向类添加新的明确定义的行为,同时保留修改类本身行为的能力。这本质上是一种相对较弱的元编程形式。
当然,仅仅因为这些是设计目标并不意味着它们是您应该使用默认实现的唯一方法。但是如果你概括一下,你会得到这两个基本用途:
- 在不破坏兼容性的情况下扩展接口,并且必须对接口进行版本控制。
- 扩展类,而不必创建从类继承中获得的严格树层次结构。
事实上,你甚至可以说这比类继承更简单、更清晰、更强大。在某种程度上,这是从扩展方法开始的方法的延续 - 本质上,默认接口方法实现是一种扩展方法,它也是虚拟的。默认实现只能与公共接口一起使用,但实现类也可以使用自己的隐藏状态。它为 C# 提供了有限形式的多重继承,而不必处理两个"父"的状态如何连接在一起(因为接口没有任何状态(。
最后,如果您担心像 SOLID 这样的原则,让我们试一试:
- SRP - 没有真正的变化,在一些地方非破坏性地添加新逻辑的新选项。我说这对SRP来说是一个小小的胜利。
- OCP - 您可以获得扩展类的强大新方法,而无需修改类本身,只要该类实现了新功能有意义的接口。但是你不能以这种方式改变现有的类行为,所以它仍然关闭进行修改。在这里赢得非常有用的胜利。
- LSP - 没有真正受到影响。允许你通过实现接口向类添加新的可替换行为(根据需要选择覆盖该行为(的一些小胜利。
- ISP - 这可以是双向的。如果您有合理的默认实现,则默认实现可能会使拥有大量小接口变得更加容易。但它们也可以鼓励您继续修改现有接口,而不是添加新接口。
- DIP - 大部分不受影响。如果抽象是对已有抽象的合理扩展,或者默认行为可以在不依赖状态的情况下实现,则可以更轻松地添加新抽象。您也可以尝试将默认行为用作合同行为,但 IMO 它仍然比抽象方法(您也可以控制状态(的诱惑更小。