個人Java設計模式-策略模式

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

孫臏:「小忌啊,哥哥看着你心疼啊,哥哥出對策幫你贏一盤如何?」。github

田忌聽到以後高興得飛起,瞪大了兩隻金魚眼「Really?只要能贏,我赴湯蹈火,以身相許又如何~」。算法

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

1、策略模式

定義

  定義一組算法,將每個算法封裝起來,從而使它們能夠相互切換。設計模式

特色

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

  2)這組算法都實現了相同的接口或者繼承相同的抽象類,因此能夠相互切換this

UML

策略模式UML圖.png

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

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

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

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

2、實戰

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

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下等馬 賽果:贏!

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

3、策略模式的優缺點

優勢

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

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

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

缺點

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

2)若是策略增多,策略類的數量就會增長。

4、擴展

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

固然,軍事家孫臏也會想到這一點,怎麼可能會把本身的套路全都暴露給別人呢,那還怎麼玩是吧。不過,歷史上並無說孫臏改善了這點,如今是我來改善這個缺陷,哈哈哈~

策略工廠

思考一個問題,策略暴露了,改善就是把策略隱藏起來,而工廠模式就有這個效果,客戶端不須要知道策略具體是什麼,只知道結果就好。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

相關文章
相關標籤/搜索