一次代码优化实践,用了模板方法+策略+工厂方法模式
優化代碼前
先來了解一下類似的業務場景,簡言之,就是:多個商戶接入我們系統,都是走一個類似的流程通過http請求出去的。
優化前,每個公司對應一個句柄服務,偽代碼如下:
//?商戶A處理句柄 CompanyAHandler?implements?RequestHandler?{Resp?hander(req){//查詢商戶信息queryMerchantInfo();//加簽signature();//?http請求(走代理)httpRequestbyProxy()//?驗簽verify();} } //?商戶B處理句柄 CompanyBHandler?implements?RequestHandler?{Resp?hander(Rreq){//查詢商戶信息queryMerchantInfo();//加簽signature();//?http請求(不走代理)httpRequestbyDirect();//?驗簽verify();?} } //?商戶C處理句柄 CompanyBHandler?implements?RequestHandler?{Resp?hander(Rreq){//查詢商戶信息queryMerchantInfo();//?webservice?方式調用requestByWebservice();} }?
?
優化代碼思路
我的優化代碼思路,是有「重復代碼,先把它抽出來,或者公用變量,或者公用方法,伸著公用類」。所以呢,查詢商戶信息呀,加簽呀,http請求呀先全部各抽成一個公用方法。你細心點會發現,連每個Handler處理過程都很類似的,大概都是查詢商戶信息+加簽+http請求+驗簽,于是呢,可以直接把它們抽象成一個公用類呀~在這里就要引入模板方法模式咯
模板方法模式
在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。 這種類型的設計模式屬于行為型模式。既然每個Handler處理,都是類似的流程,那「定義一個抽象類,把查詢商戶信息,加簽,http請求,驗簽什么的,都放到里面去,儼然一個模板一樣」。然后,因為有些商戶走http代理,有些又沒走代理,怎么辦呢? 定義「一個抽象方法,給子類實現」嘛,因為能共用就放到父類(當前的抽象類),不能共用就放到子類嘛~代碼如下:
abstract?class?AbstractCompanyCommonService?implements?ICompanyCommonService?{?//模板方法Resp?handlerTempPlate(req){//查詢商戶信息queryMerchantInfo();//?加簽signature();//http?請求if(isRequestByProxy()){httpProxy();}else{httpDirect();}//?驗簽verifySinature();}//?Http是否走代理abstract?boolean?isRequestByProxy(); }子類商戶A實現:
CompanyAServiceImpl?extends?AbstractCompanyCommonService{Resp?hander(req){return?handlerTempPlate(req);}//公司A是走代理的boolean?isRequestByProxy(){return?true;}子類商戶B實現:
CompanyBServiceImpl?extends?AbstractCompanyCommonService{Resp?hander(req){return?handlerTempPlate(req);}//公司B是不走代理的boolean?isRequestByProxy(){return?false;}策略模式
心細的讀者會發現,甚至提出疑問,「你的商戶C的服務實現跟你定義的公用模板,不太一樣呢」,那當然,實際開發中,不跟你定義的模板一樣太常見了,需求是產品提的嘛,又不是根據你模板提的,是代碼服務于需求的。好了,不多說啦,我使用了策略模式,來優化這個問題。
在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬于行為型模式。策略模式理解起來其好抽象對不對?我個人理解,其實策略模式就是定義一個方法(所謂算法),給子類自己去實現。實際上就是「定義個方法/接口,讓子類自己去實現」。看代碼吧:
//?定義一個方法,把策略交給子類去實現。 interface?ICompanyCommonService{Resp?hander(req); }前面商戶A和商戶B還是不變,使用抽象類AbstractCompanyCommonService的模板,模板不滿足商戶C,商戶C只能自己去實現咯,各個子類自己去實現的行為,就是策略模式的體現呢,如下:
CompanyCServiceImpl?extends?AbstractCompanyCommonService{Res?hander(req){//查詢商戶信息queryMerchantInfo();requestByWebservice();????}//隨意了,你都不走模板了boolean?isRequestByProxy(){return?false;}工廠方法模式
商戶A、B、C服務怎么被管理呢,之前分別給A,B,C服務實現handler的,現在好了,都不知道怎么管理了,怎么知道調用哪個呢?別慌,解決方案是「工廠方法模式」。
在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,并且是通過使用一個共同的接口來指向新創建的對象。工廠方法模式具體實現就是:接口定義一個枚舉,每個服務實現都重新實現枚舉,設置唯一標志枚舉,再交給spring容器管理。看代碼咯:
interface?ICompanyCommonService{Resp?hander(req);CompanyEnum?getCompanyEnum(); }CompanyAServiceImpl?extends?AbstractCompanyCommonService{Resp?hander(req){return?handlerTempPlate(req);}//公司A是走代理的boolean?isRequestByProxy(){return?true;}CompanyEnum?getCompanyEnum(){return?CompanyEnum.A;}?CompanyBServiceImpl?extends?AbstractCompanyCommonService{Resp?hander(req){return?handlerTempPlate(req);}//公司B是不走代理的boolean?isRequestByProxy(){return?false;}CompanyEnum?getCompanyEnum(){return?CompanyEnum.B;}?來來來,工廠方法模式出爐咯:
@Component public?class?CompanyServiceFactory?implements?ApplicationContextAware?{private?static?Map<CompanyEnum,?ICompanyCommonService>?map?=?new?HashMap<>();@Overridepublic?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{Map<String,?ICompanyCommonService>?tempMap?=?applicationContext.getBeansOfType(ICompanyCommonService.class);tempMap.values().forEach(iCompanyCommonService?->map.put(iCompanyCommonService.getCompanyEnum(),?iCompanyCommonService));}public?Resp?handler(req)?{return?map.get(CompanyEnum.getCompanyEnum(req.getCompanyFlag()).hander(req);} }?
?
最后建議
最后,不要為了使用設計模式生搬硬套,而是優化代碼過程中,發現這個設計模式剛好適用,才去用的哈。附上最后的代碼咯:
@Service public?class?CompanyHandler?implements?RequestHandler??{@Autowireprivate?CompanyServiceFactory?companyServiceFactory;Resp?hander(req){return?companyServiceFactory.handler(req);} }有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的一次代码优化实践,用了模板方法+策略+工厂方法模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle 表的创建 及相关參数
- 下一篇: 别再用硬编码写业务流程了,试试这款轻量级