今天給大家說說田忌賽馬的故事。如有雷同,純屬巧合!話說在戰(zhàn)國時(shí)期,群雄割據(jù),硝煙四起,茶余飯后還是少不了娛樂活動(dòng)的,其中賽馬是最火爆的。一天,孫臏看到田忌像個(gè)死雞似的就知道肯定賽馬又輸給了齊威王,立馬抓住田忌去跟齊威王再賽一場(chǎng)。
孫臏:“小忌啊,哥哥看著你心疼啊,哥哥出對(duì)策幫你贏一盤如何?”。
田忌聽到之后高興得飛起,瞪大了兩只金魚眼“Really?只要能贏,我赴湯蹈火,以身相許又如何~”。
孫臏心里一萬個(gè)草泥馬在奔騰,差點(diǎn)沒噎死自己“滾一邊去,我們這盤跟他show hand!”賽馬開始,策略模式上場(chǎng)。此處應(yīng)該有bgm“讓我們紅塵作伴活得瀟瀟灑灑 策馬奔騰共享人世繁華...呀啊呀啊,呀啊啊啊啊啊啊~”

一、策略模式
定義
定義一組算法,將每一個(gè)算法封裝起來,從而使它們可以相互切換。
特點(diǎn)
1)一組算法,那就是不同的策略。
2)這組算法都實(shí)現(xiàn)了相同的接口或者繼承相同的抽象類,所以可以相互切換。
UML

策略模式涉及到的角色有三個(gè):
- 封裝角色:上層訪問策略的入口,它持有抽象策略角色的引用。
- 抽象策略角色:提供接口或者抽象類,定義策略組必須擁有的方法和屬性。
- 具體策略角色:實(shí)現(xiàn)抽象策略,定義具體的算法邏輯。
二、實(shí)戰(zhàn)
在跟齊威王比賽之前來分析下之前輸?shù)舯荣惖摹安呗浴?,首先來看封裝角色,代碼如下:
public class Context {
private Strategy strategy;
/**
* 傳進(jìn)的是一個(gè)具體的策略實(shí)例
* @param strategy
*/
public Context(Strategy strategy) {
this.strategy = strategy;
}
/**
* 調(diào)用策略
*/
public void contextInterface() {
strategy.algorithmLogic();
}
}
Context持有Strategy的引用,并且提供了調(diào)用策略的方法,很清晰。
再來抽象策略角色,定義了策略組的方法,代碼如下:
public interface Strategy {
public void algorithmLogic();
}
輸?shù)舯荣惖摹安呗浴币彩且环N策略,是具體策略角色類,來看代碼:
public class ConcreteStrategyA implements Strategy{
@Override
public void algorithmLogic() {
// 具體的算法邏輯(輸了比賽)
System.out.println("第一場(chǎng):上等馬vs上等馬 第二場(chǎng):中等馬vs中等馬 第三場(chǎng):下等馬vs下等馬 賽果:輸!");
}
}
看到這里,孫臏一陣無語,慘不忍睹也得看結(jié)果的,客戶端代碼如下:
public class Client {
public static void main(String[] args) {
// 操控比賽,這場(chǎng)要輸
Context context = new Context(new ConcreteStrategyA());
context.contextInterface();
}
}
兩句代碼,傳入具體策略對(duì)象,調(diào)用策略入口方法,運(yùn)行結(jié)果如下:
第一場(chǎng):上等馬vs上等馬 第二場(chǎng):中等馬vs中等馬 第三場(chǎng):下等馬vs下等馬 賽果:輸!
田忌跟孫臏說:“臏哥,我怕!”,孫臏:“不用怕,哥哥在!”。
田忌找到齊威王“大王,我們?cè)?..再再來一盤,輸了請(qǐng)吃飯”
瞅瞅?qū)O臏出的策略,一睹軍事家的風(fēng)采,“贏”的具體策略類代碼如下:
public class ConcreteStrategyB implements Strategy{
@Override
public void algorithmLogic() {
// 贏
System.out.println("第一場(chǎng):下等馬vs上等馬 第二場(chǎng):上等馬vs中等馬 第三場(chǎng):中等馬vs下等馬 賽果:贏!");
}
}
再來看客戶端的代碼:
public class Client {
public static void main(String[] args) {
// 操控比賽,這場(chǎng)要贏,哈哈哈
Context context = new Context(new ConcreteStrategyB());
context.contextInterface();
}
}
運(yùn)行結(jié)果如下:
第一場(chǎng):下等馬vs上等馬 第二場(chǎng):上等馬vs中等馬 第三場(chǎng):中等馬vs下等馬 賽果:贏!
田忌拍爛手掌,重要的是今天晚飯有著落了,還要對(duì)臏哥哥以身相許的......
三、策略模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
1)良好的擴(kuò)展性。增加一種策略,只要實(shí)現(xiàn)接口,寫上具體邏輯就可以了。當(dāng)舊策略不需要時(shí),直接剔除就行。
2)良好的封裝性。策略的入口封裝在Context封裝類中,客戶端只要知道使用哪種策略就傳哪種策略對(duì)象就可以了。
3)避免了像簡(jiǎn)單工廠模式這樣的多重條件判斷。
缺點(diǎn)
1)客戶端必須了解策略組的各個(gè)策略,并且決定使用哪一個(gè)策略,也就是各個(gè)策略需要暴露給客戶端。
2)如果策略增多,策略類的數(shù)量就會(huì)增加。
四、擴(kuò)展
上面說到策略模式有一個(gè)缺點(diǎn),就是所有的策略都必須暴露出去,讓客戶端自行選擇策略使用。現(xiàn)在來改善這一缺陷,而改善這個(gè)缺陷需要跟簡(jiǎn)單工廠模式結(jié)合混編,繼續(xù)往下看。
當(dāng)然,軍事家孫臏也會(huì)想到這一點(diǎn),怎么可能會(huì)把自己的套路全都暴露給別人呢,那還怎么玩是吧。不過,歷史上并沒有說孫臏改善了這點(diǎn),現(xiàn)在是我來改善這個(gè)缺陷,哈哈哈~
策略工廠
思考一個(gè)問題,策略暴露了,改善就是把策略隱藏起來,而工廠模式就有這個(gè)效果,客戶端不需要知道策略具體是什么,只知道結(jié)果就好。OK,那么我們可以使用工廠模式把策略當(dāng)做產(chǎn)品生成嗎?答案是肯定的。策略模式的入口就在Context封裝類,可以從這個(gè)角色做手腳。先看代碼:
public class Context {
private Strategy strategy;
// 把創(chuàng)建策略放在封裝角色內(nèi),客戶端只需要知道結(jié)果
public void factory(String strategyType) {
if (strategyType.equals("WIN")) {
strategy = new ConcreteStrategyB();
} else if (strategyType.equals("LOSE")) {
strategy = new ConcreteStrategyA();
}
}
/**
* 調(diào)用策略
*/
public void contextInterface() {
strategy.algorithmLogic();
}
}
代碼很簡(jiǎn)單,增加了factory的方法,這個(gè)方法作用就是創(chuàng)建策略對(duì)象。這樣,客戶端就不需要去理解具體的策略,只需知道具體策略的結(jié)果就好??纯纯蛻舳舜a:
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.factory("LOSE");
context.contextInterface();
}
}
總結(jié)
注意策略模式和工廠方法模式的區(qū)別,在前面工廠方法模式中有說到,這里就不再闡述。策略模式本身也相對(duì)比較簡(jiǎn)單,重點(diǎn)在它的擴(kuò)展以及其它模式的對(duì)比,分析各自的優(yōu)缺點(diǎn)。來看看策略工廠這樣的模式存在缺點(diǎn)嗎?很明顯,如果需要添加或者淘汰一種策略,Context就必須修改,這并不符合開閉原則。在《設(shè)計(jì)模式之禪》中的提出通過策略枚舉和反射機(jī)制對(duì)策略模式進(jìn)行改良,膜拜了~但是要添加或淘汰策略,還是得去對(duì)枚舉進(jìn)行修改,也不符合開閉原則。根據(jù)自己項(xiàng)目情況,選擇最適合自己項(xiàng)目的模式。下一篇是責(zé)任鏈模式,歡迎繼續(xù)關(guān)注,goodbye!
設(shè)計(jì)模式Java源碼GitHub下載:https://github.com/jetLee92/DesignPattern