先把 Joshua Bloch 大神的 API PDF 放在这里膜拜下:“How to Design a Good API and Why it Matters.pdf”
前端
在设计和实现通用订单搜索API的过程当中,收获了一点关于API设计的得与失。总结下,但愿能给后面的工做带来有益的帮助。
java
什么是好的API ?框架
简洁、清晰、易懂、易使用。 语义行为与选项分离。
ui
得:搜索引擎
从交易订单搜索能力化角度思考API,充分考虑了组合性,作到足够灵活,同时兼顾可读性。可以在较少改动和发布 trade-manage 的基础上,实现多样化的需求。
设计
失:code
在过于注重灵活性功能的状况下,有一些地方作的不够好,给使用方带来不少困扰。对象
本文主要讨论其得与失,但愿可以给后来者带来有益的启发。
blog
强调从能力的角度而不是业务场景角度来思考API设计,考虑足够灵活性。 好比继承
定义交易订单搜索能力须要的参数,而不是依赖页面传过来的参数。经过定义的参数来组合搜索能力。
虽然搜索页面只须要传一个订单类型,可是后台容许传多个订单类型,为后续扩展性留下空间。
基于自身定义的订单搜索参数,后台实现也很是简洁,而不须要作各类参数的解析和适配。对于搜索能力的稳定性和减小BUG的发生,是很是有益的。
一体两面,总有正面和反面的地方。因为通用订单搜索API 特别强调基础层的能力化和稳定性,在基础层与业务层之间缺少一道友好的适配层,给业务方使用也带来了很多曲折。
其实通用订单搜索API 的文档也作了很多工做。 接口入参 ,代码示例, 搜索入参的分类,包括代码里 java doc 其实也写得很清楚。 为何业务方还来咨询呢? 一个缘由,确实 API 设计有失当之处(考虑了最主要的PC列表页面搜索场景),对于业务方的简单需求(好比按照下单人ID)显得过于复杂了; 另外一个缘由,我认为开发人员对于陌生事物仍是缺少必要的耐心。固然我其实也有这个毛病。 若是说文档没有,代码里 java doc API 也缺失,过来咨询是没问题的,可是文档 ,javadoc 注释写的很明白了,花2分钟稍微扫一下,可以学到更多东西,就不会为了一个小需求来咨询了。
可是文档确实也有写的不够的地方。
好比返回的错误信息,文档没有写明。因而联调的时候,业务方遇到“非法的调用源”,一脸懵逼。须要有一些错误提示文档 ; 好比业务方遇到其余的错误,也不知道是什么缘由,只能找咱们来查。
好比没有突出经常使用的须要的东西。每每业务方内部依赖订单搜索列表的时候,基于应用场景,可能更多依赖于 订单状态,订单类型,粉丝ID,下单人ID,维权状态, 这些能够单独抽离一个更 brief 的订单搜索接口,并辅以文档,也许就不会遭遇不知道用哪一个参数的问题了。 换句话说, 原来过于依赖能力化的角度思考接口,缺失的一块是, 基于应用场景的角度来思考接口。
先说下背景。当时是由于老搜索接口不太灵活,且参数与前端页面耦合比较紧,所以决定设计一个新的通用订单搜索接口,以提供更加灵活的搜索能力。
第一个失,就是不加思索地把老搜索接口里的参数所有拷贝过来。 好比说 毫无卵用的 订单标记,被废弃还让业务方传错的 pageSize, pageNo 。
经验: 因为新接口上线和接入一般有一个过程,其实没必要要当即支持全部可能的搜索请求,而是先支持最经常使用的部分,而后根据状况扩展能力。 API 参数一般是越严格越少更好,公开了就收不回来了。
设计新接口入参时,能够参考老接口入参,但必定要慎之又慎,只取必需的,严格把控每一个参数的必要性。
参数必定要仅仅针对接口服务而言,不要为了图方便把全部入参都混杂到一个类里,致使API语义不清,给业务方使用带来困惑。
第二个失,过于追求复用代码和语义分离,继承层次比较深。
追求代码复用是好习惯,但不能过分。尤为 API 设计,应该以“清晰易使用”为重。 以下是个反例:
经验: 继承通常是为了实现不一样的语义分离,好比基础通用参数与业务参数, 但强烈建议最多两层,继承层次毫不要超过两层。
public class PaginationParam extends BaseParam { } public class GeneralOrderSearchParam extends PaginationParam { protected String index = "trade_es_index"; /** * 订单查询对象 */ protected OrderSearchParam orderSearchParam; // ... } public class OrderSearchParam extends BaseParam { }
第三个失,没有充分考虑业务方的使用体验。
层次感不强
全部的搜索参数都平铺到一个 OrderSearchParam 的字段里,没有层次感, 业务方每每只要根据一个字段搜索,却要在繁多的搜索字段里搜索。
构造入参不方便
缺少方便的构造搜索入参的方法,业务方须要写很无聊的 set , set , set 来构造搜索入参,代码会比较难看。 能够考虑使用 Builder 模式来构造经常使用参数。这个方法也能够解决 层次感不强 的问题。
经验: 应该对搜索字段进行区分对待,重点突出, 能够将最经常使用的搜索字段聚合成一个子搜索对象,有关联语义的搜索字段聚合成一个子搜索对象。 让业务方可以快速找到所须要的。经过提供易懂的 API 示例,也能够帮助业务方更好地使用 API。
经常使用搜索重复构建
因为设计参数时,参数取值过于原子化,强调灵活性与可组合性,须要业务方根据搜索场景自行组装参数,当多个业务方来对接某个经常使用搜索场景时,每一个业务方都须要写相同的代码。
if (someCond) { setOrderTypeDesc(Arrays.asList(xEnum.name(),yEnum.name(),zEnum.name())) }
若是后续设计有变动,每一个业务方都必须修改一遍。所以,更好的作法是,若组合语义占了经常使用80%的情形,那么应该清晰定义这些组合语义,内部作映射消化掉,而不是委托给外部。
为了图方便,直接在原来的参数里加字段,而不是创造新的参数对象,致使原有的参数对象混杂不一样的功能,给业务方使用带来困惑。
好比原来有个实时详情接口的入参 OrderQueryOptions , 非实时详情接口,多了个 withDeliveryInfo 可选参数,虽然放在原来的参数对象会省很多事,但是会让接口语义不清。对应实时接口来讲,它根本不须要这个参数;对于非实时接口,又混杂了实时接口的入参。 两边都不讨好。
经验: 严格对接口入参进行控制,与接口服务无关的参数不容许加入。实现细节相关的东西不加入API,不公开。
为了具备更好的可扩展能力,减小发布,设计了一个入参 extendKeywords 。当要搜索的字段不在给定搜索入参时,能够直接指定 ES 里对应的字段及操做符来搜索。 虽然灵活了些,却将业务方与底层实现牢牢绑在一块儿。若是之后要改用其余的搜索引擎,这种就是很是难以摆脱的困扰了。对于API设计与实现来讲,都是很差的决策。
API 是服务提供的接口抽象。 一旦公开,收回来会很是困难并且费力。所以,设计API 要精思细虑,严格把关每一个入参字段、继承层次不要超过两次、充分考虑使用者体验、避免参数混杂、避免暴漏实现细节等问题。
API 好书推荐:《软件框架设计的艺术》,英文书名是:《 Practical API Design: Confessions of a Java Framework Architect 》