概述

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) {
List<EsCondition> result = new ArrayList();
result.add(new EsCondition(TYPE_ENTRANCE, Op.eq, "wsc"));
result.add(new EsCondition(TYPE_PLATFORM, Op.eq, "wx"));
return result;
}

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 ,需要掌握一些技巧:工厂方法、重载方法、常用操作等。

从工作中不断发现需要优化的地方,掌握方法和技巧去解决, 也是一种提升技能的方式。

最新文章

  1. Qt on Android:创建可伸缩界面
  2. mysql 新建用户、授权、远程访问
  3. java代码封装与编译
  4. ASP.NET Core 源码阅读笔记(2) ---Microsoft.Extensions.DependencyInjection生命周期管理
  5. springday02-go2
  6. mockjs学习总结(方便前端模拟数据,加快开发效率)
  7. C#编写windows服务程序
  8. PS Studio打包程序 .net版本依赖
  9. Git 基础再学习之:git checkout -- file
  10. mybatisnet轻量级ORM框架
  11. MSSQL 当前会话设置隔离级别与查询
  12. 利用json获取天气信息
  13. ionic的弹出框$ionicPopover
  14. ubuntu字符界面怎么设置中文显示和中文输入
  15. 自古枪兵幸运E
  16. mysql 连接 的drive 不一样
  17. sqlmap 使用总结
  18. BZOJ 4552 [Tjoi2016&amp;Heoi2016]排序 | 二分答案 线段树
  19. 深入理解 MySQL ——锁、事务与并发控制
  20. python远程操作服务器

热门文章

  1. 安装python 第三方库(whl,py格式)
  2. Ubuntu下LAMP的环境配置教程
  3. 常见CSS3选择器和文本字体样式汇总
  4. pycharm中新建Vue项目时没有vue.js的解决办法
  5. Centos中Redis的下载编译与安装(超详细)
  6. centos7添加网卡
  7. 折腾vue--环境搭建(一)
  8. go笔记--几个例子理解context的作用
  9. selenium实现网易邮箱的登录注册
  10. 「BZOJ3065」带插入区间K小值 [分块]