如何定义一个好的或者不好的API?

66

背景:

我在大学上一门名为“软件约束”的课程。在前几节课中,我们学习了如何构建良好的API。

一个非常糟糕的API函数的好例子是C#中的socket public static void Select(IList checkRead, IList checkWrite, IList checkError, int microseconds);。该函数接收3个套接字列表,并破坏它们,使用户必须在将它们馈送到Select()之前克隆所有套接字。它还有一个超时(以微秒为单位),它是一个int,设置服务器等待套接字的最长时间。这个值的限制是+/- 35分钟(因为它是int类型)。


问题:

  1. 你如何定义一个API是“糟糕”的?
  2. 你如何定义一个API是“好”的?

需要考虑的要点:

  • 函数名称很难记住。
  • 函数参数难以理解。
  • 文档不好。
  • 所有东西都是如此相互关联,如果你需要更改1行代码,实际上你需要在其他地方更改数百行。
  • 函数破坏它们的参数。
  • 由于“隐藏”的复杂性,可扩展性差。
  • 用户/开发人员需要构建包装器来使用API。

1
REST API架构 - 最佳实践 - Techie
13个回答

109

在API设计中,我经常觉得这篇演讲很有用:
API 的设计与重要性 by Joshua Bloch

以下是一个摘录,我建议您阅读整篇文章/观看视频。

II. 通用原则

  • API 应做一件事,并且做好它
  • API 应该尽可能小,但不应更小
  • 实现不应影响 API
  • 最小化一切的可访问性
  • 名称很重要- API 是一种小语言
  • 文档很重要
  • 必须有规律地记录文档
  • 考虑 API 设计决策对性能的影响
  • API 设计决策对性能的影响是真实而永久的
  • API 必须与平台和平共处

III. 类设计

  • 最小化可变性
  • 仅在有意义的情况下使用子类
  • 为继承设计和记录,否则禁止

IV. 方法设计

  • 不要让客户端做模块可以做的事情
  • 不要违反最少惊奇原则
  • 快速失败-尽快报告错误
  • 以字符串形式提供所有可用数据的编程访问权限
  • 谨慎使用重载
  • 使用适当的参数和返回类型
  • 在方法之间使用一致的参数排序
  • 避免长参数列表
  • 避免需要异常处理的返回值

2
+1 这比我的回答更完整、更有用。值得一读。 - Tim
1
如果有人愿意做这项工作,把幻灯片的要点扩展到文章中是值得的。 - Barry Kelly
感谢评论/投票。根据Barry Kelly的建议,我扩展了我的帖子,包括幻灯片中的一些信息。 - Tim
3
当我看到“文档要虔诚地执行”这句话时,我不禁想起使用安条克圣手榴弹的指令。 - T.E.D.
2
圣斯托曼高举着API,说道:“哦,主啊,请保佑这个API,让它能够与你的操作系统进行接口交互……” - T.E.D.
"最小化可变性" -- 我真希望微软能够学会这一点。 - Robert Fraser

44

您不必阅读文档就能正确使用它。

一个出色API的标志。


1
你怎么能这么说呢?想象一下,如果有一个客户要求你使用特定的API来完成某个任务。首先,你需要知道这个任务是什么,然后你需要学习这个API。如果你没有代码访问权限,那么它怎么能自我解释呢? - Filip Ekberg
@Quarrelsome 你能举个例子吗? - Tim
3
极简 API - 意思是没有混淆需要调用哪些方法。良好的命名使得易于找到所需的方法。异常处理机制可以纠正错误的 API 使用方式,等等。 - Quibblesome
2
没错。使用 SQLite ADO Wrapper 2.0 就是一个例子,通过看类、方法或异常的名称,你几乎可以猜到(正确地)会发生什么。 - Manuel Ferreria
3
我也见过一些非常出色的API,它们基本上是自文档化的,这是因为它们有非常好的设计、恰当的命名和简洁的注释/描述。一个简单的头文件突然变得就是你所需要的一切,这真是令人着迷。 - none

14

很多编码规范、更长的文档甚至书籍(框架设计指南)都对这个主题进行了讨论,但大部分仅适用于较低级别。

同时也有品味这一方面。即使API遵循任何规则,仍然可能糟糕,因为过分奉承各种时髦思想。最近的罪魁祸首是模式导向,其中单例模式(不过是初始化全局变量)和工厂模式(一种参数化构建的方式,但常常被滥用)被过度使用。近来,更可能的是控制反转(IoC)及其相关的大量接口类型数量爆炸会给设计增加多余的概念复杂性。

品味的最佳老师是模仿(阅读大量代码和API,找出有效或无效的方法),经验(犯错并从中学习)以及思考(不要只是追随潮流,三思而后行)。


12
  • 有用 - 它解决了尚未满足的需求(或改进了现有需求)
  • 易于解释 - 基本理解它所做的事情应该很容易掌握
  • 遵循某些问题域或现实世界的对象模型。使用有意义的结构
  • 正确使用同步和异步调用。(不要为需要时间的事情阻塞)
  • 良好的默认行为 - 在可能的情况下允许可扩展性和调整,但为简单情况提供必要的默认值
  • 示例用途和工作样例应用程序。这可能是最重要的。
  • 优秀的文档
  • 自己使用(如果适用)
  • 保持小型或将其分段,以便它不是一个巨大的污染空间。保持功能集明确且隔离,几乎没有依赖关系。

还有更多,但这是一个好的开始


7
一个好的API应该与其描述的事物的语义模型非常接近。
例如,用于创建和操作Excel电子表格的API将具有类似WorkbookSheetCell的类,以及像Cell.SetValue(text)Workbook.listSheets()这样的方法。

良好的API应保留领域概念(即工作簿、工作表等)和架构意图。此外,良好的API还将遵循系统的概念完整性。 - j4zzcat

7
一个好的API应该能让客户端做他们需要做的几乎所有事情,但不需要他们去做很多毫无意义的繁琐工作。这些“毫无意义的繁琐工作”的示例包括初始化数据结构字段、按照永远不变的顺序调用多个例程,并没有真正的自定义代码等。
一个坏的API最可靠的标志是,如果你的客户都想要用自己的辅助代码来包装它。至少,你的API应该提供这种辅助代码。很可能,它应该被设计成为提供客户正在自行实现的更高级别的抽象化。

3

一个糟糕的API是未被预定受众使用的API。

一个优秀的API是被预定受众用于为其设计目的服务的API。

一个伟大的API是被其预定受众用于其预定目的,同时也被意外的受众因为设计者未曾预料到的原因所使用的API。

如果亚马逊将其API发布成SOAP和REST两种版本,而REST版本获胜,并不意味着底层的SOAP API就是糟糕的。

我想你也会有同样的经历。你可以尽情阅读关于设计的内容,并尝试做到最好,但检验一切的标准是实际运用。花些时间建立获取反馈的方式,了解哪些部分有效,哪些部分无效,并随时准备进行重构以使其更好。


2
一个好的API应该让简单的事情变得更简单(最少的样板代码和学习曲线来完成最常见的任务),并且让复杂的事情变得可能(最大的灵活性,尽量少的假设)。一个普通的API只有其中之一表现得很好(要么极其简单,但只适用于基本操作;要么功能非常强大,但是需要非常陡峭的学习曲线等等)。一个糟糕的API既不能让简单的事情变得简单,也不能让复杂的事情变得可能。

2

Trolltech的链接已经失效了 - Internet Archive版本为https://web.archive.org/web/20090520234149/http://chaos.troll.no/~shausman/api-design/api-design.pdf和https://web.archive.org/web/20120814061212/http://doc.trolltech.com/qq/qq13-apis.html。 - Holly

1
如果API产生错误消息,请确保该消息和诊断信息能够帮助开发人员找出问题所在。
我期望API的调用者传入正确的输入。开发人员是API产生的任何错误消息的消费者(而不是最终用户),针对开发人员的消息有助于开发人员调试其调用程序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接