Java設(shè)計(jì)模式-策略模式

定義

Define a family of algorithms,encapsulate each one,and make them interchangeable.

定義一組算法,將每個(gè)算法都封裝起來歼狼,并且使它們之間可以互換绪穆。

策略模式使用的就是面向?qū)ο蟮睦^承和多態(tài)機(jī)制蘸际,非常容易理解和掌握

實(shí)現(xiàn)

抽象策略

策略苦始、算法家族的抽象擂达,通常為接口蹋肮,也可以是抽象類待侵,定義每個(gè)策略或算法必須具有的方法和屬性丢早。

public interface Strategy {

    /**
     * 策略模式的運(yùn)算法則
     */
    void doSomething();
}

具體策略

實(shí)現(xiàn)抽象策略中的操作,該類含有具體的算法

public class ConcreteStrategyA implements Strategy {
    @Override
    public void doSomething() {
        System.out.println("具體策略A的運(yùn)算法則");
    }
}
public class ConcreteStrategyB implements Strategy {
    @Override
    public void doSomething() {
        System.out.println("具體策略B的運(yùn)算法則");
    }
}

封裝類

也叫做上下文類或環(huán)境類秧倾,起承上啟下封裝作用怨酝,屏蔽高層模塊對(duì)策略、算法的直接訪問那先,封裝可能存在的變化农猬。

策略模式的重點(diǎn)就是封裝角色,它是借用了代理模式的思路售淡,和代理模式的差別就是策略模式的封裝角色和被封裝的策略類不用是同一個(gè)接口斤葱,如果是同一個(gè)接口那就成為了代理模式。

public class Context {

    /**
     * 抽象策略
     */
    private Strategy strategy;

    /**
     * 構(gòu)造函數(shù)設(shè)置具體策略
     *
     * @param strategy
     */
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 封裝后的策略方法
     */
    public void executeStrategy() {
        this.strategy.doSomething();
    }
}

客戶端代碼

public class Client {

    public static void main(String[] args) {
        // 聲明一個(gè)具體的策略
        Strategy strategyA = new ConcreteStrategyA();
        // 聲明上下文對(duì)象
        Context contextA = new Context(strategyA);
        // 執(zhí)行封裝后的方法
        contextA.executeStrategy();

        Strategy strategyB = new ConcreteStrategyB();
        Context contextB = new Context(strategyB);
        contextB.executeStrategy();
    }
}

優(yōu)點(diǎn)

  • 算法可以自由切換

    這是策略模式本身定義的揖闸,只要實(shí)現(xiàn)抽象策略揍堕,它就成為策略家族的一個(gè)成員,通過封裝角色對(duì)其進(jìn)行封裝汤纸,保證對(duì)外提供“可自由切換”的策略衩茸。

  • 避免使用多重條件判斷

    如果沒有策略模式,一個(gè)策略家族有5個(gè)策略算法贮泞,一會(huì)要使用A策略楞慈,一會(huì)要使用B策略,怎么設(shè)計(jì)呢啃擦?使用多重的條件語句抖部?多重條件語句不易維護(hù),而且出錯(cuò)的概率大大增強(qiáng)议惰。使用策略模式后,可以由其他模塊決定采用何種策略乡恕,策略家族對(duì)外提供的訪問接口就是封裝類言询,簡(jiǎn)化了操作俯萎,同時(shí)避免了條件語句判斷。

  • 擴(kuò)展性良好

    在現(xiàn)有的系統(tǒng)中增加一個(gè)策略太容易了运杭,只要實(shí)現(xiàn)接口就可以了夫啊,其他都不用修改,類似于一個(gè)可反復(fù)拆卸的插件辆憔,這大大地符合了OCP原則撇眯。

缺點(diǎn)

  • 策略類數(shù)量增多

    每一個(gè)策略都是一個(gè)類,復(fù)用的可能性很小虱咧,類數(shù)量增多熊榛。

  • 所有的策略類都需要對(duì)外暴露

    上層模塊必須知道有哪些策略,然后才能決定使用哪一個(gè)策略腕巡,這與迪米特法則是相違背的(只是想使用一個(gè)策略玄坦,卻要要了解這個(gè)策略)』娉粒可以使用其他模式來修正這個(gè)缺陷煎楣,如工廠方法模式、代理模式或享元模式车伞。

使用場(chǎng)景

  • 多個(gè)類只有在算法或行為上稍有不同的場(chǎng)景择懂。

  • 算法需要自由切換的場(chǎng)景。

    例如另玖,算法的選擇是由使用者決定的困曙,或者算法始終在進(jìn)化,特別是一些站在技術(shù)前沿的行業(yè)日矫,連業(yè)務(wù)專家都無法給你保證這樣的系統(tǒng)規(guī)則能夠存在多長(zhǎng)時(shí)間赂弓,在這種情況下策略模式是你最好的助手。

  • 需要屏蔽算法規(guī)則的場(chǎng)景哪轿。

    現(xiàn)在的科技發(fā)展得很快盈魁,人腦的記憶是有限的(就目前來說是有限的),太多的算法你只要知道一個(gè)名字就可以了窃诉,傳遞相關(guān)的數(shù)字進(jìn)來杨耙,反饋一個(gè)運(yùn)算結(jié)果,萬事大吉飘痛。

注意事項(xiàng)

如果系統(tǒng)中的一個(gè)策略家族的具體策略數(shù)量超過4個(gè)珊膜,則需要考慮使用混合模式,解決策略類膨脹和對(duì)外暴露的問題宣脉,否則日后的系統(tǒng)維護(hù)就會(huì)成為一個(gè)燙手山芋车柠,誰都不想接。

最佳實(shí)踐

策略模式是一個(gè)非常簡(jiǎn)單的模式(主要是用了Java繼承與多態(tài)的機(jī)制)。它在項(xiàng)目中使用得非常多竹祷,但單獨(dú)使用的地方就比較少了谈跛,因?yàn)樗兄旅毕荩?strong>所有的策略都需要暴露出去,這樣才方便客戶端決定使用哪一個(gè)策略塑陵。我們的策略模式只是實(shí)現(xiàn)了策略的管理感憾,但是沒有嚴(yán)格地定義“適當(dāng)?shù)膱?chǎng)景”使用“適當(dāng)?shù)牟呗浴保趯?shí)際項(xiàng)目中令花,一般通過工廠方法模式來實(shí)現(xiàn)策略類的聲明阻桅。

擴(kuò)展(策略枚舉)

定義
  • 它是一個(gè)枚舉。
  • 它是一個(gè)濃縮了的策略模式的枚舉兼都。

啥意思嫂沉?來看代碼:

定義一個(gè)計(jì)算器(枚舉類)
public enum Calculator {
    PLUS("+") {
        public int exec(int x, int y) {
            return x + y;
        }
    },
    MINUS("-") {
        public int exec(int x, int y) {
            return x - y;
        }
    };

    private final String symbol;

    Calculator(String symbol) {
        this.symbol = symbol;
    }

    public String getSymbol() {
        return this.symbol;
    }

    /**
     * 聲明一個(gè)抽象方法
     * 枚舉類型中的抽象方法必須被它的所有常量中的具體方法所覆蓋(被稱為特定于常量的方法實(shí)現(xiàn))
     */
    public abstract int exec(int a, int b);
}

把原有定義在抽象策略中的方法移植到枚舉中,每個(gè)枚舉成員就成為一個(gè)具體策略

客戶端代碼
public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 10;
        System.out.println(x + " + " + y + " = " + Calculator.PLUS.exec(x, y));
        System.out.println(x + " - " + y + " = " + Calculator.MINUS.exec(x, y));
    }
}

代碼量非常少俯抖,而且還有一個(gè)顯著的優(yōu)點(diǎn):真實(shí)地面向?qū)ο?/p>

Calculator.PLUS.exec(x, y)類似于“拿出計(jì)算器(Calculator)输瓜,對(duì)x和y進(jìn)行加法運(yùn)算(MINUS),并立刻執(zhí)行(exec)”芬萍,這與我們?nèi)粘=佑|邏輯非常相似

策略枚舉是一個(gè)非常優(yōu)秀和方便的模式(《Effective Java》中枚舉相關(guān)條目也有詳細(xì)介紹該模式)尤揣,但是它受枚舉類型的限制,每個(gè)枚舉項(xiàng)都是public柬祠、final北戏、static的,擴(kuò)展性受到了一定的約束漫蛔,因此在系統(tǒng)開發(fā)中嗜愈,策略枚舉一般擔(dān)當(dāng)不經(jīng)常發(fā)生變化的角色。

源碼地址:https://gitee.com/tianranll/java-design-patterns.git

參考文獻(xiàn):《設(shè)計(jì)模式之禪》莽龟、《Effective Java》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蠕嫁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子毯盈,更是在濱河造成了極大的恐慌剃毒,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搂赋,死亡現(xiàn)場(chǎng)離奇詭異赘阀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)脑奠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門基公,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宋欺,你說我怎么就攤上這事轰豆∫任椋” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵秒咨,是天一觀的道長(zhǎng)喇辽。 經(jīng)常有香客問我,道長(zhǎng)雨席,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任吠式,我火速辦了婚禮陡厘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘特占。我一直安慰自己糙置,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布是目。 她就那樣靜靜地躺著谤饭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪懊纳。 梳的紋絲不亂的頭發(fā)上揉抵,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音嗤疯,去河邊找鬼冤今。 笑死,一個(gè)胖子當(dāng)著我的面吹牛茂缚,可吹牛的內(nèi)容都是我干的戏罢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脚囊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼龟糕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悔耘,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤讲岁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后淮逊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體催首,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年泄鹏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郎任。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡备籽,死狀恐怖舶治,靈堂內(nèi)的尸體忽然破棺而出分井,到底是詐尸還是另有隱情,我是刑警寧澤霉猛,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布尺锚,位于F島的核電站,受9級(jí)特大地震影響惜浅,放射性物質(zhì)發(fā)生泄漏瘫辩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一坛悉、第九天 我趴在偏房一處隱蔽的房頂上張望伐厌。 院中可真熱鬧,春花似錦裸影、人聲如沸挣轨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卷扮。三九已至,卻和暖如春均践,著一層夾襖步出監(jiān)牢的瞬間晤锹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工浊猾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抖甘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓葫慎,卻偏偏與公主長(zhǎng)得像衔彻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子偷办,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 今天給大家說說田忌賽馬的故事艰额。如有雷同,純屬巧合椒涯!話說在戰(zhàn)國(guó)時(shí)期柄沮,群雄割據(jù),硝煙四起废岂,茶余飯后還是少不了娛樂活動(dòng)的...
    Jet啟思閱讀 5,506評(píng)論 4 17
  • 定義 策略模式屬于對(duì)象的行為模式祖搓。其用意是針對(duì)一組算法,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中湖苞,從而使得它們可...
    步積閱讀 767評(píng)論 0 2
  • 一拯欧、定義 策略模式是指對(duì)一系列的算法定義,并將每一個(gè)算法封裝起來财骨,而且使它們還可以相互替換镐作。策略模式讓算法獨(dú)立于使...
    怡紅快綠閱讀 881評(píng)論 0 0
  • 策略模式屬于對(duì)象的行為模式藏姐。其用意是針對(duì)一組算法,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中该贾,從而使得它們可以相互...
    夢(mèng)想編織者灬小楠閱讀 732評(píng)論 2 5
  • 在我們需要實(shí)現(xiàn)一個(gè)功能的時(shí)候羔杨,可以有多種算法來實(shí)現(xiàn)的時(shí)候,我們可以使用if...else或者case來選擇對(duì)應(yīng)的算...
    MrKing5946閱讀 220評(píng)論 0 0