一切抛开业务的设计,都是扯淡!在项目中有个我们会对多个接口进行多业务逻辑判断,项目开始工期紧,所以先以实现功能为主,最近打算优化一番,一开始的代码是这样的
public void checkProjectAdd(List<OrderDetail> list) { if (!CollectionUtils.isEmpty(list)) { orderService.check(list); stockService.check(list); orderAddress.check(list); userScoreService.check(list); activityService.check(list); this.check(); } excute(); }
虽然对检查类的方法做了简单的封装,但在好多地方可能会存在重复的判断,比如订单提交,加入购物车等等,冗余代码随处可见,且不易扩展。在此情形下,废话不多说,准备开干,先来分析分析业务,因为我们的各个检查接口的处理原则是要保证顺序性的,而在检查的过程中可能根据商品提交参数而实质去动态改变list参数中的内容,比如一个库存当两个卖,用户会员等级结合实际商品数判断能否享受折扣等等,所以每一级的检查或手动处理值都是要求检查类代码执行顺序,思前想后,想起曾在阅读mybatis源码时觉得其设计的plugins模式很适合此类场景,于是决定使用《责任链模式》来进行这一次代码的优化。
首先简单介绍一下什么是责任链,说到链,大家就能想到链表,这里我们只说最简单的链表(单向链表)
单向链表就是当前对象内存储着下一个对象的引用地址,责任链模式的概念也描述下:
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
概念都是标准,一般都很难理解是什么意思,我们拿图说事
这每一层都是大哥,想要跨过难关还是不太容易呀。但是没办法为了要干真正的实事还是要一关一关闯的,接下来我们要开始进行包装,AOP大家都知道(面向切面),既然我们最终的提交方法是excute,那切面就是excute了,在切面之前如何执行自定义的Interceptor(拦截器)呢?这里我是用的是jdk动态代理模式,动态代理,不太了解的看这里(JDK动态代理),使用动态代理的好处是我们可以对成百上千个已知与将来可能要编写的接口进行包装,比如我的订单提交事件叫做submitOrder,加入购车addCart,我都可以通过我的代理模式对这些接口进行包装,在代理模式中我规定好Interceptor,这样就能统一了
通过一级一级的代理模型,最终我们才能真正调用到IOrderService的具体实现上,废话不多说,开始撸代码
首先定义好我们自己拦截器类
public interface Interceptor { Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException; }
接下来代理工厂:
public class ProxyFactory implements InvocationHandler { private Object target; private Interceptor interceptor; private ProxyFactory(Object target, Interceptor interceptor) { this.target = target; this.interceptor = interceptor; } public static Object wrap(Object target, Interceptor filter) { ProxyFactory proxyFactory = new ProxyFactory(target, filter); return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyFactory); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Invocation invocation = new Invocation(target, method, args); return interceptor.intercept(invocation); } }
代理工厂主要就是对目标对象target进行包装,这里的target可能是原对象,也有可能是包装过后代理对象二次包装代理,简单来说是这样,我们的IOrderService中的submitOrder方法,在托管给JDK动态代理后,为了实现责任链的链式原则,我们会基于此对象再次调用wrap方法进行二次包装,将我们的各式各样的拦截器一层一层的包装上去,这样处理成上一级引用下一级的链式关系。
接下来定义我们具体都又那些拦截器大哥
public class TodoInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException { System.out.println(" todo something "); Object result = invocation.proceed(); System.out.println(" to do end something "); return result; } } public class LogInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException { System.out.println(" do log "); Object result = invocation.proceed(); System.out.println(" do log "); return result; } }
两位拦路大哥已再此,接下来我们吧拦路大哥们放入工厂进行包装
@Override public void checkSubmitOrder() { IOrderService orderService = new OrderService(); orderService = (IOrderService) ProxyFactory.wrap(orderService, new LogInterceptor()); orderService = (IOrderService) ProxyFactory.wrap(orderService, new TodoInterceptor()); orderService.submitOrder(); }
代码执行效果如下
例子挺简单的,主要是说明责任链可以使用的场景,结合此责任链的扩展就非常多了,例如我把拦截器统一交给spring 托管,在每个检查方法上自定义注解,标记需要执行的拦截器,在AOP切面扫描业务方法时,判断是否存在此类责任链的拦截注解,有的话则执行业务判断,这样子就统一了业务检查代码的封装,再也不用再每行代码里写那些重复的chek()了,介于篇幅,就不在过多介绍了,有兴趣可以私聊我。
我是tom:一切的恐惧都源于自己的未知。