Java設計模式系列十五(策略模式)

前言面試

 

前面講完了設計模式的建立型模式和結構型模式,不知道一路下來有沒有過陪伴的讀者,由於我是三天打魚兩天曬網的模式,因此可能好久纔會更新一篇,可是就這樣以這種隨性的方式堅持着更新,我本身以爲挺好,自己能力水平有限,文章質量可能也是層次不齊,因此仍是但願你們多擔待些,給咱們一塊兒成長的時間和空間。算法

 

接下來準備講剩下的行爲型模式了,由於設計模式自己可能看起來比較枯燥,每次幾千字碼下來,估計有耐心看的人很少,因此我想了一下,剩下的設計模式我儘可能找一些生活中常見的例子,結合着實際場景講,但願能幫助你們更容易理解,若是講得很差,仍是請見諒!設計模式

 

 

不知道有沒有碼之初的小夥伴看NBA,看過NBA的必定會認識上圖的傳奇人物,沒錯,他就是咱們今天的男豬腳——泰倫盧。人稱戰術大師、戰術鬼才盧教頭。關於他的傳奇,坊間有太多的傳說,感興趣的小夥伴能夠去了解一下,保證你不會失望,你會愛他恨他甚至有時候還想打他,但無論怎樣,他永遠那麼可愛,由於他就是——ide

 

 

今天,我就帶你們來揭祕,深刻了解一下,爲什麼你們都叫他戰術鬼才,爲什麼騎士勇士球迷都深愛着他,盧教頭到底有何魅力,在短短四年間吸粉無數,成爲新一代NBA的流量小生,對不起,流量教練。this

 

 

 

咱們先來回顧一下騎士勇士球迷都沒法忘懷的2015-16賽季的總決賽,那一年騎士以總比分4:3力克勇士,成爲史上第一支總決賽1:3落後翻盤的冠軍球隊,也讓勇士成爲史上第一支常規賽73勝卻無緣總冠軍的球隊,球迷相愛相殺互撕的部分這裏就不談了,咱們談談爲什麼騎士那一年拿到了總冠軍。是由於詹姆斯的統治力嗎?仍是由於歐文樂福的王者輔助?又或是由於騎士全員的衆志成城?錯,大家都錯了,是由於他——戰術鬼才盧指導。設計

 

 

 

可能沒看過球的小夥伴仍是一臉懵逼,說到如今說了個什麼J&B,別慌,這就爲你們講爲何贏球的功勞是盧指導的,盧指導的戰術究竟如何神奇?請看盧指導的戰術騷操做:3d

 

 

又或者code

 

 

仍是這樣對象

 

 

你是否領略到了戰術鬼才的魅力了呢?若是你沒有意識到盧指導有多麼厲害,那麼你可能真的不懂球。戰術大師的備戰計劃,比賽策略都獨樹一幟,不管媒體何時採訪戰術問題時,盧指導都能成竹在胸的說:blog

 

 

看到這兒,你是否有疑慮,這真的就是我前面吹噓的傳奇教練嗎?我是否是在黑他?拜託,我真的沒有黑他,我尚未說完呢,既然叫戰術鬼才,怎麼可能就一套戰術,就一套比賽策略呢?是否是?那麼,盧指導的策略究竟是什麼呢?咱們來一探究竟。

 

什麼是策略模式

策略模式(Strategy Pattern):定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶,也就是算法的變化不會影響到使用算法的客戶。

 

定義一組算法,將每個算法封裝起來,使這些算法能夠相互切換。既然是一組算法,說明是不一樣的策略。能夠相互切換,說明這些策略實現了相同的接口或者繼承了相同的抽象類。

 

通俗點理解就是,解決一個問題,有多種辦法策略。就像你追女孩子,一般的策略就是請吃飯、看電影、送花、噓寒問暖等,這些就是不一樣的策略,都實現追女孩子這個方法,你想先送花仍是先請吃飯,都由你本身決定,也能夠隨意切換。

 

那咱們可愛的盧指導呢?咱們都知道NBA通常是四節比賽,除非進加時賽,每場都要面對不一樣的對手,每一節比賽場上形勢都瞬息萬變,這就須要咱們的盧指導日夜操勞,隨時制定不一樣的比賽計劃和策略去應對,這是同樣的道理。

 

 

策略模式的UML圖

 

 

 

策略模式的三個角色

 

  • Strategy(抽象策略角色):一般由一個接口或抽象類實現。此角色定義了全部的具體策略類的公共接口。

  • ConcreteStrategy(具體策略角色): 實現了抽象策略類,包裝了相關的算法或行爲。

  • Context(上下文角色): 上層訪問策略的入口,它持有抽象策略角色Strategy的引用。

 

 

假如一共有四節比賽,每節比賽都須要一個比賽策略,也就是戰術,那咱們就能夠抽象出一個比賽策略的接口,裏面有個制定戰術的方法。每一節的比賽策略就是具體策略角色,都實現了制定戰術的方法。那麼有同窗不由要問了,盧指導在策略模式裏充當什麼角色呢?沒錯,盧指導就是Context上下文角色,球員上場都須要先問盧指導打什麼戰術,執行哪個計劃策略,盧指導就給他們相應的比賽策略。下面咱們用代碼來實現一下策略模式,哦,抱歉,用代碼來看一下盧指導究竟是個什麼樣的戰術鬼才。

 

 

 

盧指導的戰術演示(策略模式代碼實例)

 

一、編寫抽象策略接口


package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能: Strategy抽象策略角色類:比賽策略接口,也能夠是抽象類* </p>** @author Moore* @ClassName Game plan.* @Version V1.0.* @date 2019.09.09 10:26:05*/public interface GameStrategy {/*** 制定比賽計劃*/void plan();}

 

二、編寫具體策略類,實現抽象策略接口


package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能: 具體策略角色* </p>** @author Moore* @ClassName First game strategy.* @Version V1.0.* @date 2019.09.09 17:58:14*/public class FirstGameStrategy implements GameStrategy {/*** 制定比賽計劃*/@Overridepublic void plan() {System.err.println("比賽計劃是:把球給樂福單打,若是拉不開分差就把球給詹姆斯,詹姆斯快想一想辦法!");}}

package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能: 具體策略角色* </p>** @author Moore* @ClassName First game strategy.* @Version V1.0.* @date 2019.09.09 17:58:14*/public class SecondGameStrategy implements GameStrategy {/*** 制定比賽計劃*/@Overridepublic void plan() {System.err.println("比賽計劃是:把球給歐文單打,若是比分落後就把球給詹姆斯,詹姆斯快想一想辦法!");}}

package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能: 具體策略角色* </p>** @author Moore* @ClassName First game strategy.* @Version V1.0.* @date 2019.09.09 17:58:14*/public class ThirdGameStrategy implements GameStrategy {/*** 制定比賽計劃*/@Overridepublic void plan() {System.err.println("比賽計劃是:把球給JR和姐夫格林投三分,若是都投不進把球給詹姆斯,詹姆斯快想一想辦法!");}}

package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能: 具體策略角色* </p>** @author Moore* @ClassName First game strategy.* @Version V1.0.* @date 2019.09.09 17:58:14*/public class LastTimeGameStrategy implements GameStrategy {/*** 制定比賽計劃*/@Overridepublic void plan() {        System.err.println("比賽計劃是:把球給詹姆斯,詹姆斯快想一想辦法!");}}

 

三、戰術大師盧指導登場,編寫Context上下文類:




package com.weiya.mazhichu.designpatterns.strategy;import lombok.Getter;import lombok.Setter;/*** <p class="detail">* 功能:Context角色,持有Strategy角色的引用* </p>** @author Moore* @ClassName Tyronn lue.* @Version V1.0.* @date 2019.09.10 10:09:56*/public class TyronnLue {@Setterprivate GameStrategy strategy;public TyronnLue(GameStrategy strategy) {this.strategy = strategy;}/*** <p class="detail">* 功能:* </p>** @author Moore* @date 2019.09.10 10:09:56*/public void play(){strategy.plan();}}

 

四、客戶端調用(帶你們回顧一下2015-16賽季NBA總決賽第七場):









package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能:總決賽,客戶端引用* </p>** @author Moore* @ClassName Finals.* @Version V1.0.* @date 2019.09.10 10:12:17*/public class Finals {/*** 主程序入口.** @param args :* @author Moore* @date 2019.09.10 10:12:17*/public static void main(String[] args) {System.out.println("---------2015-2016賽季總決賽騎士VS勇士比賽開始!---------"+"\n\n");System.out.println("------戰術鬼才盧指導制定了4節比賽計劃,並一一告訴了球員----------");System.out.println("---------第一節比賽開始,騎士球員拿出第一節的比賽計劃---------");GameStrategy first = new FirstGameStrategy();TyronnLue strategy = new TyronnLue(first);strategy.play();System.out.println("---------第一節比賽結束,騎士23:22領先-------"+"\n\n");System.out.println("---------第二節開始,騎士球員拿出第二節比賽計劃---------");GameStrategy second = new SecondGameStrategy();strategy.setStrategy(second);strategy.play();System.out.println("---------半場結束,勇士49:42領先騎士7分----------"+"\n\n");System.out.println("---------第三節開始,騎士球員拿出第三節比賽計劃---------");GameStrategy third = new ThirdGameStrategy();strategy.setStrategy(third);strategy.play();System.out.println("---------第三節結束,騎士將比分追至75:76----------"+"\n\n");System.out.println("---------第四節比賽雙方比分交替領先,不知不覺來到了最後三分鐘,兩隊戰平---------");System.out.println("---------騎士請求暫停,球員拿出關鍵時刻比賽計劃---------");GameStrategy last = new LastTimeGameStrategy();strategy.setStrategy(last);strategy.play();System.out.println("---------歐文在比賽時間還剩53秒時命中關鍵三分,幫助騎士領先3分---------");System.out.println("---------庫裏3分不中,歐文上籃不中,詹姆斯追冒伊戈達拉,詹姆斯兩罰一中,鎖定勝局!---------");System.out.println("---------最終騎士隊93-89力克勇士,以總比分4-3險勝奪冠,他們成爲史上第一支在總決賽實現1-3落後翻盤的球隊,得到2015-16賽季總冠軍----------");System.out.println("---------詹姆斯抱着樂福哭了,戰術大師泰倫盧也哭了!---------");}}

 

運行結果,看一下整場比賽,盧指導是怎樣制定策略,帶領詹歐樂體驗冠軍經歷的:

 

至此,你們應該知道了盧指導的戰術鬼才,也知道了什麼是策略模式,那麼策略模式有什麼優缺點呢?

 

策略模式優勢

  • 算法能夠自由切換(每節比賽的比賽計劃均可以隨意切換)。

  • 避免使用多重條件判斷。

  • 擴展性好(若是要加新的算法,只要實現抽象策略類就行了,假如進了加時賽,盧指導就要制定新的比賽策略就行了)。

 

策略模式缺點

  • 客戶端必須知道全部的類,而且本身決定使用哪一個類,也就是全部策略類都要暴露給客戶端,對客戶端也是個負擔(球員必須一開始就知道全部的比賽策略,可是籃球場形式瞬息萬變,戰術太多,球員要一會兒記得全部的戰術太不容易)。

  • 策略類會不少。

 

 

策略模式+簡單工廠

你覺得故事就結束了嗎?咱們可愛的盧指導既然已經知道這種比賽策略有缺點,並且已經有媒體和騎士內部球員開始說閒話,說他根本就沒有什麼戰術,就知道明牌託管,全部的戰術最後都是詹姆斯快想一想辦法,一個總冠軍仍是抱詹姆斯大腿得的,隨便換哪一個教練都行,這下可傷到了盧指導的自尊了,因而他決定不再明牌給球員和媒體了,他要像波波維奇那樣,成爲真正的受人尊敬愛戴的戰術大師,那該怎麼辦呢?

 

 

 

盧指導回家想了一個休賽期,決定再也不明牌,也不一開始就告訴球員全部的戰術,不讓球員自主選擇戰術了,要讓球員主動來問本身戰術,要樹立本身的教練威望。因而盧教練次日就執行了該想法,咱們來看看盧指導怎麼改的:

 

修改Context上下文類,由盧指導根據球員請求,盧指導動態決定給他們哪一項比賽策略,球員只須要上場打比賽執行給到的比賽計劃就好了,不用一開始就知道整個賽季全部的具體比賽計劃(具體策略類),代碼以下:

 

一、編寫Context上下文,利用簡單工廠模式:




package com.weiya.mazhichu.designpatterns.strategy;import lombok.Setter;/*** <p class="detail">* 功能:Context角色,持有Strategy角色的引用* </p>** @author Moore* @ClassName Tyronn lue.* @Version V1.0.* @date 2019.09.10 10:09:56*/public class TyronnLueContext {private GameStrategy strategy;/*** <p class="detail">* 功能:* </p>** @author Moore* @date 2019.09.10 10:09:56*/public void play(String time){switch (time) {case "first":strategy = new FirstGameStrategy();break;case "second":strategy = new ThirdGameStrategy();break;case "third":System.err.println("歐文去了凱爾特人了,仍是執行上一節的比賽策略吧");strategy = new ThirdGameStrategy();break;case "last":strategy = new LastTimeGameStrategy();break;}strategy.plan();}}

 

 

就這樣一個賽季過去了,盧教練都靠這種方式執行比賽計劃,可是質疑聲仍是不絕於耳,盧教練心想:盧瑟,等我再帶這幫崽子拿到總冠軍,大家就知道個人厲害了,到時候都得乖乖的叫我諸葛-波波-泰倫盧了,嘿嘿。盧指導想到這,嘴角不自覺的上揚起來。轉眼間,2017-2018賽季總決賽來了,詹姆斯帶領騎士艱難的再次站上總決賽的舞臺,面對老對手勇士。下面看看兩隊的比勝過程,以及盧指導的神策略。

 

二、客戶端調用














package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能: 2018總決賽,客戶端引用* </p>** @author Moore* @ClassName Finals.* @Version V1.0.* @date 2019.09.10 10:12:17*/public class Finals2018 {/*** 主程序入口.** @param args :* @author Moore* @date 2019.09.10 10:12:17*/public static void main(String[] args) {System.out.println("---------2017-2018賽季總決賽騎士VS勇士第一場比賽正式開始!---------"+"\n\n");System.out.println("------戰術鬼才盧指導制定了比賽計劃,讓球員來詢問本身的時候,再告訴他們怎麼執行----------");System.out.println("---------第一節比賽開始,騎士球員請求第一節的比賽計劃---------");TyronnLueContext strategy = new TyronnLueContext();strategy.play("first");System.out.println("---------第一節比賽結束-------"+"\n\n");System.out.println("---------第二節開始,騎士球員拿出第二節比賽計劃---------");TyronnLueContext strategy2 = new TyronnLueContext();strategy2.play("second");System.out.println("---------半場結束----------"+"\n\n");System.out.println("---------第三節開始,騎士球員拿出第三節比賽計劃---------");TyronnLueContext strategy3 = new TyronnLueContext();strategy3.play("third");System.out.println("---------第三節結束----------"+"\n\n");System.out.println("---------第四節比賽騎士一直領先,最後時刻勇士追平比分---------");System.out.println("--------最後9秒,JR搶到籃板,詹姆斯想問問盧指導有沒有暫停,想請求比賽計劃----------");TyronnLueContext strategy4 = new TyronnLueContext();System.err.println("本來詹姆斯想讓JR請求的關鍵時刻的戰術是--->");strategy4.play("last");System.out.println("---------可是盧指導懵逼中,不知道有沒有暫停,也忘記返回比賽策略---------");System.out.println("---------JR也懵逼了,抱着球思考人生,時間走完,勇士將比賽拖進加時賽---------");System.out.println("---------詹姆斯心灰意冷砍下50分,勇士士氣正旺,4:0橫掃騎士得到2017-2018賽季總冠軍!----------");}}

 

運行,查看結果:

 

至此,咱們又一次見到了盧教練的神操做,雖然改善了策略模式,可是由於本身和球員有時候神經短路,最終騎士輸掉了總冠軍。詹姆斯遠走洛杉磯,盧指導也解甲歸田,江湖今後只剩下他的傳說了。

咱們先不關心改變策略帶來的結果,咱們就只看一下策略模式結合簡單工廠模式帶來的優缺點。

優勢

  • 策略模式與簡單工廠結合的用法,客戶端就只須要認識一個Context上下文工廠類就能夠了。耦合更加下降,達到解耦目的。

     

  • 抽象策略的方法和客戶端分離,客戶端不用關心具體策略怎麼制定算法的,只要經過上下文對象就能夠拿到算法結果。

 

缺點

  • 每次增長算法,除了須要實現抽象策略接口,還要修改Context上下文工廠類,違背了「開閉原則」。

  • Context上下文類中if/else或則swith/case判斷條件太多,不易維護。

 

 

策略模式+簡單工廠+反射

 

最近據說盧指導要復出了,目的地是洛杉磯快船隊,和雞湯教父裏弗斯相聚,雙份雞湯戰術大師相聚,加上卡哇伊喬治和一衆狠角色,新賽季勢必勢如破竹,隔壁湖人隊更衣室某老漢凶多吉少。

 

這兒要說的是,據說盧指導休息的這一年可沒有白閒着,在家閉門修煉策略模式,他以爲18賽季的比賽策略不是很好,雖然球員和媒體不知道他變幻無常的戰術了,可是他本身做爲上下文類,裏面有太多的if/else和swith/case條件判斷,一個賽季82場比賽,要寫那麼多條件,太爲難本盧寶寶了。

 

因而,盧寶寶又想了個好辦法,他決定本身不去管理這麼多具體策略了,每次只要有新增的策略,就交給另外一個助教去保管,這樣每次球員請求比賽策略的時候,他只要從助教那兒拿過來就行了。想到這,盧寶寶又開心的笑起來了,心想本身真是個戰術天才,嘿嘿。

 

那咱們就來看看盧指導是怎樣改變他的策略的。

 

一、由助教管理全部的具體策略,這兒用枚舉類




package com.weiya.mazhichu.designpatterns.strategy;import lombok.Getter;import lombok.Setter;/*** <p class="detail">* 功能: 全部具體策略類放枚舉裏* </p>** @author Moore* @ClassName Concrete strategy enum.* @Version V1.0.* @date 2019.09.11 09:59:40*/public enum ConcreteStrategyEnum {FIRST("com.weiya.mazhichu.designpattern.strategy.FirstGameStrategy"),SECOND("com.weiya.mazhichu.designpattern.strategy.SecondGameStrategy"),THIRD("com.weiya.mazhichu.designpattern.strategy.ThirdGameStrategy"),LAST("com.weiya.mazhichu.designpattern.strategy.LastGameStrategy"),;@Getter@Setterprivate String className;ConcreteStrategyEnum(String className) {this.className = className;}}

 

二、盧寶寶本身不用那麼多條件判斷了,改用反射獲取,修改Context上下文類




package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能:Context角色,持有Strategy角色的引用* </p>** @author Moore* @ClassName Tyronn lue.* @Version V1.0.* @date 2019.09.10 10:09:56*/public class TyronnLueReflectContext {private GameStrategy strategy;/*** <p class="detail">* 功能: 利用反射動態獲取具體策略類* </p>** @param time :* @throws Exception the exception* @author Moore* @date 2019.09.11 10:02:20*/public static GameStrategy getStrategy(String time) throws Exception {String className = ConcreteStrategyEnum.valueOf(time).getClassName();return (GameStrategy) Class.forName(className).newInstance();}}

 

三、新賽季還未開始,又因盧教練對16賽季的騎士還有感情,因此就用老隊員模擬了球員請求比賽策略的場景










package com.weiya.mazhichu.designpatterns.strategy;/*** <p class="detail">* 功能:盧指導模擬客戶端請求結合工廠模式+反射的策略模式* </p>** @author Moore* @ClassName Nba 2019.* @Version V1.0.* @date 2019.09.11 10:15:29*/public class NBA2019 {public static void main(String[] args) throws Exception {System.out.println("---------2019-20賽季揭幕戰---------"+"\n\n");System.out.println("------戰術鬼才盧指導制定了比賽計劃,讓球員來詢問本身的時候,再告訴他們怎麼執行----------"+"\n\n");System.out.println("---------第一節比賽開始,騎士球員請求第一節的比賽計劃---------");GameStrategy strategy = TyronnLueReflectContext.getStrategy("FIRST");strategy.plan();System.out.println("---------第一節比賽結束-------"+"\n\n");System.out.println("---------第二節開始,騎士球員請求第二節比賽計劃---------");strategy = TyronnLueReflectContext.getStrategy("SECOND");strategy.plan();System.out.println("---------半場結束----------"+"\n\n");System.out.println("---------第三節開始,騎士球員請求第三節比賽計劃---------");strategy = TyronnLueReflectContext.getStrategy("THIRD");strategy.plan();System.out.println("---------第三節結束----------"+"\n\n");System.out.println("---------第四節雙方交替領先,比賽來到最後兩分鐘,騎士球員請求關鍵時刻戰術---------");strategy = TyronnLueReflectContext.getStrategy("LAST");strategy.plan();System.out.println("---------比賽結束,詹姆斯35+9+8準三雙率隊險勝快船,取得開門紅!----------");}}

 

運行,看看盧指導的模擬結果:

 

我不知道盧寶寶新賽季會展示什麼樣的神操做,但至少他帶咱們一步步深刻理解了策略模式,咱們看一下結合工廠模式+反射的策略模式,作了哪些改進,有什麼好處。

 

優勢

  • 去除了Context中的條件判斷,使代碼易於讀懂和維護。

  • 擴展性好,增長算法只須要增長對應的策略實現類和枚舉,符合開閉原則。

 

好了,策略模式就講到這兒了,但願在盧指導的buff加成下,你們都能理解策略模式。最後,不知道說什麼了,再來一個盧指導的表情包吧,也送給全部碼之初的小夥伴。

 

 

我不能保證我寫的文章都是正確的,可是我能保證都是我本身花時間用心寫的,全部的代碼示例都是原創,全部的理解都只是我我的理解,不能表明官方權威,因此請各位讀者閱讀時帶着批判的眼光,有選擇性的認同,謝謝!

 

若是以爲本文有用,請推薦給您身邊的人或者同行關注「碼之初」公衆號,讓咱們一塊兒前行,謝謝!

若是發現文章中有問題或者代碼裏有bug,歡迎留言,請隨時批評指正,謝謝!

爲了感謝您的關注和喜好,碼之初爲正在找工做的小夥伴悄悄的送上一批乾貨,後臺發送」面試「關鍵字,便可領取隨機一份面試資料,祝全部小夥伴步步高昇,前程似錦!

另外,後臺回覆「設計模式」,可獲取設計模式源碼地址。

相關文章
相關標籤/搜索