如何设计接口的粒度

390 查看

场景

场景一

用户基本信息的展示.产品需要有3个页面,分别面向的是消费者,运营,商家。所以3个页面的内容不是完全一样的,并且有些内容是敏感的,但是他们的数据源是一致的。现在一共有四种解决方案:

  • 分别写三个DAO,根据页面需求返回不同的数据

  • 分别写三个service,调用的是同一个DAO,在service中根据页面需求进行裁剪

  • 分别写三个组合层的接口,组合层调用的是同一个service,然后在组合层根据页面需求进行裁剪

  • 根据页面需求在controller层对字段进行裁剪,调用的是同一个组合层

处理方式:在这个场景中,我一开始选择的处理方式是第三种,当时是基于公有转换代码的重复利用。但是站在抽象层次和接口的复用率角度应该第四种才是最合理的

场景二

有两个接口ServiceA.methodA()ServiceA.methodB(),现在在组合层需要同时调用methodA()methodB()来组装成一个新的实体。有两种方案可以解决:

  • ServiceA中重新写一个方法methodC()给组合层使用

  • 在组合层自己写私有方法或者Utils方法去组装实体

场景三

电商系统商品详情页,有放入购物车和直接购买两个选项,直接购买选项也会放入购物车,但是和实际上的购物车是分离的。实现方式有三种:

  • 前台通过传递一个时间戳t来标示是放入购物车还是直接购买,如果t=0就是放入购物车,如果是t!=0就是直接购买。购物车相对应也有两个接口Service.MethodA(),Service.MethodB(long t),分别对应放入购物车和直接购买(这里的t对购物车模块有特殊作用)

  • 购物车值提供一个接口Service.Method(long t),在购物车模块中根据t自己去判断是直接购买还是放入购物车

  • 前台传一个标示flag表示是否直接购买,购物车提供两个接口Service.MethodA(),Service.MethodB()(这里去除了购物车模块对时间戳t的依赖)

场景四

一个Service.Method()返回了一个A实体.现在有新的需求发现A实体返回的字段太少了,得多加一个字段。这个时候有两种处理方式:

  • 直接在A实体中添加

  • 重新写一个接口返回的是实体B,BA要多一些字段

总结

场景一和场景四代表的是各个层次如何定义自己的接口,以及是否需要定义一些特殊的接口,目前感觉:在开发之前需要评估一下每个接口的调用频率,是否需要定义一些单独的返回接口(除非明显感觉有性能问题,一些接口可能返回一些大字段,而这个大字段在很多场景下是用不到的,或者因为接口需要返回一个字段而需要做很多消耗性能的工作,或者接口返回50个字段,而我只需要一个字段,并且这种场景很多。这些可以单独定义一些特殊接口),而其它的可以共用的接口尽量共用,当后期在线上运行的时候发现某个调用确实因为接口太粗而导致了性能问题,那么可以重新拎出一个接口。

场景二代表的是,一开始定义的接口粒度很小,但是后期需求发现上层调用需要用到一个粒度更加大的接口,这个时候需要分析这种场景是否很多,如果发现很多,就可以单独为其开一个新的接口,新的接口调用内部的粒度小的接口来组合成一个新的返回实体(假设这些实体分属不同的表),这种相当于在原来的层次中间加了一层层次更加高的抽象,如果以后这种越来场景越多(小粒度组成大粒度),这层就会变得越来越明显。

场景三,表示的是模块需要给上层提供什么样子的接口,一些模块内部的信息或者其它东西就不要暴露给上层调用者,比如方法一合方法三的不同之处就是是否需要知道内部的Key是要用时间戳来表示的,所以在设计接口的时候需要同时站在使用者的角度和设计者的角度考虑,尽量不要把内部的实现细节暴露给使用者(比如上述的例子,如果以后不是以时间戳来当key的话,那么这个接口传入的时间戳就显得毫无意义的),当然也不是一定要遵循这个原则,当接口由于你传入一个参数而导致性能出现了质的飞跃,那么这些反设计的接口还是允许存在的