掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
本文轉(zhuǎn)載自微信公眾號「源碼興趣圈」,作者龍臺。轉(zhuǎn)載本文請聯(lián)系源碼興趣圈公眾號。

為南皮等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及南皮網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站設(shè)計(jì)、做網(wǎng)站、南皮網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
日常 Coding 過程中,設(shè)計(jì)模式三板斧:模版、構(gòu)建者、策略,今天來說下第三板斧 策略設(shè)計(jì)模式
策略模式還是比較簡單并且使用較多的,平常我們多運(yùn)用策略模式用來消除 if-else、switch 等多重判斷的代碼,消除 if-else、switch 多重判斷 可以有效應(yīng)對代碼的復(fù)雜性,使設(shè)計(jì)解耦
如果分支判斷會(huì)不斷變化(增、刪、改),那么可以使用技巧讓策略模式滿足開閉原則,提高代碼的擴(kuò)展性 (策略模式場景主要負(fù)責(zé)解耦,開閉原則需要額外支持)
下文中會(huì)詳細(xì)列舉如何使用設(shè)計(jì)模式做個(gè) Demo 、模式的真實(shí)場景以及策略模式的好處
策略設(shè)計(jì)模式大綱如下:
什么是策略模式
策略模式在 GoF 的《設(shè)計(jì)模式》一書中,是這樣定義的:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
定義一組算法類,將每個(gè)算法分別封裝起來,讓它們可以互相替換。策略模式使這些算法在客戶端調(diào)用它們的時(shí)候能夠互不影響地變化,客戶端代指使用算法的代碼
看到上面的介紹可能不太明白策略模式具體為何物,這里會(huì)從最基本的代碼說起,一步一步徹底掌握此模式。下述代碼可能大家都能聯(lián)想出對應(yīng)的業(yè)務(wù),根據(jù)對應(yīng)的優(yōu)惠類型,對價(jià)格作出相應(yīng)的優(yōu)惠
這段代碼是能夠滿足項(xiàng)目中業(yè)務(wù)需求的,而且很多已上線生產(chǎn)環(huán)境的代碼也有這類代碼。但是,這一段代碼存在存在兩個(gè)弊端
如何運(yùn)用策略模式優(yōu)化上述代碼,使程序設(shè)計(jì)看著簡約、可擴(kuò)展等特性
簡化代碼的復(fù)雜性,將不同的優(yōu)惠類型定義為不同的策略算法實(shí)現(xiàn)類
保證開閉原則,增加程序的健壯性以及可擴(kuò)展性
策略模式示例
將上述代碼塊改造為策略設(shè)計(jì)模式,大致需要三個(gè)步驟
目前把抽象策略接口、具體的策略實(shí)現(xiàn)類以及策略工廠都已經(jīng)創(chuàng)建了,現(xiàn)在可以看一下客戶端需要如何調(diào)用,又是如何對客戶端屏蔽具體實(shí)現(xiàn)細(xì)節(jié)的
根據(jù)代碼塊圖片得知,具體策略類是從策略工廠中獲取,確實(shí)是取消了 if-else 設(shè)計(jì),在工廠中使用 Map 存儲(chǔ)策略實(shí)現(xiàn)。獲取到策略類后執(zhí)行具體的優(yōu)惠策略方法就可以獲取優(yōu)惠后的金額
通過分析大家得知,目前這種設(shè)計(jì)確實(shí)將應(yīng)用代碼的復(fù)雜性降低了。如果新增一個(gè)優(yōu)惠策略,只需要新增一個(gè)策略算法實(shí)現(xiàn)類即可。但是,添加一個(gè)策略算法實(shí)現(xiàn),意味著需要改動(dòng)策略工廠中的代碼,還是不符合開閉原則
如何完整實(shí)現(xiàn)符合開閉原則的策略模式,需要借助 Spring 的幫助,詳細(xì)案例請繼續(xù)往下看
項(xiàng)目中真實(shí)的應(yīng)用場景
最近項(xiàng)目中設(shè)計(jì)的一個(gè)功能用到了策略模式,分為兩類角色,筆者負(fù)責(zé)定義抽象策略接口以及策略工廠,不同的策略算法需要各個(gè)業(yè)務(wù)方去實(shí)現(xiàn),可以聯(lián)想到上文中的優(yōu)惠券功能。因?yàn)槭?Spring 項(xiàng)目,所以都是按照 Spring 的方式進(jìn)行處理,話不多說,上代碼
可以看到,比對上面的示例代碼,有兩處明顯的變化
小貼士:為了閱讀方便,mark() 返回直接使用字符串替代,讀者朋友在返回標(biāo)示時(shí)最好使用枚舉定義
接下來繼續(xù)查看抽象策略工廠如何改造,才能滿足開閉原則
和之前 責(zé)任鏈模式 相同,都是通過 InitializingBean 接口實(shí)現(xiàn)中調(diào)用 IOC 容器查找對應(yīng)策略實(shí)現(xiàn),隨后將策略實(shí)現(xiàn) mark() 方法返回值作為 key, 策略實(shí)現(xiàn)本身作為 value 添加到 Map 容器中等待客戶端的調(diào)用
這里使用的 SpringBoot 測試類,注入策略工廠 Bean,通過策略工廠選擇出具體的策略算法類,繼而通過算法獲取到優(yōu)惠后的價(jià)格。小插曲:如果不想把策略工廠注入 Spring 也可以,實(shí)現(xiàn)方法有很多
總結(jié)下本小節(jié),我們通過和 Spring 結(jié)合的方式,通過策略設(shè)計(jì)模式對文初的代碼塊進(jìn)行了兩塊優(yōu)化:應(yīng)對代碼的復(fù)雜性,讓其滿足開閉原則。更具體一些呢就是 通過抽象策略算法類減少代碼的復(fù)雜性,繼而通過 Spring 的一些特性同時(shí)滿足了開閉原則,現(xiàn)在來了新需求只要添加新的策略類即可,健壯易擴(kuò)展
源碼底層如何耍策略模式
自己用肯定覺得不夠,必要時(shí)候還得看看設(shè)計(jì)開源框架源碼的大佬們?nèi)绾卧诖a中運(yùn)用策略模式的
在作者了解中,JDK、Spring、SpringMvc、Mybatis、Dubbo 等等都運(yùn)用了策略設(shè)計(jì)模式,這里就以 Mybatis 舉例說明
Mybatis 中 Executor 代表執(zhí)行器,負(fù)責(zé)增刪改查的具體操作。其中用到了兩種設(shè)計(jì)模式,模版方法以及策略模式
Executor 代表了抽象策略接口,剛才說到的模版方法模式源自 BaseExecutor
Configuration 代表策略工廠,負(fù)責(zé)創(chuàng)建具體的策略算法實(shí)現(xiàn)類
SimpleExecuto、ReuseExecutor... 表示封裝了具體的策略算法實(shí)現(xiàn)類
上述代碼塊發(fā)生在 Configuration 類中創(chuàng)建執(zhí)行器 Executor,通過 executorType 判斷創(chuàng)建不同的策略算法。
上述代碼塊并沒有徹底消除 if-else,因?yàn)?Mybatis 中執(zhí)行器策略基本是固定的,也就是說它只會(huì)有這些 if-else 判斷,基本不會(huì)新增或修改。如果非要消除 if-else,可以這么搞,這里寫一下偽代碼
這種方式叫做 "查表法",通過策略工廠實(shí)現(xiàn)消除 if-else 分支。最后,Mybatis 太過詳細(xì)的設(shè)計(jì)這里不再贅述,有興趣的小伙伴可以去把源碼下載啃一啃
到了這里可能有讀者看出了問題,策略模式就算消除了 if-else 但是如果添加新的策略類,不還是會(huì)違反開閉原則么?
沒錯(cuò),因?yàn)?Mybatis 本身沒有引入 Spring 依賴,所以沒有辦法借助 IOC 容器實(shí)現(xiàn)開閉原則。Spring 是一種開閉原則解決方式,那還有沒有別的解決方式?
解決方式有很多,開閉原則核心就是 對原有代碼修改關(guān)閉,對新增代碼開放??梢酝ㄟ^掃描指定包下的自定義注解亦或者通過 instanceof 判斷是否繼承自某接口都可以。不過, 項(xiàng)目如果用了 Spring 還是消停的吧
結(jié)言
文章中圖文并茂的方式介紹策略模式,通過價(jià)格優(yōu)惠的場景,進(jìn)而引用本文的重點(diǎn):策略設(shè)計(jì)模式,相信小伙伴看完后都會(huì)有一定的收獲
策略模式的本質(zhì)依然是對代碼設(shè)計(jì)解耦合,通過三類角色貫穿策略模式:抽象策略接口、策略工廠以及具體的策略實(shí)現(xiàn)類。通過細(xì)粒度的策略實(shí)現(xiàn)類避免了主體代碼量過多,減少了設(shè)計(jì)中的復(fù)雜性。并通過開閉原則特性,添加新策略時(shí)可以保證最小、集中化修改代碼
作者聽到過很多小伙伴覺得自己做的都是 CRUD 工作,沒有挑戰(zhàn)性沒意思。其實(shí),我想說的是:業(yè)務(wù)代碼一樣牛逼,一樣能體現(xiàn)程序員的水平。不一定非要高并發(fā)、大數(shù)據(jù)等場景。頗有一屋不掃何以掃天下的意思
最后拋出一個(gè)問題:出現(xiàn) if-else 的代碼,一定要使用策略模式優(yōu)化么
如果 if-else 判斷分支不多并且是固定的,后續(xù)不會(huì)出現(xiàn)新的分支,那我們完全 可以通過抽函數(shù)的方式降低程序復(fù)雜性;不要想法設(shè)法去除 if-else 語句,存在即合理。而且,使用策略模式會(huì)導(dǎo)致類增多,沒有必要為了少量的判斷分支引入策略模式
關(guān)于策略設(shè)計(jì)模式本文就講到這里,后面會(huì)陸續(xù)輸出工廠、原型、享元等模式;如果文章對你有幫助那就點(diǎn)個(gè)關(guān)注支持下吧,祝好。
文章參考:《設(shè)計(jì)模式之美:策略模式》

我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流