概述
API 是一个服务的门面, 就像衣装是人的形象一样.
优雅的 API 设计, 能让业务方使用起来倍儿爽, 提升开发效率, 降低维护成本; 糟糕的 API 设计, 则让业务方遭心, 陷入混沌.
本文将展示一个扩展搜索 API 的优化过程, 从中也可以学到一些东西.
现状
找一个上游工程的扩展搜索代码如下:
- extendKeywords.add((EsCondition) ConditionFactory.in("order_tags", Arrays.asList("IS_XXX_ORDER")));
- extendKeywords.add(new EsCondition("goods_title", Op.match, new Match(goodsTitle, "100%")));
啊啊, 真是丑死了 ! 为什么呢 ?
强制类型转换. 让业务方写强制类型转换, 简直是让业务方来遭罪的 ! 这 API 设计简直了 !
暴漏底层细节. 让业务方 new EsCondition , 不仅是暴漏底层细节, 还很难看!
不方便的传值. 为了传一个 in 的数值, 需要写个 Arrays.asList(e) !
遍布的 EsCondition . 由于 API 设计的很裸, 导致上游工程到处弥漫着 EsCondition 的烟雾.
emmm... 其实是我设计的 API 造的孽 ! 解铃还须系铃人.
优化过程
工厂方法
这种硬的 new EsConditon , 完全可以通过工厂方法和方法重载来消除. 此外, 为了收拢扩展搜索条件的构建, 可以构建一个专门的 ExtendSearchParam .
- public class ExtendSearchParam implements Serializable {
- private static final long serialVersionUID = 2824767430430079287L;
- private List<EsCondition> extendConditions = new ArrayList<>();
- public ExtendSearchParam addEq(String field, Object value) {
- extendConditions.add(new EsCondition(field, Op.eq, value));
- return this;
- }
- public ExtendSearchParam addIn(String field, Object... list) {
- extendConditions.add(new EsCondition(field, Op.in, list));
- return this;
- }
- public ExtendSearchParam addRange(String field, long gte, long lte) {
- extendConditions.add(new EsCondition(field, Op.range, new Range(gte, lte)));
- return this;
- }
- public ExtendSearchParam addMatch(String field, String query) {
- extendConditions.add(new EsCondition(field, Op.match, new Match(query, "100%")));
- return this;
- }
- public ExtendSearchParam addMatch(String field, String query, String match) {
- extendConditions.add(new EsCondition(field, Op.match, new Match(query, match)));
- return this;
- }
- }
这里借鉴了 Builder 模式, 能够链式地构建扩展搜索条件. 这样, 业务方就可以舒心地写上:
extendSearchParam.addIn(ORDER_TAGS, "IS_XXX_ORDER").addMatch("goods_title", goodsTitle);
没有了类型强制转换, 没有了暴漏底层细节, 没有了不方便的传值, 还可以一直 add 下去, 世界多美好 !
拦路虎
很快, 就遇到了拦路虎:
- private List<EsCondition> dealOrderSourceCondition(String orderSource) {
- // 判空处理
- if (WXWSC.getCode().equals(orderSource)) {
- List<EsCondition> wxWscList = Lists.newArrayList();
- wxWscList.add(new EsCondition(TYPE_ENTRANCE, Op.eq, WXWSC.getValue()));
- wxWscList.add(new EsCondition(TYPE_PLATFORM, Op.eq, PLATFORM_WX));
- return wxWscList;
- } else if (XCX.getCode().equals(orderSource)) {
- // ....
- }
- return null;
- }
emmm , 写这个方法的小伙伴也是好意, 封装一个方法来构建订单来源扩展条件也是好意. 不过这给 API 重构带来了一点点小小的障碍.
怎么重写这一段呢 ? 第一想到的是, 在 dealOrderSourceCondition 的方法里额外增加一个参数 ExtendSearchParam , 传进去, 修改它. 也能达到目地. 但是,-- 破坏了 "不可变" 原则. 不可变原则要求, 尽可能避免修改入参. 修改入参这种行为, 很容易导致不起眼的 BUG , 如果在关键流程中做这个事情, 有可能导致故障. 有线上教训的.
怎么办呢 ? 又不能修改 dealOrderSourceCondition 的入参, 又要把这个方法的扩展搜索条件合并到已有的扩展搜索对象中.
有一种办法 ! 合并扩展搜索对象 ExtendSearchParam . 这样, ExtendSearchParam 需要支持一个合并操作:
- public ExtendSearchParam merge(ExtendSearchParam extendSearchParam) {
- if (extendSearchParam != null && extendSearchParam.has()) {
- extendConditions.addAll(extendSearchParam.getUnmodifiedExtendSearch());
- }
- return extendSearchParam;
- }
这样, 将 dealOrderSourceCondition 的返回值改为 ExtendSearchParam 对象, 就能使用 merge 方法来合并扩展搜索条件了.
Yeap ! 想一想, 除了 合并操作, 还需要支持哪些操作呢 ?API 设计需要考虑周全, 可不能遇到一个问题加一个支持啊 !
辅助方法
为了与原来的 OrderSearchParam 联合使用, 需要加一些辅助方法, 比如:
- public boolean has() {
- return CollectionUtils.isNotEmpty(extendConditions);
- }
- public List<EsCondition> getUnmodifiedExtendSearch() {
- return Collections.unmodifiableList(extendConditions);
- }
小结
本文讲解了一个扩展搜索 API 的优化过程. 好的 API 设计能提升业务方的使用体验, 降低维护成本. 设计优雅的 API , 需要掌握一些技巧: 工厂方法, 重载方法, 常用操作等.
从工作中不断发现需要优化的地方, 掌握方法和技巧去解决, 也是一种提升技能的方式.
来源: http://www.bubuko.com/infodetail-3388478.html