后端思維篇:如何應(yīng)用設(shè)計(jì)模式優(yōu)化代碼
前言
大家好,我是撿田螺的小男孩。
本文是后端思維專(zhuān)欄的第三篇哈,本文內(nèi)容就是:在原有代碼基礎(chǔ)上,如何一步步通過(guò)設(shè)計(jì)模式去優(yōu)化代碼?日常工作中,我們用得最多的設(shè)計(jì)模式,就是策略模式、工廠(chǎng)模式和模板方法模式啦。最近剛好用這幾種模式優(yōu)化了代碼,所以今天跟大家聊聊,我是怎么優(yōu)化的,思路是怎么樣的。希望本文對(duì)大家有幫助哈。
優(yōu)化前偽代碼的流程 策略模式是如何應(yīng)用進(jìn)去的 工廠(chǎng)設(shè)計(jì)模式是怎么使用的 模板方法模式又是怎么應(yīng)用進(jìn)去的。 嘮叨幾句
1. 優(yōu)化前偽代碼流程
大家先看下,優(yōu)化前,原有代碼的大概邏輯哈。代碼如下:
class?Parameter{
????int?pageSize;
????int?pageNo;
??? int reqNum;
????//其他參數(shù)。
}
//邏輯處理,是否命中客群
boolean?isMarketHit(Parameter?dto){
????//如果是企業(yè)客群類(lèi)型
????if(dto.type?==?'enterprise'){
???????
???????//開(kāi)關(guān)關(guān)閉不請(qǐng)求
???????if(isEnterpriseSwitchClose){
??????????return?false;???????????
???????}
???????
????????//請(qǐng)求只有一條記錄的話(huà)
????????if(dto.reqNum==1){
????????????//調(diào)用大數(shù)據(jù)的點(diǎn)查接口
????????????return?singleRemoteEOIinvoke(dto);?
????????????
????????????//請(qǐng)求超過(guò)一條的話(huà)
????????}else?if(dto.reqNum>1){
????????
????????????//調(diào)用大數(shù)據(jù)的批量接口
????????????return?batchRemoteEOIinvoke(dto);????
????????}
????????
????????//如果是市場(chǎng)營(yíng)銷(xiāo)類(lèi)型
????}else?if(dto.type=='market_list'){
????
??????//開(kāi)關(guān)關(guān)閉不請(qǐng)求
???????if(isMarketListSwitchClose){
??????????return?false;???????????
???????}
????????//請(qǐng)求只有一條記錄的話(huà)
????????if(dto.reqNum==1){
????????????//調(diào)用營(yíng)銷(xiāo)的點(diǎn)查接口
????????????return?singleRemoteMarketinvoke(dto);??
???????????
??????????//請(qǐng)求超過(guò)一條的話(huà)
????????}else?if(dto.reqNum>1){
????????????//調(diào)用營(yíng)銷(xiāo)的批量接口
????????????return?batchRemoteMarketinvoke(dto);????
????????}
????}
}
這個(gè)代碼可能存在哪些問(wèn)題呢?
如果 if分支變多的話(huà),代碼就會(huì)變得臃腫如果你需要接入 一種新的類(lèi)型,只能在源代碼修改
說(shuō)得專(zhuān)業(yè)一點(diǎn)點(diǎn),就是以上代碼,違背了面向?qū)ο蟮?strong>開(kāi)閉原則和單一原則。
開(kāi)閉原則:(對(duì)于擴(kuò)展是開(kāi)放的,對(duì)于修改是封閉的),增加或者刪除某個(gè)邏輯,都需要修改原來(lái)的代碼 單一原則:(規(guī)定一個(gè)類(lèi)應(yīng)該只有一個(gè)發(fā)生變化的原因),修改任何類(lèi)型的分支邏輯代碼,都需要修改當(dāng)前類(lèi)的代碼
2. 策略模式是如何應(yīng)用進(jìn)去的
大家是否還記得,如果代碼中有多個(gè)
if...else等條件分支,并且每個(gè)條件分支,可以封裝起來(lái)替換的,我們就可以使用策略模式來(lái)優(yōu)化。
回憶一下,什么是策略模式呢?
策略模式定義了算法族,分別封裝起來(lái),讓它們之間可以相互替換,此模式讓算法的變化獨(dú)立于使用算法的的客戶(hù)。這個(gè)策略模式的定義是不是有點(diǎn)抽象呢?打個(gè)通俗易懂的比喻:
假設(shè)你跟不同性格類(lèi)型的小姐姐約會(huì),要用不同的策略,有的請(qǐng)電影比較好,有的則去吃小吃效果不錯(cuò),有的去逛街買(mǎi)買(mǎi)買(mǎi)最合適。當(dāng)然,目的都是為了得到小姐姐的芳心,請(qǐng)看電影、吃小吃、逛街就是不同的策略。
策略模式針對(duì)一組算法,將每一個(gè)算法封裝到實(shí)現(xiàn)共同接口的不同獨(dú)立的類(lèi)中,從而使得它們可以相互替換。策略模式我們一般是怎么定義的呢?
一個(gè)接口或者抽象類(lèi),里面兩個(gè)方法(一個(gè)方法匹配類(lèi)型,一個(gè)可替換的邏輯實(shí)現(xiàn)方法) 不同策略的差異化實(shí)現(xiàn)(就是說(shuō),不同策略的實(shí)現(xiàn)類(lèi))
所以,對(duì)于原有的偽代碼流程,我們就可以定義企業(yè)客群類(lèi)型的策略實(shí)現(xiàn)類(lèi),和市場(chǎng)營(yíng)銷(xiāo)類(lèi)型的策略實(shí)現(xiàn)類(lèi)。這兩個(gè)策略實(shí)現(xiàn)類(lèi)都實(shí)現(xiàn)了兩個(gè)方法,一個(gè)方法是匹配類(lèi)型的,就是返回原始代碼if...else條件判斷的類(lèi)型;然后另外個(gè)方法,就是if...else條件的實(shí)現(xiàn)內(nèi)容。代碼如下:
//一個(gè)接口
interface?IGroupLabelStrategyService?{
????//這個(gè)方法對(duì)應(yīng)策略實(shí)現(xiàn)類(lèi)的具體實(shí)現(xiàn)
????boolean?processBiz(Parameter?dto);
????
????//這個(gè)方法就是策略類(lèi)的類(lèi)型,也就是對(duì)應(yīng)```if...else```條件判斷的類(lèi)型
????String?getType();
}?
//企業(yè)客群類(lèi)型的策略實(shí)現(xiàn)類(lèi)
EnterpriseGroupLablelStrategyServiceImpl?implements?IGroupLabelStrategyService{
????
????//對(duì)應(yīng)企業(yè)客群類(lèi)型的條件分支里面的實(shí)現(xiàn)
????boolean?processBiz(Parameter?dto){
????
?????????//開(kāi)關(guān)關(guān)閉不請(qǐng)求
???????if(isEnterpriseSwitchClose){
??????????return?false;???????????
???????}
??????
????????//請(qǐng)求只有一條記錄的話(huà)
????????if(dto.reqNum==1){
????????????//調(diào)用大數(shù)據(jù)的點(diǎn)查接口
????????????return?singleRemoteEOIinvoke(dto);?
????????????
????????????//請(qǐng)求超過(guò)一條的話(huà)
????????}else?if(dto.reqNum>1){
????????
????????????//調(diào)用遠(yuǎn)程大數(shù)據(jù)批量接口
????????????return?batchRemoteEOIinvoke(dto);????
????????}
????????
?????}
?????
????//對(duì)應(yīng)企業(yè)類(lèi)型
????String?getType(){
???????return?"enterprise";
????}
}
//市場(chǎng)營(yíng)銷(xiāo)類(lèi)型的策略實(shí)現(xiàn)類(lèi)
MarketListGroupLablelStrategyServiceImpl?implements?IGroupLabelStrategyService{
?????//對(duì)應(yīng)市場(chǎng)營(yíng)銷(xiāo)類(lèi)型的條件分支里面的實(shí)現(xiàn)
?????boolean?processBiz(Parameter?dto){
?????
???????//開(kāi)關(guān)關(guān)閉不請(qǐng)求
???????if(isMarketListSwitchClose){
??????????return?false;???????????
???????}
???????
????????//請(qǐng)求只有一條記錄的話(huà)
????????if(dto.reqNum==1){
????????????//調(diào)用營(yíng)銷(xiāo)點(diǎn)查接口
????????????return?singleRemoteMarketinvoke(dto);??
???????????
??????????//請(qǐng)求超過(guò)一條的話(huà)
????????}else?if(dto.reqNum>1){
????????????//調(diào)用營(yíng)銷(xiāo)批量接口
????????????return?batchRemoteMarketinvoke(dto);????
????????}
????????
??????}
??????
??????String?getType(){
?????????return?"market_list";
???????}
}
3. 工廠(chǎng)設(shè)計(jì)模式是怎么使用的
每個(gè)策略現(xiàn)在都實(shí)現(xiàn)好了,不同策略的實(shí)現(xiàn)類(lèi)怎么交給spring管理呢?
我們可以實(shí)現(xiàn)ApplicationContextAware接口,把策略的實(shí)現(xiàn)類(lèi)注入到一個(gè)map,然后根據(jù)請(qǐng)求方不同的策略請(qǐng)求類(lèi)型,去實(shí)現(xiàn)不同的調(diào)用嘛,其實(shí)就是類(lèi)似于工廠(chǎng)模式的思想啦。代碼如下:
@Component
public?class?GroupLabelStrategyServiceFactory?implements?ApplicationContextAware{
????//存放對(duì)應(yīng)的類(lèi)型和實(shí)現(xiàn)類(lèi)
????private?Map?map?=?new?ConcurrentHashMap<>();
????
????//策略實(shí)現(xiàn)類(lèi)注入到map
????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????Map?tempMap?=?applicationContext.getBeansOfType(IGroupLabelStrategyService.class);
????????
????????tmepMap.values().forEach(strategyService?->?map.put(strategyService.getType(),?strategyService));
????}
????
????//工廠(chǎng)方法
????public?boolean?processBiz(ParamDTO?dto){
????
????????//根據(jù)不同類(lèi)型,獲取不同的實(shí)現(xiàn)類(lèi)
????????IGroupLabelStrategyService?groupLabelStrategyService=?map.get(dto.getType());
????????
????????if?(batchGroupLabelJudgeService?!=?null)?{
????????????return?groupLabelStrategyService.processBiz(dto);
????????}
????????return?false;?????
?????}?
}
有了策略模式+工廠(chǎng)方法模式后,我們偽代碼流程簡(jiǎn)化成這樣啦:
class?Parameter{
????int?pageSize;
????int?pageNo;
??? int reqNum;
????//其他參數(shù)。
}
boolean?isMarketHit(Parameter?dto){
??//直接調(diào)用工廠(chǎng)類(lèi)就可以啦,其他邏輯處理已經(jīng)在策略實(shí)現(xiàn)類(lèi)里面了。
??return?groupLabelStrategyServiceFactory.processBiz(dto);
}
4. 模板方法模式又是怎么應(yīng)用進(jìn)去的
小伙伴們,細(xì)心回頭觀(guān)察下原先的偽代碼流程,會(huì)發(fā)現(xiàn)一個(gè)共性的代碼流程,就是先開(kāi)關(guān)控制,然后根據(jù)請(qǐng)求數(shù)量決定走單筆調(diào)用還是批量調(diào)用。
這就可以使用模板方法繼續(xù)優(yōu)化了。所謂模板方法模式,其實(shí)就是:
定義一個(gè)操作中的算法的骨架流程,而將一些步驟延遲到子類(lèi)中,使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。它的核心思想就是:定義一個(gè)操作的一系列步驟,對(duì)于某些暫時(shí)確定不下來(lái)的步驟,就留給子類(lèi)去實(shí)現(xiàn),這樣不同的子類(lèi)就可以定義出不同的步驟。
為了通俗易懂一點(diǎn),打個(gè)比喻:
追女朋友要先“牽手”,再“擁抱”,再“接吻”, 再“拍拍..額..手”。至于具體你用左手還是右手牽,無(wú)所謂,但是整個(gè)過(guò)程,定了一個(gè)流程模板,按照模板來(lái)就行。
模板方法使用比較簡(jiǎn)單:
一個(gè)抽象類(lèi),定義骨架流程(抽象方法放一起) 確定的共同方法步驟,放到抽象類(lèi)(去除抽象方法標(biāo)記) 不確定的步驟,給子類(lèi)去差異化實(shí)現(xiàn)
我們只需要把開(kāi)關(guān)控制接口,單筆遠(yuǎn)程調(diào)用、批量遠(yuǎn)程調(diào)用這個(gè)通用共性的流程,定義到模板抽象類(lèi)就好啦。代碼如下:
public?abstract??AbstractGroupLabelJudgeTemplate?implements?IGroupLabelStrategyService{
?????????//模板骨架
?????????public?boolean?processBiz(Parameter?dto){
??????????????if(isSwitchClose){
?????????????????return?false;
??????????????}
?????????????if(dto.reqNum==1){
?????????????????return?singleRemote(dto);
?????????????}else?if(dto.reqNum>1){
?????????????????return?batchRemote(dto);
????????????}
?????????}
???????//開(kāi)關(guān)由子類(lèi)控制
????????abstract?boolean?isSwitchClose();
????????//單筆遠(yuǎn)程調(diào)用,由子類(lèi)控制
????????astract?boolean?singleRemote(dto);
????????//批量遠(yuǎn)程調(diào)用,由子類(lèi)控制
????????astract?boolean?batchRemote(dto);
}
不同的策略子類(lèi)自己控制開(kāi)關(guān),和控制不同接口的調(diào)用即可。
EnterpriseGroupLablelStrategyServiceImpl?extends?AbstractGroupLabelJudgeTemplate{
?????boolean?isSwitchClose(){
?????????//企業(yè)客群開(kāi)關(guān)
?????}
?????boolean?singleRemote(ParamDTO?dto){
????????//企業(yè)客群?jiǎn)喂P調(diào)用?????
????????return?singleRemoteEOIinvoke(dto);????
????}
????boolean?batchRemote(ParamDTO?dto){
??????//企業(yè)客群批量調(diào)用???
??????return?batchRemoteEOIinvoke(dto);
???}????????
}
MarketListGroupLablelStrategyServiceImpl?extends?AbstractGroupLabelJudgeTemplate{
?????boolean?isSwitchClose(){
?????????//營(yíng)銷(xiāo)客群開(kāi)關(guān)
?????}
?????boolean?singleRemote(ParamDTO?dto){
????????//營(yíng)銷(xiāo)客群?jiǎn)喂P調(diào)用?????
????????return?singleRemoteMarketinvoke(dto);????
????}
????boolean?batchRemote(ParamDTO?dto){
??????//營(yíng)銷(xiāo)客群批量調(diào)用???
??????return?batchRemoteMarketinvoke(dto);
???}????????
}
5. 嘮叨幾句
策略模式、工廠(chǎng)模式和模板方法模式這三種設(shè)計(jì)模式,是日常開(kāi)發(fā)用得最多的。本文呢,也是闡述了我是如何在原有代碼上,抽取出設(shè)計(jì)模式的。大家可以應(yīng)用到自己的工作中去哈,如果還沒(méi)理解的話(huà),可以加我微信,回復(fù)加群,拉你進(jìn)學(xué)習(xí)群,跟群上小伙伴一起探討。
推薦閱讀:
歡迎關(guān)注微信公眾號(hào):互聯(lián)網(wǎng)全棧架構(gòu),收取更多有價(jià)值的信息。
