我的Java設計模式-策略模式

今天給大家說說田忌賽馬的故事。如有雷同,純屬巧合!話說在戰國時期,群雄割據,硝煙四起,茶余飯后還是少不了娛樂活動的,其中賽馬是最火爆的。一天,孫臏看到田忌像個死雞似的就知道肯定賽馬又輸給了齊威王,立馬抓住田忌去跟齊威王再賽一場。

孫臏:“小忌啊,哥哥看著你心疼啊,哥哥出對策幫你贏一盤如何?”。

田忌聽到之后高興得飛起,瞪大了兩只金魚眼“Really?只要能贏,我赴湯蹈火,以身相許又如何~”。

孫臏心里一萬個草泥馬在奔騰,差點沒噎死自己“滾一邊去,我們這盤跟他show hand!”賽馬開始,策略模式上場。此處應該有bgm“讓我們紅塵作伴活得瀟瀟灑灑 策馬奔騰共享人世繁華...呀啊呀啊,呀啊啊啊啊啊啊~”

一、策略模式

定義

定義一組算法,將每一個算法封裝起來,從而使它們可以相互切換。

特點

1)一組算法,那就是不同的策略。

2)這組算法都實現了相同的接口或者繼承相同的抽象類,所以可以相互切換

UML

策略模式UML圖.png

策略模式涉及到的角色有三個:

- 封裝角色:上層訪問策略的入口,它持有抽象策略角色的引用。

- 抽象策略角色:提供接口或者抽象類,定義策略組必須擁有的方法和屬性。

- 具體策略角色:實現抽象策略,定義具體的算法邏輯。

二、實戰

在跟齊威王比賽之前來分析下之前輸掉比賽的“策略”,首先來看封裝角色,代碼如下:

public class Context {

    private Strategy strategy;

    /**
     * 傳進的是一個具體的策略實例
     * @param strategy
     */
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 調用策略
     */
    public void contextInterface() {
        strategy.algorithmLogic();
    }

}

Context持有Strategy的引用,并且提供了調用策略的方法,很清晰。

再來抽象策略角色,定義了策略組的方法,代碼如下:

public interface Strategy {

    public void algorithmLogic();

}

輸掉比賽的“策略”也是一種策略,是具體策略角色類,來看代碼:

public class ConcreteStrategyA implements Strategy{

    @Override
    public void algorithmLogic() {
        // 具體的算法邏輯(輸了比賽)
        System.out.println("第一場:上等馬vs上等馬  第二場:中等馬vs中等馬  第三場:下等馬vs下等馬  賽果:輸!");
    }
}

看到這里,孫臏一陣無語,慘不忍睹也得看結果的,客戶端代碼如下:

public class Client {

    public static void main(String[] args) {
        // 操控比賽,這場要輸
        Context context = new Context(new ConcreteStrategyA());
        context.contextInterface();
    }
}

兩句代碼,傳入具體策略對象,調用策略入口方法,運行結果如下:

第一場:上等馬vs上等馬 第二場:中等馬vs中等馬 第三場:下等馬vs下等馬 賽果:輸!

田忌跟孫臏說:“臏哥,我怕!”,孫臏:“不用怕,哥哥在!”。

田忌找到齊威王“大王,我們再...再再來一盤,輸了請吃飯”

瞅瞅孫臏出的策略,一睹軍事家的風采,“贏”的具體策略類代碼如下:

public class ConcreteStrategyB implements Strategy{
    @Override
    public void algorithmLogic() {
        // 贏
        System.out.println("第一場:下等馬vs上等馬  第二場:上等馬vs中等馬  第三場:中等馬vs下等馬  賽果:贏!");
    }
}

再來看客戶端的代碼:

public class Client {
    public static void main(String[] args) {

        // 操控比賽,這場要贏,哈哈哈
        Context context = new Context(new ConcreteStrategyB());
        context.contextInterface();
    }
}

運行結果如下:

第一場:下等馬vs上等馬 第二場:上等馬vs中等馬 第三場:中等馬vs下等馬 賽果:贏!

田忌拍爛手掌,重要的是今天晚飯有著落了,還要對臏哥哥以身相許的......

三、策略模式的優缺點

優點

1)良好的擴展性。增加一種策略,只要實現接口,寫上具體邏輯就可以了。當舊策略不需要時,直接剔除就行。

2)良好的封裝性。策略的入口封裝在Context封裝類中,客戶端只要知道使用哪種策略就傳哪種策略對象就可以了。

3)避免了像簡單工廠模式這樣的多重條件判斷。

缺點

1)客戶端必須了解策略組的各個策略,并且決定使用哪一個策略,也就是各個策略需要暴露給客戶端。

2)如果策略增多,策略類的數量就會增加。

四、擴展

上面說到策略模式有一個缺點,就是所有的策略都必須暴露出去,讓客戶端自行選擇策略使用。現在來改善這一缺陷,而改善這個缺陷需要跟簡單工廠模式結合混編,繼續往下看。

當然,軍事家孫臏也會想到這一點,怎么可能會把自己的套路全都暴露給別人呢,那還怎么玩是吧。不過,歷史上并沒有說孫臏改善了這點,現在是我來改善這個缺陷,哈哈哈~

策略工廠

思考一個問題,策略暴露了,改善就是把策略隱藏起來,而工廠模式就有這個效果,客戶端不需要知道策略具體是什么,只知道結果就好。OK,那么我們可以使用工廠模式把策略當做產品生成嗎?答案是肯定的。策略模式的入口就在Context封裝類,可以從這個角色做手腳。先看代碼:

public class Context {

    private Strategy strategy;

    // 把創建策略放在封裝角色內,客戶端只需要知道結果
    public void factory(String strategyType) {
        if (strategyType.equals("WIN")) {
            strategy = new ConcreteStrategyB();
        } else if (strategyType.equals("LOSE")) {
            strategy = new ConcreteStrategyA();
        }
    }

    /**
     * 調用策略
     */
    public void contextInterface() {
        strategy.algorithmLogic();
    }
}

代碼很簡單,增加了factory的方法,這個方法作用就是創建策略對象。這樣,客戶端就不需要去理解具體的策略,只需知道具體策略的結果就好。看看客戶端代碼:

public class Client {

    public static void main(String[] args) {
        Context context = new Context();
        context.factory("LOSE");
        context.contextInterface();
    }
}

總結

注意策略模式和工廠方法模式的區別,在前面工廠方法模式中有說到,這里就不再闡述。策略模式本身也相對比較簡單,重點在它的擴展以及其它模式的對比,分析各自的優缺點。來看看策略工廠這樣的模式存在缺點嗎?很明顯,如果需要添加或者淘汰一種策略,Context就必須修改,這并不符合開閉原則。在《設計模式之禪》中的提出通過策略枚舉和反射機制對策略模式進行改良,膜拜了~但是要添加或淘汰策略,還是得去對枚舉進行修改,也不符合開閉原則。根據自己項目情況,選擇最適合自己項目的模式。下一篇是責任鏈模式,歡迎繼續關注,goodbye!

設計模式Java源碼GitHub下載https://github.com/jetLee92/DesignPattern

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,611評論 25 708
  • 設計模式匯總 一、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,957評論 1 15
  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,796評論 2 17
  • 2017年1月29日 周日 雨 今天,我被一根線給刮到了額頭,而且很紅,有一點點血液。有一點點痛痛的,只是一點皮外...
    葉賴子閱讀 261評論 1 2
  • 今天是星期天,也是世界讀書日。帶上喜歡的書,約上可愛的人兒,來到了美麗的地方。 第一站:靖港古鎮 逛街、賞景…… ...
    碧葉青蓮閱讀 388評論 0 0