优雅的API是清晰简洁的,就像少女的肌肤一样柔滑。
背景API 是软件应用向外部提供自身服务的一种形态和公开接口。就像一个人的着装打扮、举止言行、形象状态,是其内在的某种体现。很少有人能看到对方灵魂的内涵,但通过公共接口,可以略窥一二。
以前缺乏API设计的意识,没有经过仔细的思考,做出来的API够用,但比较粗糙。如果要重新设计API,会是怎样呢? 本文以导出API为例,试以阐释。 限于个人知识和经验,若有不对之处,欢迎指出 :)
好的API应该是怎样的呢?
参数要尽可能少,平铺形式,尽可能避免继承和嵌套(有些情况下例外);
若无法避免继承或嵌套,不要超过两层。
不要混杂与使用无关的东西。比如,不暴露任何实现细节;不暴露与功能无关的选项。
API 是给程序猿媛们使用的。因此容易理解和使用,也是建立在这个圈子,而不是给小白用户。 API 主要由接口签名、参数、返回值构成。 而参数和返回值,都是包含三要素:语义、名称、类型。
语义: 每个参数都必须有确定的语义。避免语义不明的参数。
名称: 尽量选择简单的单个通用单词和约定俗成的词语,望文知义。避免使用四级以上词汇。比如 维度 dimension, 来源 source, 业务类型 biz_type 都是可以接受的。 而 ValueSource,虽然也没问题,但含有两个单词;选项用 options 而不是 choose ;
类型。 尽可能用确定的类型,而不是包容性强的类型。比如传值的列表,List 而不是 String。序列化可以用框架搞定,而不是手工解析或者额外写代码。
API 必须完成其使命。 如果API 足够清晰简洁,却不能完成所需要的功能服务,那么是有缺失的。要实现灵活强大的API,必须遵循正交与组合的古老法则。
容纳 80% 的常用场景,但为所有场景留下空间。 比如通常只传一个订单类型,但有时需要多个怎么办?使用List 而非单个。
参数语义可组合。 参数语义没有隐式的耦合,可以灵活组合。
体验友好,有时与灵活强大是相矛盾的。想想GUI 和 CUI ,通常人们认为 GUI 的体验比 CUI 好得多,只有程序猿知道, CUI 在功能和效率上比 GUI 胜过不知多少倍。 因此,只能在两者之间做出合适的权衡。不过,仍然有一些办法,可以保证灵活强大的基础上,提供友好的使用体验。
使用的概念。通常,人们认为使用是指无意识的使用,无师自通的学会使用。实际上,使用包含了隐式的学习过程。使用与学习密切相关。怎么让用户使用更友好,某种意义上,是怎样让用户更容易学会。
符合习惯。参数命名与业界API保持一致性,符合习惯,更容易让程序猿媛上手。
工具方法。 如果有些参数(比如扩展参数、嵌套参数)设置起来很费力不直观,可以提供友好的工具类和工具方法,让使用者更容易。归根结底,是让使用者更容易地学会自定义的方式和方法。
链式调用。 含有多个参数时,可以提供链式调用,让使用者写起来更流畅。
设计好的API,需要考虑哪些因素呢?
核心。 考虑实现核心功能的必要参数。没有这些参数,就无法完成核心功能。比如导出实现中,必须先筛选出所需要的记录,筛选条件参数就是必要参数。
扩展。 获得功能的定制化结果所需要的参数。比如导出文件格式,导出维度、导出字段列表等。
外围。 为了更好地管理、联调、监控、统计、运维等。比如调用源、请求ID、业务类型、导出ID等。
下面,分别以退款导出、电子卡券导出、订单导出、通用导出为例,说明导出API的设计过程。
从最简单着手。 假设要做一个简单的退款导出,只有一个调用方。 API 应该是怎样的? 首先只从核心功能入手。
想象下,外部需要关心什么? emmm ... 如果不需要搜索什么,那么内部可以获取到所有信息,自己搞定一切,API 可以是无参的!
当然,现实没那么简单!
假设有多个调用方。通常需要使用基础参数,来记录导出的调用方等。
需要标识是什么业务方来调用。可以使用 source = 'xxx'。目前为止,一个就足够了。不要给调用方添加任何多余的负担。
返回值呢? 通常采用约定俗成的方式。 会有一个 XXXResult 类标识是否成功,错误码和错误消息之类。 为了避免阻塞,导出一般采用异步的实现。前端发送请求给后端,后端给前端一个简单的响应。待任务完成后,再行后面的事情。
这样, 最简单的退款导出API 如下所示: