64

背景:

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

我们得到的一个非常糟糕的 API 函数的一个很好的例子是public static void Select(IList checkRead, IList checkWrite, IList checkError, int microseconds);C# 中的套接字。该函数接收 3 个套接字列表,并销毁它们,使用户必须克隆所有套接字,然后再将它们送入Select(). 它还有一个超时(以微秒为单位),它是一个 int,它设置服务器可以等待套接字的最长时间。这个限制是 +/-35 分钟(因为它是一个整数)。


问题:

  1. 您如何将 API 定义为“坏”?
  2. 您如何将 API 定义为“好”?

需要考虑的要点:

  • 难以记住的函数名称。
  • 难以理解的函数参数。
  • 糟糕的文档。
  • 一切都如此相互关联,以至于如果您需要更改 1 行代码,您实际上需要在其他地方更改数百行代码。
  • 破坏其参数的函数。
  • 由于“隐藏”的复杂性,可扩展性差。
  • 用户/开发人员需要围绕 API 构建包装器,以便可以使用它。
4

13 回答 13

108

在 API 设计中,我一直觉得这个主题演讲非常有帮助:
如何设计一个好的 API 以及为什么它很重要——作者 Joshua Bloch

这是摘录,我建议阅读整篇文章/观看视频。

二、一般原则

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

三、班级设计

  • 最小化可变性
  • 仅在有意义的地方子类
  • 设计和文档继承或禁止它

四。方法设计

  • 不要让客户端做模块可以做的任何事情
  • 不要违反最小惊讶原则
  • 快速失败 - 错误发生后尽快报告
  • 提供对字符串形式的所有可用数据的编程访问
  • 小心超载
  • 使用适当的参数和返回类型
  • 跨方法使用一致的参数排序
  • 避免长参数列表
  • 避免需要异常处理的返回值
于 2009-01-22T13:52:17.767 回答
44

您无需阅读文档即可正确使用它。

一个很棒的 AP​​I 的标志。

于 2009-01-22T14:12:12.067 回答
14

许多编码标准和更长的文档甚至书籍(框架设计指南)都已针对此主题编写,但其中大部分仅在相当低的水平上有所帮助。

还有一个口味问题。API 可能会遵守任何规则手册中的每条规则,但仍然很糟糕,因为它们盲目地坚持各种流行的意识形态。最近的一个罪魁祸首是面向模式,其中单例模式(比初始化的全局变量多一点)和工厂模式(一种参数化构造的方式,但通常在不需要时实现)被过度使用。最近,控制反转 (IoC) 和相关的微型接口类型数量的激增更有可能为设计增加了冗余的概念复杂性。

品味的最佳导师是模仿(阅读大量代码和 API,找出有效和无效的方法)、经验(犯错误并从中学习)和思考(不要只为时尚而做,三思而后行)。

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

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

于 2009-01-22T13:48:38.947 回答
7

一个好的 API 有一个与它所描述的事物相近的语义模型。

例如,用于创建和操作 Excel 电子表格的 API 将具有 、 和 之类的类WorkbookSheet以及 和之类的Cell方法。Cell.SetValue(text)Workbook.listSheets()

于 2009-01-22T14:12:07.900 回答
7

一个好的 API 可以让客户做他们需要做的几乎所有事情,但不需要他们做很多盲目的忙碌工作。“无心的忙碌工作”的例子是初始化数据结构字段,以从不改变的顺序调用多个例程,中间没有真正的自定义代码,等等。

一个糟糕的 API 最可靠的迹象是,如果你的客户都想用他们自己的帮助代码包装它。至少,您的 API 应该提供该帮助程序代码。最有可能的是,它应该被设计为提供更高级别的抽象,客户每次都自行滚动。

于 2009-01-22T15:32:08.083 回答
3

糟糕的 API 是其目标受众未使用的 API。

一个好的 API 是由其目标受众用于其设计目的的 API。

一个优秀的 API 既可以被其预期受众用于其预期目的,也可以被非预期受众用于其设计者未预料到的原因。

如果亚马逊将其 API 发布为 SOAP 和 REST,并且 REST 版本胜出,这并不意味着底层 SOAP API 不好。

我想你也一样。您可以阅读有关设计的所有内容并尽力而为,但将使用酸性测试。花一些时间建立方法以获得关于哪些有效和哪些无效的反馈,并准备好根据需要进行重构以使其更好。

于 2009-01-22T14:59:25.060 回答
2

一个好的 API 可以使简单的事情变得简单(做最常见的事情的最小样板和学习曲线)和可能的复杂事情(最大的灵活性,尽可能少的假设)。一个平庸的 API 可以很好地完成其中之一(要么非常简单,但前提是您尝试做非常基本的事情,或者非常强大,但学习曲线非常陡峭,等等)。一个糟糕的 API 就是不能很好地完成这两个方面的事情。

于 2009-06-05T18:01:34.317 回答
2

已经有其他几个很好的答案了,所以我想我只是添加一些我没有看到提到的链接。

文章

  1. 奇趣科技的 Jasmin Blanchette 撰写的“API 设计小手册”
  2. “定义 QT 风格的 C++ API”也是奇趣科技

图书:

  1. Joshua Bloch 的《有效的 Java》
  2. Kernighan 和 Pike 的“编程实践”
于 2009-06-05T18:14:00.787 回答
1

我认为一个好的 API 应该允许自定义 IO 和内存管理钩子(如果适用的话)。

一个典型的例子是,您在磁盘上有自定义压缩归档格式的数据,而具有不良 api 的第三方库想要访问磁盘上的数据,并希望有一个可以加载其数据的文件的路径。

这个链接有一些好处: http: //gamearchitect.net/2008/09/19/good-middleware/

于 2009-01-22T14:41:11.337 回答
1

如果 API 产生错误消息,请确保消息和诊断有助于开发人员找出问题所在。

我的期望是 API 的调用者传入正确的输入。开发人员是 API 产生的任何错误消息的消费者(而不是最终用户),针对开发人员的消息帮助开发人员调试他们的调用程序。

于 2009-02-05T01:37:02.677 回答
0

当 API记录不充分时,它就是不好的

当 API 有良好的文档并遵循编码标准时,它就是好的

现在这是两个非常简单但也非常难遵循的点,这将一个带入软件架构领域。您需要一个优秀的架构师来构建系统并帮助框架遵循自己的指导方针。

注释代码,为 API 编写解释清楚的手册是强制性的。

如果一个 API 有一个很好的文档来解释如何使用它,它就会很好。但是,如果代码是干净的、良好的并且本身遵循一个标准,那么它是否有一个像样的文档并不重要。

我在这里写了一些关于编码结构的文章

于 2009-01-22T14:01:21.963 回答
0

我认为最重要的是可读性,我的意思是让大多数程序员在尽可能短的时间内理解代码在做什么的质量。但是判断哪个软件可读,哪个不可读具有难以形容的人性:模糊性。您提到的几点确实部分成功地使其具体化。但是,总体而言,它必须保持逐案处理,并且很难提出普遍的规则。

于 2009-01-22T14:01:37.703 回答