java設(shè)計(jì)模式策略模式圖文示例詳解
策略模式
亦稱(chēng):Strategy
意圖
策略模式是一種行為設(shè)計(jì)模式,它能讓你定義一系列算法,并將每種算法分別放入獨(dú)立的類(lèi)中,以使算法的對(duì)象能夠相互替換。

問(wèn)題
一天,你打算為游客們創(chuàng)建一款導(dǎo)游程序。該程序的核心功能是提供美觀的地圖,以幫助用戶(hù)在任何城市中快速定位。
用戶(hù)期待的程序新功能是自動(dòng)路線規(guī)劃:他們希望輸入地址后就能在地圖上看到前往目的地的最快路線。
程序的首個(gè)版本只能規(guī)劃公路路線。駕車(chē)旅行的人們對(duì)此非常滿(mǎn)意。但很顯然,并非所有人都會(huì)在度假時(shí)開(kāi)車(chē)。因此你在下次更新時(shí)添加了規(guī)劃步行路線的功能。此后,你又添加了規(guī)劃公共交通路線的功能。
而這只是個(gè)開(kāi)始。不久后,你又要為騎行者規(guī)劃路線。又過(guò)了一段時(shí)間,你又要為游覽城市中的所有景點(diǎn)規(guī)劃路線。

導(dǎo)游代碼將變得非常臃腫。
盡管從商業(yè)角度來(lái)看,這款應(yīng)用非常成功,但其技術(shù)部分卻讓你非常頭疼:每次添加新的路線規(guī)劃算法后,導(dǎo)游應(yīng)用中主要類(lèi)的體積就會(huì)增加一倍。終于在某個(gè)時(shí)候,你覺(jué)得自己沒(méi)法繼續(xù)維護(hù)這堆代碼了。
無(wú)論是修復(fù)簡(jiǎn)單缺陷還是微調(diào)街道權(quán)重,對(duì)某個(gè)算法進(jìn)行任何修改都會(huì)影響整個(gè)類(lèi),從而增加在已有正常運(yùn)行代碼中引入錯(cuò)誤的風(fēng)險(xiǎn)。
此外,團(tuán)隊(duì)合作將變得低效。如果你在應(yīng)用成功發(fā)布后招募了團(tuán)隊(duì)成員,他們會(huì)抱怨在合并沖突的工作上花費(fèi)了太多時(shí)間。在實(shí)現(xiàn)新功能的過(guò)程中,你的團(tuán)隊(duì)需要修改同一個(gè)巨大的類(lèi),這樣他們所編寫(xiě)的代碼相互之間就可能會(huì)出現(xiàn)沖突。
解決方案
策略模式建議找出負(fù)責(zé)用許多不同方式完成特定任務(wù)的類(lèi),然后將其中的算法抽取到一組被稱(chēng)為策略的獨(dú)立類(lèi)中。
名為上下文的原始類(lèi)必須包含一個(gè)成員變量來(lái)存儲(chǔ)對(duì)于每種策略的引用。上下文并不執(zhí)行任務(wù),而是將工作委派給已連接的策略對(duì)象。
上下文不負(fù)責(zé)選擇符合任務(wù)需要的算法——客戶(hù)端會(huì)將所需策略傳遞給上下文。實(shí)際上,上下文并不十分了解策略,它會(huì)通過(guò)同樣的通用接口與所有策略進(jìn)行交互,而該接口只需暴露一個(gè)方法來(lái)觸發(fā)所選策略中封裝的算法即可。
因此,上下文可獨(dú)立于具體策略。這樣你就可在不修改上下文代碼或其他策略的情況下添加新算法或修改已有算法了。

路線規(guī)劃策略。
在導(dǎo)游應(yīng)用中,每個(gè)路線規(guī)劃算法都可被抽取到只有一個(gè)buildRoute生成路線方法的獨(dú)立類(lèi)中。該方法接收起點(diǎn)和終點(diǎn)作為參數(shù),并返回路線中途點(diǎn)的集合。
即使傳遞給每個(gè)路徑規(guī)劃類(lèi)的參數(shù)一模一樣,其所創(chuàng)建的路線也可能完全不同。主要導(dǎo)游類(lèi)的主要工作是在地圖上渲染一系列中途點(diǎn),不會(huì)在意如何選擇算法。該類(lèi)中還有一個(gè)用于切換當(dāng)前路徑規(guī)劃策略的方法,因此客戶(hù)端(例如用戶(hù)界面中的按鈕)可用其他策略替換當(dāng)前選擇的路徑規(guī)劃行為。
真實(shí)世界類(lèi)比

各種前往機(jī)場(chǎng)的出行策略
假如你需要前往機(jī)場(chǎng)。你可以選擇乘坐公共汽車(chē)、預(yù)約出租車(chē)或騎自行車(chē)。這些就是你的出行策略。你可以根據(jù)預(yù)算或時(shí)間等因素來(lái)選擇其中一種策略。
策略模式結(jié)構(gòu)

- 上下文(Context)維護(hù)指向具體策略的引用,且僅通過(guò)策略接口與該對(duì)象進(jìn)行交流。
- 策略(Strategy)接口是所有具體策略的通用接口,它聲明了一個(gè)上下文用于執(zhí)行策略的方法。
- 具體策略(Concrete Strategies)實(shí)現(xiàn)了上下文所用算法的各種不同變體。
- 當(dāng)上下文需要運(yùn)行算法時(shí),它會(huì)在其已連接的策略對(duì)象上調(diào)用執(zhí)行方法。上下文不清楚其所涉及的策略類(lèi)型與算法的執(zhí)行方式。
- 客戶(hù)端(Client)會(huì)創(chuàng)建一個(gè)特定策略對(duì)象并將其傳遞給上下文。上下文則會(huì)提供一個(gè)設(shè)置器以便客戶(hù)端在運(yùn)行時(shí)替換相關(guān)聯(lián)的策略。
偽代碼
在本例中,上下文使用了多個(gè)策略來(lái)執(zhí)行不同的計(jì)算操作。
// 策略接口聲明了某個(gè)算法各個(gè)不同版本間所共有的操作。上下文會(huì)使用該接口來(lái)
// 調(diào)用有具體策略定義的算法。
interface Strategy is
method execute(a, b)
// 具體策略會(huì)在遵循策略基礎(chǔ)接口的情況下實(shí)現(xiàn)算法。該接口實(shí)現(xiàn)了它們?cè)谏舷挛?
// 中的互換性。
class ConcreteStrategyAdd implements Strategy is
method execute(a, b) is
return a + b
class ConcreteStrategySubtract implements Strategy is
method execute(a, b) is
return a - b
class ConcreteStrategyMultiply implements Strategy is
method execute(a, b) is
return a * b
// 上下文定義了客戶(hù)端關(guān)注的接口。
class Context is
// 上下文會(huì)維護(hù)指向某個(gè)策略對(duì)象的引用。上下文不知曉策略的具體類(lèi)。上下
// 文必須通過(guò)策略接口來(lái)與所有策略進(jìn)行交互。
private strategy: Strategy
// 上下文通常會(huì)通過(guò)構(gòu)造函數(shù)來(lái)接收策略對(duì)象,同時(shí)還提供設(shè)置器以便在運(yùn)行
// 時(shí)切換策略。
method setStrategy(Strategy strategy) is
this.strategy = strategy
// 上下文會(huì)將一些工作委派給策略對(duì)象,而不是自行實(shí)現(xiàn)不同版本的算法。
method executeStrategy(int a, int b) is
return strategy.execute(a, b)
// 客戶(hù)端代碼會(huì)選擇具體策略并將其傳遞給上下文??蛻?hù)端必須知曉策略之間的差
// 異,才能做出正確的選擇。
class ExampleApplication is
method main() is
創(chuàng)建上下文對(duì)象。
讀取第一個(gè)數(shù)。
讀取最后一個(gè)數(shù)。
從用戶(hù)輸入中讀取期望進(jìn)行的行為。
if (action == addition) then
context.setStrategy(new ConcreteStrategyAdd())
if (action == subtraction) then
context.setStrategy(new ConcreteStrategySubtract())
if (action == multiplication) then
context.setStrategy(new ConcreteStrategyMultiply())
result = context.executeStrategy(First number, Second number)
打印結(jié)果。策略模式適合應(yīng)用場(chǎng)景
當(dāng)你想使用對(duì)象中各種不同的算法變體,并希望能在運(yùn)行時(shí)切換算法時(shí),可使用策略模式。
策略模式讓你能夠?qū)?duì)象關(guān)聯(lián)至可以不同方式執(zhí)行特定子任務(wù)的不同子對(duì)象,從而以間接方式在運(yùn)行時(shí)更改對(duì)象行為。
當(dāng)你有許多僅在執(zhí)行某些行為時(shí)略有不同的相似類(lèi)時(shí),可使用策略模式。
策略模式讓你能將不同行為抽取到一個(gè)獨(dú)立類(lèi)層次結(jié)構(gòu)中,并將原始類(lèi)組合成同一個(gè),從而減少重復(fù)代碼。
如果算法在上下文的邏輯中不是特別重要,使用該模式能將類(lèi)的業(yè)務(wù)邏輯與其算法實(shí)現(xiàn)細(xì)節(jié)隔離開(kāi)來(lái)。
策略模式讓你能將各種算法的代碼、內(nèi)部數(shù)據(jù)和依賴(lài)關(guān)系與其他代碼隔離開(kāi)來(lái)。不同客戶(hù)端可通過(guò)一個(gè)簡(jiǎn)單接口執(zhí)行算法,并能在運(yùn)行時(shí)進(jìn)行切換。
當(dāng)類(lèi)中使用了復(fù)雜條件運(yùn)算符以在同一算法的不同變體中切換時(shí),可使用該模式。
策略模式將所有繼承自同樣接口的算法抽取到獨(dú)立類(lèi)中,因此不再需要條件語(yǔ)句。原始對(duì)象并不實(shí)現(xiàn)所有算法的變體,而是將執(zhí)行工作委派給其中的一個(gè)獨(dú)立算法對(duì)象。
實(shí)現(xiàn)方式
- 從上下文類(lèi)中找出修改頻率較高的算法(也可能是用于在運(yùn)行時(shí)選擇某個(gè)算法變體的復(fù)雜條件運(yùn)算符)。
- 聲明該算法所有變體的通用策略接口。
- 將算法逐一抽取到各自的類(lèi)中,它們都必須實(shí)現(xiàn)策略接口。
- 在上下文類(lèi)中添加一個(gè)成員變量用于保存對(duì)于策略對(duì)象的引用。然后提供設(shè)置器以修改該成員變量。上下文僅可通過(guò)策略接口同策略對(duì)象進(jìn)行交互,如有需要還可定義一個(gè)接口來(lái)讓策略訪問(wèn)其數(shù)據(jù)。
- 客戶(hù)端必須將上下文類(lèi)與相應(yīng)策略進(jìn)行關(guān)聯(lián),使上下文可以預(yù)期的方式完成其主要工作。
策略模式優(yōu)缺點(diǎn)
- 你可以在運(yùn)行時(shí)切換對(duì)象內(nèi)的算法。
- 你可以將算法的實(shí)現(xiàn)和使用算法的代碼隔離開(kāi)來(lái)。
- 你可以使用組合來(lái)代替繼承。
- 開(kāi)閉原則。你無(wú)需對(duì)上下文進(jìn)行修改就能夠引入新的策略。
策略模式優(yōu)缺點(diǎn)
- 如果你的算法極少發(fā)生改變,那么沒(méi)有任何理由引入新的類(lèi)和接口。使用該模式只會(huì)讓程序過(guò)于復(fù)雜。
- 客戶(hù)端必須知曉策略間的不同——它需要選擇合適的策略。
- 許多現(xiàn)代編程語(yǔ)言支持函數(shù)類(lèi)型功能,允許你在一組匿名函數(shù)中實(shí)現(xiàn)不同版本的算法。這樣,你使用這些函數(shù)的方式就和使用策略對(duì)象時(shí)完全相同,無(wú)需借助額外的類(lèi)和接口來(lái)保持代碼簡(jiǎn)潔。
與其他模式的關(guān)系
橋接模式、狀態(tài)模式和策略模式(在某種程度上包括適配器模式)模式的接口非常相似。實(shí)際上,它們都基于組合模式——即將工作委派給其他對(duì)象,不過(guò)也各自解決了不同的問(wèn)題。模式并不只是以特定方式組織代碼的配方,你還可以使用它們來(lái)和其他開(kāi)發(fā)者討論模式所解決的問(wèn)題。
命令模式和策略看上去很像,因?yàn)閮烧叨寄芡ㄟ^(guò)某些行為來(lái)參數(shù)化對(duì)象。但是,它們的意圖有非常大的不同。
- 你可以使用命令來(lái)將任何操作轉(zhuǎn)換為對(duì)象。操作的參數(shù)將成為對(duì)象的成員變量。你可以通過(guò)轉(zhuǎn)換來(lái)延遲操作的執(zhí)行、將操作放入隊(duì)列、保存歷史命令或者向遠(yuǎn)程服務(wù)發(fā)送命令等。
- 另一方面,策略通常可用于描述完成某件事的不同方式,讓你能夠在同一個(gè)上下文類(lèi)中切換算法。
裝飾模式可讓你更改對(duì)象的外表,策略則讓你能夠改變其本質(zhì)。
模板方法模式基于繼承機(jī)制:它允許你通過(guò)擴(kuò)展子類(lèi)中的部分內(nèi)容來(lái)改變部分算法。策略基于組合機(jī)制:你可以通過(guò)對(duì)相應(yīng)行為提供不同的策略來(lái)改變對(duì)象的部分行為。模板方法在類(lèi)層次上運(yùn)作,因此它是靜態(tài)的。策略在對(duì)象層次上運(yùn)作,因此允許在運(yùn)行時(shí)切換行為。
狀態(tài)可被視為策略的擴(kuò)展。兩者都基于組合機(jī)制:它們都通過(guò)將部分工作委派給“幫手”對(duì)象來(lái)改變其在不同情景下的行為。策略使得這些對(duì)象相互之間完全獨(dú)立,它們不知道其他對(duì)象的存在。但狀態(tài)模式?jīng)]有限制具體狀態(tài)之間的依賴(lài),且允許它們自行改變?cè)诓煌榫跋碌臓顟B(tài)。
以上就是java設(shè)計(jì)模式策略模式圖文示例詳解的詳細(xì)內(nèi)容,更多關(guān)于java設(shè)計(jì)模式策略模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作
這篇文章主要介紹了SpringBoot 如何整合 ES 實(shí)現(xiàn) CRUD 操作,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10
創(chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇?jiǎng)?chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
舉例解析Java的設(shè)計(jì)模式編程中里氏替換原則的意義
這篇文章主要介紹了Java的設(shè)計(jì)模式中里氏替換原則的意義,文中舉例來(lái)說(shuō)明里氏替換原則中強(qiáng)調(diào)的繼承特性方面可能帶來(lái)的問(wèn)題,需要的朋友可以參考下2016-02-02
Java面試崗常見(jiàn)問(wèn)題之ArrayList和LinkedList的區(qū)別
ArrayList和LinkedList作為我們Java中最常使用的集合類(lèi),很多人在被問(wèn)到他們的區(qū)別時(shí),憋了半天僅僅冒出一句:一個(gè)是數(shù)組一個(gè)是鏈表。這樣回答簡(jiǎn)直讓面試官吐血。為了讓兄弟們打好基礎(chǔ),我們通過(guò)實(shí)際的使用測(cè)試,好好說(shuō)一下ArrayList和LinkedList的區(qū)別這道經(jīng)典的面試題2022-01-01
JAVA多線程實(shí)現(xiàn)的四種方式及使用場(chǎng)景詳解
這篇文章主要介紹了JAVA多線程實(shí)現(xiàn)的四種方式及使用場(chǎng)景,并舉例說(shuō)明了在管理系統(tǒng)中多線程的應(yīng)用場(chǎng)景,如數(shù)據(jù)導(dǎo)入導(dǎo)出、數(shù)據(jù)緩存更新、并發(fā)用戶(hù)操作處理、系統(tǒng)監(jiān)控和定時(shí)任務(wù)執(zhí)行,需要的朋友可以參考下2025-01-01
Java使用String.format方法格式化字符串的示例詳解
在編程過(guò)程中,我們經(jīng)常需要?jiǎng)?chuàng)建格式化的字符串來(lái)滿(mǎn)足特定的需求,比如生成用戶(hù)友好的消息、構(gòu)建報(bào)告或是輸出調(diào)試信息,Java 提供了一個(gè)強(qiáng)大的工具——String.format 方法,本文給大家介紹了Java使用String.format方法格式化字符串的示例,需要的朋友可以參考下2024-11-11
常用校驗(yàn)注解之@NotNull,@NotBlank,@NotEmpty的區(qū)別及說(shuō)明
這篇文章主要介紹了常用校驗(yàn)注解之@NotNull,@NotBlank,@NotEmpty的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

