zoukankan      html  css  js  c++  java
  • 通用订单搜索的API设计得失录

    先把 Joshua Bloch 大神的 API PDF 放在这里膜拜下:“How to Design a Good API and Why it Matters.pdf”

    总述###

    在设计和实现通用订单搜索API的过程中,收获了一点关于API设计的得与失。总结下,希望能给后面的工作带来有益的帮助。

    什么是好的API ?

    简洁、清晰、易懂、易使用。 语义行为与选项分离。

    • Easy to learn
    • Easy to use, even without documentation
    • Hard to misuse
    • Easy to read and maintain code that uses it
    • Sufficiently powerful to satisfy requirements
    • Easy to extend
    • Appropriate to audience

    得:

    从交易订单搜索能力化角度思考API,充分考虑了组合性,做到足够灵活,同时兼顾可读性。能够在较少改动和发布 trade-manage 的基础上,实现多样化的需求。

    失:

    在过于注重灵活性功能的情况下,有一些地方做的不够好,给使用方带来很多困扰。

    本文主要讨论其得与失,希望能够给后来者带来有益的启发。

    好的地方###

    能力化####

    强调从能力的角度而不是业务场景角度来思考API设计,考虑足够灵活性。 比如

    1. 定义交易订单搜索能力需要的参数,而不是依赖页面传过来的参数。通过定义的参数来组合搜索能力。

    2. 虽然搜索页面只需要传一个订单类型,但是后台允许传多个订单类型,为后续扩展性留下空间。

    实现清晰####

    基于自身定义的订单搜索参数,后台实现也非常简洁,而不需要做各种参数的解析和适配。对于搜索能力的稳定性和减少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 》


  • 相关阅读:
    Hadoop 解除 “Name node is in safe mode”
    ubuntu永久修改主机名
    ssh免密码登录
    su 和 su- 会影响环境变量
    卸载ubuntu自带openJDK,更改成自己的JDK版本
    ubuntu安装jdk 1.6
    147.Insertion Sort List
    145.Binary Tree Postorder Traversal
    144.Binary Tree Preorder Traversal
    143.Reorder List
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/9902305.html
Copyright © 2011-2022 走看看