一切拋開業務的設計,都是扯淡!在專案中有個我們會對多個介面進行多業務邏輯判斷,專案開始工期緊,所以先以實現功能為主,最近打算優化一番,一開始的程式碼是這樣的
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:一切的恐懼都源於自己的未知。