???? Command模式是GOF中較為簡單的,用來封裝行為的一個模式。在我們初涉設計模式的領域前,我們可能就在不知不覺中使用了它。比如說JAVA多線程中的Ruuable接口,比如說swing編程中用于處理事件的action,這些通通都是Command模式的使用。跟很多行為型模式一樣,command模式用于降低接收者和發送者的耦合,我們經常可以在一些開源框架中看到,command實例對象常在層與層之間進行傳遞,接收者對于接收到的command,根本不知道其所能處理的業務,只知道如何調用它來執行。
?
???? 舉我們常見的業務框架的處理來講,每一個command對應于一個業務處理類,這些command統一在一個Front Controller中進行調用。
??? Controller中調用command的方法:
?
#controller ..... //獲取command對象 //調用command command.execute(ctx); ....
?
?
? 而在我們的業務處理類即command中,我們會去調用service層的方法來獲取對應的數據,并返回給頁面處理。這個是一般的流程,而我們在處理過程中,我們可能會遇到多種不同的異常(一個command中需要實現的業務可能需要調用多個不同service來完成),而在command中,我們處理異常的方式大多是將異常信息記錄下來,然后返回給client端一個友好的錯誤信息提示,或者直接拋給Front Controller進行統一的處理。如果command這一層的開發與service層的開發是由不同的人員進行的,那么對于service層拋出的異常,還需要先進行封裝成command層的異常(這個也就是我們所說的異常鏈的轉化),再拋給Front Controller進行處理。
?
#controller try{ ..... //獲取command對象 //調用command command.execute(ctx); .... }catch(xxxBizException ex) { //記錄異常信息 logger.warn("xxx",ex); //構造一個友好的response }
?
?? 當我們現實世界中的業務遠不會這么簡單,我們經常需要根據不同異常來構造不同的友好提示信息,如果我們把這些不同的異常都放到controller進行處理,那么每次當command需要增加新的異常處理時,都需要對controller進行修改,而且,從設計層面上講,這些業務異常的處理是屬于controller的職責嗎?在我看來,controller只需要處理一些通用的異常即可,對于那些可變而且跟業務強依賴的異常,還是需要放到command自己本身來處理,但是,這樣一來,command就變成了下面這個樣子:
#ListingGoodCommand public void execute(Context ctx) throws Throwable { ... try{ //校驗權限 Authenticator.validate(ctx.getUser); }catch(UserNotFoundException ex) { //記錄異常 logger.warn(ex); //構造用戶不存在的信息 ctx.setError("user not found"); //直接返回,不執行后續的操作 return; }catch(UserExpiredException ex) //記錄異常 logger.warn(ex); //構造用戶過期的信息 ctx.setError("user is expired."); //直接返回,不執行后續的操作 return; } //獲取所有的商品 try{ response=GoodService.listAll(ctx.getUser); }catch(WebServiceException ex) { //記錄異常 logger.warn(ex); //出現webservice異常 ctx.setError("Can not acquier goods now."); //直接返回,不執行后續的操作 return; } //其他操作 ... }
?
??? 從上可以看出,我們的command代碼可讀性很差,而且不易擴展。有沒有什么好的方式能夠統一處理異常但又不放在controller中呢?有。只需要修改下ICommand接口以及controller類即可。
?? ICommand接口增加一個postProcess()方法:
??
??? Controller類中增加調用ICommand#postProcess()的處理:
?
#controller try{ //保存發生的異常 Throwable t=null; ..... //獲取command對象 //調用command command.execute(ctx); .... }catch(xxxBizException1 ex) { //記錄異常信息 logger.warn("xxx",ex); //構造一個友好的response } catch(xxxBizException2 ex) { //記錄異常信息 logger.warn("xxx",ex); //構造一個友好的response }catch(Throwable ex) { //保存未能進行處理的異常 t=ex; } //如果有未能處理的異常,則調用ICommand#postProcess()處理 if(t!=null) command. postProcess(ctx,t);
?
??? 從上可以看出,在controller的異常處理中,我們把xxxBizException1,xxxBizException2 都當作是通用的異常進行處理,剩下的都會轉發到command中去進行處理。這樣,我們的ListingGoodCommand就演變成了:
?
#ListingGoodCommand public void execute(Context ctx) throws Throwable { ... //校驗權限 Authenticator.validate(ctx.getUser); //獲取所有的商品 response=GoodService.listAll(ctx.getUser); //其他操作 ... } public void postProcess(Context ctx,Throwable t) { if(t instanceof UserNotFoundException) { //記錄異常 logger.warn(ex); //構造用戶不存在的信息 ctx.setError("user not found"); } if(t instanceof UserExpiredException) { //記錄異常 logger.warn(ex); //構造用戶過期的信息 ctx.setError("user is expired."); } //其他異常同理 }
?
? 這樣一來,我們就能夠在同一個地方進行異常的處理,并且我們command中的業務邏輯變得很清晰。看到這里,眼尖的同學可能已經發現,這實際上就是Command的后Filter模式。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
