如无必要,勿增实体。成功的系统不是有特别闪光的地方,而是实践时点点滴滴的积累。

最近做 Code Review,有些人的代码看不下去,有些人的代码看起来很规整,写的也算用心,但总无法开心或兴奋起来。

我在毕业的前 6 年喜欢欣赏各种代码,Github 上 Star 了近 2000 个仓库,各种标准、语言设计规范会读一读,深度上没有太多精进,只学其形。身在后端,却总窥探 GUI,当时学习了 F3 语言,Form Follow Function,现在竟又关心起形式。

关于如何做好 API 设计,我觉得就两点:

  1. 在意 API 的用户(使用者、维护者)
  2. 抄袭,大量的借鉴。(JDK、JSR、Spring、Open XX API、CNCF 各种主流的框架都可以抄袭,但不只看一个)

Java API 设计的细节

这里以提供第三方 Jar 包为例

依赖

  1. 尽量减少依赖,对一些公共的依赖,使用较少,看能否 Copy。
  2. 依赖接口而非实现,比如有些人把 logback 放了进去。
  3. Maven 的 POM 中传递依赖尽可能排除。
  4. 不强加主观的依赖,比如某些业界标准 Jar,非常值得推广也要克制。
  5. 公司范围内达成共识的依赖可以适当放宽。

命名

命名,最能提升软件的可理解性。随着对软件认知的加深,不停修正 API 的名字也是一个求真的过程。但是命名存在兼容性问题,所以命名更加不能随意。至少在形上:

  1. Package 名称不要层次太深,如果大业务团队为一级,那提供的模块通常可以是二级。
  2. 除了 Annotation 相关文件可以用 annotation 的包名,像 enum 什么的就别加 enum 包后缀了,不需要单独的包。
  3. 减少 common 包的出现。 更要减少 Common 类的出现,是抽象的偷懒。
  4. Interface 类不要加 I,枚举不要加 Enum 后缀,抽象类可以加 Abstract 前缀。
  5. 很多 BaseObject 看看能不能具体一点,比如 Auditable、Named。
  6. 类可以考虑以资源的方式定义,名称多 Translate 翻译,尽量准确。
  7. 操作方法比如 save 就别类似 saveUser 了。看 Spring Repository 的 save 不会是 saveEntity,参数中就已经显得啰嗦了。
  8. 减少 Util 类的出现。
  9. 减少大 Constant 类的出现,可以没有。
  10. Exception 有个模块级别的总异常,对外抛出的应该是它或它的自异常。异常的名字不要缩写成 Exp,一点都不美,也不好猜。看了那么多的类库都是 Exception 后缀。

扩展性

还是抄袭吧,比如分页对象,大家都喜欢 Copy 自己的工具箱,我看了很多分页类,Spring data-common 中分页相关对象的定义就可以作为终态,按需抽象最粗的结构自用,命名也可以直接用了。

  1. 按需抽象,不要终态,随软件生长。
  2. 变量尽量隐藏,有 set 未必有暴露 get,相反也成立。

错误码

关于错误码要少的问题,但目前看来,统一的一个错误枚举类,好像给问题定位或提示提供了方便。

设计准则

  1. 提供清晰的思维模型
  2. 简单(也不能过于简单)
  3. 容许多个实现

image.png

最佳实践

  1. 学习优秀的 API 设计
  2. 写详细的文档并保持更新。
  3. 选择资源+操作的方式定义 API,因此要谨慎定义资源。
  4. 选择合适的抽象层。
  5. 资源的标识,是面向用户还是面向结构。
  6. 对于相应资源,什么操作是合理的。
  7. 操纵幂等,部分操作适当控制。
  8. 兼容性
  9. 批量更新,尽量交给客户端,便于扩展,保持一致性。
  10. Error Code 要克制,这一点看过很多 Open API 的设计有提到。

参考

  1. 谷朴的 《API 设计最佳实践的思考》。本文是他的读后笔记。
  2. Azure Web API Design https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design
  3. OPEN API Design https://www.openapis.org/