如无必要,勿增实体。成功的系统不是有特别闪光的地方,而是实践时点点滴滴的积累。
最近做 Code Review,有些人的代码看不下去,有些人的代码看起来很规整,写的也算用心,但总无法开心或兴奋起来。
我在毕业的前 6 年喜欢欣赏各种代码,Github 上 Star 了近 2000 个仓库,各种标准、语言设计规范会读一读,深度上没有太多精进,只学其形。身在后端,却总窥探 GUI,当时学习了 F3 语言,Form Follow Function,现在竟又关心起形式。
关于如何做好 API 设计,我觉得就两点:
- 在意 API 的用户(使用者、维护者)
- 抄袭,大量的借鉴。(JDK、JSR、Spring、Open XX API、CNCF 各种主流的框架都可以抄袭,但不只看一个)
Java API 设计的细节
这里以提供第三方 Jar 包为例
依赖
- 尽量减少依赖,对一些公共的依赖,使用较少,看能否 Copy。
- 依赖接口而非实现,比如有些人把 logback 放了进去。
- Maven 的 POM 中传递依赖尽可能排除。
- 不强加主观的依赖,比如某些业界标准 Jar,非常值得推广也要克制。
- 公司范围内达成共识的依赖可以适当放宽。
命名
命名,最能提升软件的可理解性。随着对软件认知的加深,不停修正 API 的名字也是一个求真的过程。但是命名存在兼容性问题,所以命名更加不能随意。至少在形上:
- Package 名称不要层次太深,如果大业务团队为一级,那提供的模块通常可以是二级。
- 除了 Annotation 相关文件可以用 annotation 的包名,像 enum 什么的就别加 enum 包后缀了,不需要单独的包。
- 减少 common 包的出现。 更要减少 Common 类的出现,是抽象的偷懒。
- Interface 类不要加 I,枚举不要加 Enum 后缀,抽象类可以加 Abstract 前缀。
- 很多 BaseObject 看看能不能具体一点,比如 Auditable、Named。
- 类可以考虑以资源的方式定义,名称多 Translate 翻译,尽量准确。
- 操作方法比如 save 就别类似 saveUser 了。看 Spring Repository 的 save 不会是 saveEntity,参数中就已经显得啰嗦了。
- 减少 Util 类的出现。
- 减少大 Constant 类的出现,可以没有。
- Exception 有个模块级别的总异常,对外抛出的应该是它或它的自异常。异常的名字不要缩写成 Exp,一点都不美,也不好猜。看了那么多的类库都是 Exception 后缀。
扩展性
还是抄袭吧,比如分页对象,大家都喜欢 Copy 自己的工具箱,我看了很多分页类,Spring data-common 中分页相关对象的定义就可以作为终态,按需抽象最粗的结构自用,命名也可以直接用了。
- 按需抽象,不要终态,随软件生长。
- 变量尽量隐藏,有 set 未必有暴露 get,相反也成立。
错误码
关于错误码要少的问题,但目前看来,统一的一个错误枚举类,好像给问题定位或提示提供了方便。
设计准则
- 提供清晰的思维模型
- 简单(也不能过于简单)
- 容许多个实现
最佳实践
- 学习优秀的 API 设计
- 写详细的文档并保持更新。
- 选择资源+操作的方式定义 API,因此要谨慎定义资源。
- 选择合适的抽象层。
- 资源的标识,是面向用户还是面向结构。
- 对于相应资源,什么操作是合理的。
- 操纵幂等,部分操作适当控制。
- 兼容性
- 批量更新,尽量交给客户端,便于扩展,保持一致性。
- Error Code 要克制,这一点看过很多 Open API 的设计有提到。
参考
- 谷朴的 《API 设计最佳实践的思考》。本文是他的读后笔记。
- Azure Web API Design https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design
- OPEN API Design https://www.openapis.org/