我应该在实现之前编写接口API吗?


14

最近,我一直在研究更多的“有组织的”编程,并且我一直在学习应该对接口而不是对实现进行编程。考虑到这一点,在可能的情况下为项目编写实现之前,最好在接口中“略过”项目吗?

如果是这种情况,那么在使用第三方库(即Lidgren)的情况下,我是否也应该将它们包装在接口中并通过IOC容器进行解析,还是可以将它们公开给接口?


以我个人的经验-最好先设计架构-每个班级的责任。您不必写下来,只需考虑一下或在纸上画草图即可。然后是关于个人喜好的问题,但我建议您首先为开始实现的每种方法编写文档注释。编写文档确实使您在开始编写代码之前先考虑一下功能。
苏珊(Sulthan)2013年

是的,并在实现接口之前对接口(或相应的抽象类)进行编程。在陷入困境(并投资于实现)之前,它有助于使消息流从客户端流向服务器,反之亦然。关于此事的很好的幻灯片演示:如何设计一个好的API
Marjan Venema 2013年

Answers:


8

不幸的是,您会发现这通常归结为个人喜好。

不过,到目前为止您描述的内容似乎不错。实际上,如果您想要(并且我推荐),则可以使用以下方法:

  1. 将您的应用程序框架编写为接口,抽象类(已存根)和类(也已存根)
  2. 针对这些接口和存根编写测试(它们现在将失败)
  3. 编写您的实现(完成实现后,您的测试将开始通过)

您正在集中精力尝试编写更多“有组织的”代码。遵循TDD可以帮助您。

一些额外的要点:

  • IoC容器很方便。尽可能多地使用它们和DI。
  • 包装第三方库。这将放松您的代码(您控制的代码)和第三方代码(您不控制的代码)之间的耦合

1
这是我最初的想法,但是我被告知它将违反YAGNI原则。我在许多未完成的项目中发现的问题是,由于我编写的Blob代码数量很大,它们很快变得难以维护,因为我没有正确组织它或计划我的攻击计划。
Dan Pantry 2013年

哪一部分会违反YAGNI?
MetaFight 2013年

包装第三方库。
Dan Pantry 2013年

2
我想可以归结为:第三方图书馆发生变化的几率是多少?如果此可能性为0%,请确保YAGNI。但是,这种情况很少发生。另外,包装第三方库可能会使您的其他代码更易于单元测试(例如,如果您无法模拟第三方库)
MetaFight 2013年

1
@DanPantry:包装第三方库并不是违反YAGNI的行为,而是一种非常需要的保护措施,以防止“第三方库感染您自己的代码”。这不仅涉及交换库的问题,而且正如MetaFight所说的那样,可以防御库的较新版本的更改,否则将需要在您自己的代码中进行更改。通过包装库(尤其是其特定类型:类,枚举,结构等),可以隔离自己的代码,并且在库更改时(无论出于何种原因)都可以更改一个点。
Marjan Venema 2013年

13

是的,您应该针对接口而不是已知的实现进行编码,是的,应该首先构造接口,而不要让它们从您自己的代码中出现。

两项建议的原因大致相同:计算机编程很大程度上与人为因素有关。许多人发现这令人惊讶,但请考虑:有几乎无限多种不同的方法可以很好地解决同一计算问题。对于几乎没有写过它们的人(或者实际上在不久之后写给作者),几乎所有这些都是完全不可能的。

随之而来的是,良好的软件工程主要是关于如何以允许以后使用源代码的方式实现期望的效果(以合理的效率进行正确的计算)。接口和API是该学科的关键部分:它们使您可以一次在一个描述级别上考虑问题。这比同时考虑业务一致性规则链表实现要容易得多,因此强行强加这种关注点分离比允许客户端程序员以自己喜欢的方式使用您的代码更好

对于许多牛仔程序员来说,这是令人难以置信的,他们坚信他们能理解所写的一切,比普通思想家要好得多,并且能够处理给“较少”程序员带来麻烦的所有复杂性。不了解自己的认知极限是一个非常普遍的现象-这就是为什么代码组织中的最佳实践如此重要(并且常常被忽略)的原因。

重复一遍,即使您只与自己合作,接口和API障碍也大体上不错。对于外部库,如果它们带来了经过深思熟虑的API,那么只要您不希望将那个库换成另一个库,我认为使用它就没有问题。否则,包装或反腐败层可能是一个很好的主意。


我喜欢您关于SE的观点,SE的主要目的是以一种允许以后使用源代码的方式实现预期的效果。我希望我能够在上一份工作中一直如此表述,因为我一直在为干净的代码而战!
MetaFight 2013年

是否有API的命名约定,这些API只是我将在所有地方最终使用的接口?就像,如果我正在执行命令模式,是否将其称为“ commandables”?
史努比(Snoop)

@StevieV有多种,例如IBlah由实现BlahBlah由实现BlahImpl。我不喜欢这两个,并倾向于使用Blah由实施OralBlahWrittenBlahASLBlah。但是像往常一样,遵守您现有的代码库和期望比遵守任何通用标准更为重要。
Kilian Foth '16

4

而不是仅仅刻苦地编程接口,为什么不考虑测试驱动开发/设计(TDD)?

许多人将TDD视为一种测试实践,但实际上,它是一种设计方法,您可以让测试通过测试(最初通过单元测试,也可以通过集成测试)公开代码的使用方式。

接口编程是工具集中的重要武器,但是像大多数事物一样,它并不总是合适的解决方案/技术/实践,因为它并不总是必需的。您应该对需要的接口进行编程。

使用TDD将迫使您探索此类接口在哪些地方很重要,坦率地说,在哪些地方无关紧要。最后,您应该在代码库中拥有一组相当不错的单元测试。

至于使用第三方库,我强烈建议在适当的时候将它们包装在您自己的抽象中。而不是让您API的客户“了解”他们。

祝好运!

[编辑:看到megaflight的答案-完全同意]


2
尽管TDD可能不是正式的“接口”声明,但它隐含地让您考虑接口而不是实现。
DougM 2013年

1
这是一个很好的答案。+1表示TDD,我认为这是OP 处理新项目时从何处开始的真正问题的解决方案,如果可以,我会再次+1“使用TDD将迫使您探索此类接口的位置很重要,坦率地说,无关紧要。”
本杰明·霍奇森

2

我认为这太过分了。如果您的API用户不需要被迫以某种方式实现/使用某些东西,那么我将忽略它。接口是合同,如果我不需要合同,那为什么还要给我一个?

我认为人们过度使用界面。您将添加大多数情况下不需要的复杂性层。


我认为,人们使用界面。如果要创建可重用的组件,则界面不仅是“很不错”的附加功能,而且还是主要要注意的事项。除了实际的实施过程。
JensG 2013年

1

根据合同进行编程几乎总是一个好主意。该合同不必是接口,而是可以由一个类来实现。在我看来,由于单元测试的考虑和模拟框架的原因,接口与DI一起已被过度使用。

我个人更喜欢仅在我很可能拥有或确实有一项以上合同实现时才引入接口。接口非常适合我希望抽象化数据访问的存储库,但对于我的标准业务逻辑来说可能不太合适,因为我的标准业务逻辑可能相对不灵活。

现在没有接口会导致单元测试出现问题,特别是对于纯粹主义者而言。但是我有兴趣模拟程序的外部依赖关系,而不是内部依赖关系。我希望我的测试执行代码验证,而不是回显代码结构。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.