31 設(shè)計模式——策略模式(策略設(shè)計模式)詳解

在現(xiàn)實生活中常常遇到實現(xiàn)某種目標(biāo)存在多種策略可供選擇的情況谣沸,例如刷钢,出行旅游可以乘坐飛機、乘坐火車乳附、騎自行車或自己開私家車等内地,超市促銷可以釆用打折、送商品许溅、送積分等方法瓤鼻。

在軟件開發(fā)中也常常遇到類似的情況,當(dāng)實現(xiàn)某一個功能存在多種算法或者策略贤重,我們可以根據(jù)環(huán)境或者條件的不同選擇不同的算法或者策略來完成該功能茬祷,如數(shù)據(jù)排序策略有冒泡排序、選擇排序并蝗、插入排序祭犯、二叉樹排序等。

如果使用多重條件轉(zhuǎn)移語句實現(xiàn)(即硬編碼)滚停,不但使條件語句變得很復(fù)雜沃粗,而且增加、刪除或更換算法要修改原代碼键畴,不易維護最盅,違背開閉原則。如果采用策略模式就能很好解決該問題起惕。

策略模式的定義與特點

策略(Strategy)模式的定義:該模式定義了一系列算法涡贱,并將每個算法封裝起來,使它們可以相互替換惹想,且算法的變化不會影響使用算法的客戶问词。策略模式屬于對象行為模式,它通過對算法進行封裝嘀粱,把使用算法的責(zé)任和算法的實現(xiàn)分割開來激挪,并委派給不同的對象對這些算法進行管理。

策略模式的主要優(yōu)點如下锋叨。

  1. 多重條件語句不易維護垄分,而使用策略模式可以避免使用多重條件語句,如 if...else 語句娃磺、switch...case 語句锋喜。
  2. 策略模式提供了一系列的可供重用的算法族,恰當(dāng)使用繼承可以把算法族的公共代碼轉(zhuǎn)移到父類里面,從而避免重復(fù)的代碼嘿般。
  3. 策略模式可以提供相同行為的不同實現(xiàn)段标,客戶可以根據(jù)不同時間或空間要求選擇不同的。
  4. 策略模式提供了對開閉原則的完美支持炉奴,可以在不修改原代碼的情況下逼庞,靈活增加新算法。
  5. 策略模式把算法的使用放到環(huán)境類中瞻赶,而算法的實現(xiàn)移到具體策略類中赛糟,實現(xiàn)了二者的分離。

其主要缺點如下砸逊。

  1. 客戶端必須理解所有策略算法的區(qū)別璧南,以便適時選擇恰當(dāng)?shù)乃惴悺?/li>
  2. 策略模式造成很多的策略類,增加維護難度师逸。

策略模式的結(jié)構(gòu)與實現(xiàn)

策略模式是準(zhǔn)備一組算法司倚,并將這組算法封裝到一系列的策略類里面,作為一個抽象策略類的子類篓像。策略模式的重心不是如何實現(xiàn)算法动知,而是如何組織這些算法,從而讓程序結(jié)構(gòu)更加靈活员辩,具有更好的維護性和擴展性盒粮,現(xiàn)在我們來分析其基本結(jié)構(gòu)和實現(xiàn)方法。

1. 模式的結(jié)構(gòu)

策略模式的主要角色如下奠滑。

  1. 抽象策略(Strategy)類:定義了一個公共接口丹皱,各種不同的算法以不同的方式實現(xiàn)這個接口,環(huán)境角色使用這個接口調(diào)用不同的算法宋税,一般使用接口或抽象類實現(xiàn)摊崭。
  2. 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口,提供具體的算法實現(xiàn)弃甥。
  3. 環(huán)境(Context)類:持有一個策略類的引用爽室,最終給客戶端調(diào)用汁讼。

其結(jié)構(gòu)圖如圖 1 所示淆攻。

3-1Q116103K1205.gif

圖1 策略模式的結(jié)構(gòu)圖

2. 模式的實現(xiàn)

策略模式的實現(xiàn)代碼如下:

public class StrategyPattern {
    public static void main(String[] args) {
        Context c = new Context();
        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();
        System.out.println("-----------------");
        s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}
//抽象策略類
interface Strategy {
    public void strategyMethod();    //策略方法
}
//具體策略類A
class ConcreteStrategyA implements Strategy {
    public void strategyMethod() {
        System.out.println("具體策略A的策略方法被訪問!");
    }
}
//具體策略類B
class ConcreteStrategyB implements Strategy {
    public void strategyMethod() {
        System.out.println("具體策略B的策略方法被訪問嘿架!");
    }
}
//環(huán)境類
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

程序運行結(jié)果如下:

具體策略A的策略方法被訪問瓶珊!
-----------------
具體策略B的策略方法被訪問!

策略模式的應(yīng)用實例

【例1】策略模式在“大閘蟹”做菜中的應(yīng)用耸彪。

分析:關(guān)于大閘蟹的做法有很多種伞芹,我們以清蒸大閘蟹和紅燒大閘蟹兩種方法為例,介紹策略模式的應(yīng)用。

首先唱较,定義一個大閘蟹加工的抽象策略類(CrabCooking)扎唾,里面包含了一個做菜的抽象方法 CookingMethod();然后南缓,定義清蒸大閘蟹(SteamedCrabs)和紅燒大閘蟹(BraisedCrabs)的具體策略類胸遇,它們實現(xiàn)了抽象策略類中的抽象方法;由于本程序要顯示做好的結(jié)果圖汉形,所以將具體策略類定義成 JLabel 的子類纸镊;最后,定義一個廚房(Kitchen)環(huán)境類概疆,它具有設(shè)置和選擇做菜策略的方法逗威;客戶類通過廚房類獲取做菜策略,并把做菜結(jié)果圖在窗體中顯示出來岔冀,圖 2 所示是其結(jié)構(gòu)圖凯旭。

3-1Q116103R5638.gif

圖2 大閘蟹做菜策略的結(jié)構(gòu)圖

程序代碼如下:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CrabCookingStrategy implements ItemListener {
    private JFrame f;
    private JRadioButton qz, hs;
    private JPanel CenterJP, SouthJP;
    private Kitchen cf;    //廚房
    private CrabCooking qzx, hsx;    //大閘蟹加工者  
    CrabCookingStrategy() {
        f = new JFrame("策略模式在大閘蟹做菜中的應(yīng)用");
        f.setBounds(100, 100, 500, 400);
        f.setVisible(true);
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        SouthJP = new JPanel();
        CenterJP = new JPanel();
        f.add("South", SouthJP);
        f.add("Center", CenterJP);
        qz = new JRadioButton("清蒸大閘蟹");
        hs = new JRadioButton("紅燒大閘蟹");
        qz.addItemListener(this);
        hs.addItemListener(this);
        ButtonGroup group = new ButtonGroup();
        group.add(qz);
        group.add(hs);
        SouthJP.add(qz);
        SouthJP.add(hs);
        //---------------------------------
        cf = new Kitchen();    //廚房
        qzx = new SteamedCrabs();    //清蒸大閘蟹類
        hsx = new BraisedCrabs();    //紅燒大閘蟹類
    }
    public void itemStateChanged(ItemEvent e) {
        JRadioButton jc = (JRadioButton) e.getSource();
        if (jc == qz) {
            cf.setStrategy(qzx);
            cf.CookingMethod(); //清蒸
        } else if (jc == hs) {
            cf.setStrategy(hsx);
            cf.CookingMethod(); //紅燒
        }
        CenterJP.removeAll();
        CenterJP.repaint();
        CenterJP.add((Component) cf.getStrategy());
        f.setVisible(true);
    }
    public static void main(String[] args) {
        new CrabCookingStrategy();
    }
}
//抽象策略類:大閘蟹加工類
interface CrabCooking {
    public void CookingMethod();    //做菜方法
}
//具體策略類:清蒸大閘蟹
class SteamedCrabs extends JLabel implements CrabCooking {
    private static final long serialVersionUID = 1L;
    public void CookingMethod() {
        this.setIcon(new ImageIcon("src/strategy/SteamedCrabs.jpg"));
        this.setHorizontalAlignment(CENTER);
    }
}
//具體策略類:紅燒大閘蟹
class BraisedCrabs extends JLabel implements CrabCooking {
    private static final long serialVersionUID = 1L;
    public void CookingMethod() {
        this.setIcon(new ImageIcon("src/strategy/BraisedCrabs.jpg"));
        this.setHorizontalAlignment(CENTER);
    }
}
//環(huán)境類:廚房
class Kitchen {
    private CrabCooking strategy;    //抽象策略
    public void setStrategy(CrabCooking strategy) {
        this.strategy = strategy;
    }
    public CrabCooking getStrategy() {
        return strategy;
    }
    public void CookingMethod() {
        strategy.CookingMethod();    //做菜  
    }
}

程序運行結(jié)果如圖 3 所示。

3-1Q116103Z1315.jpg

圖3 大閘蟹做菜結(jié)果

【例2】用策略模式實現(xiàn)從韶關(guān)去婺源旅游的出行方式楣颠。

分析:從韶關(guān)去婺源旅游有以下幾種出行方式:坐火車尽纽、坐汽車和自駕車,所以該實例用策略模式比較適合童漩,圖 4 所示是其結(jié)構(gòu)圖弄贿。

3-1Q11610393E92.gif

圖4 婺源旅游結(jié)構(gòu)圖

策略模式的應(yīng)用場景

策略模式在很多地方用到,如 Java SE 中的容器布局管理就是一個典型的實例矫膨,Java SE 中的每個容器都存在多種布局供用戶選擇差凹。在程序設(shè)計中,通常在以下幾種情況中使用策略模式較多侧馅。

  1. 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種時危尿,可將每個算法封裝到策略類中。
  2. 一個類定義了多種行為馁痴,并且這些行為在這個類的操作中以多個條件語句的形式出現(xiàn)谊娇,可將每個條件分支移入它們各自的策略類中以代替這些條件語句。
  3. 系統(tǒng)中各算法彼此完全獨立罗晕,且要求對客戶隱藏具體算法的實現(xiàn)細節(jié)時济欢。
  4. 系統(tǒng)要求使用算法的客戶不應(yīng)該知道其操作的數(shù)據(jù)時,可使用策略模式來隱藏與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu)小渊。
  5. 多個類只區(qū)別在表現(xiàn)行為不同法褥,可以使用策略模式,在運行時動態(tài)選擇具體要執(zhí)行的行為酬屉。

策略模式的擴展

在一個使用策略模式的系統(tǒng)中半等,當(dāng)存在的策略很多時揍愁,客戶端管理所有策略算法將變得很復(fù)雜,如果在環(huán)境類中使用策略工廠模式來管理這些策略類將大大減少客戶端的工作復(fù)雜度杀饵,其結(jié)構(gòu)圖如圖 5 所示莽囤。

3-1Q116104010550.gif

圖5 策略工廠模式的結(jié)構(gòu)圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市切距,隨后出現(xiàn)的幾起案子烁登,更是在濱河造成了極大的恐慌,老刑警劉巖蔚舀,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饵沧,死亡現(xiàn)場離奇詭異,居然都是意外死亡赌躺,警方通過查閱死者的電腦和手機狼牺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來礼患,“玉大人是钥,你說我怎么就攤上這事∶宓” “怎么了悄泥?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長肤粱。 經(jīng)常有香客問我弹囚,道長,這世上最難降的妖魔是什么领曼? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任鸥鹉,我火速辦了婚禮,結(jié)果婚禮上庶骄,老公的妹妹穿的比我還像新娘毁渗。我一直安慰自己,他們只是感情好单刁,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布灸异。 她就那樣靜靜地躺著,像睡著了一般羔飞。 火紅的嫁衣襯著肌膚如雪肺樟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天褥傍,我揣著相機與錄音儡嘶,去河邊找鬼喇聊。 笑死恍风,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朋贬,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凯楔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锦募?” 一聲冷哼從身側(cè)響起摆屯,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糠亩,沒想到半個月后虐骑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡赎线,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年廷没,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垂寥。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡颠黎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滞项,到底是詐尸還是另有隱情狭归,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布文判,位于F島的核電站过椎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏戏仓。R本人自食惡果不足惜潭流,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柜去。 院中可真熱鬧灰嫉,春花似錦、人聲如沸嗓奢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽股耽。三九已至根盒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間物蝙,已是汗流浹背炎滞。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诬乞,地道東北人册赛。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓钠导,卻偏偏與公主長得像,于是被迫代替她去往敵國和親森瘪。 傳聞我的和親對象是個殘疾皇子牡属,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

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

  • 在現(xiàn)實生活中常常遇到實現(xiàn)某種目標(biāo)存在多種策略可供選擇的情況,例如扼睬,出行旅游可以乘坐飛機逮栅、乘坐火車、騎自行車或自己開...
    Zal哥哥閱讀 912評論 0 0
  • 在現(xiàn)實生活中常常遇到實現(xiàn)某種目標(biāo)存在多種策略可供選擇的情況窗宇,例如措伐,出行旅游可以乘坐飛機、乘坐火車军俊、騎自行車或自己開...
    迷心迷閱讀 111評論 0 0
  • 1废士、設(shè)計模式分類 參考 創(chuàng)建型(5種):將對象的創(chuàng)建與使用分離,使用者不需要關(guān)注對象的創(chuàng)建細節(jié) 單例(Singl...
    與搬磚有關(guān)的日子閱讀 2,542評論 2 69
  • 從本篇文章開始將開始一些列對Java設(shè)計模式的學(xué)習(xí)蝇完。Java設(shè)計模式是前任總結(jié)的優(yōu)秀的經(jīng)驗官硝,是編寫高質(zhì)量代碼的學(xué)習(xí)...
    Ruheng閱讀 1,966評論 2 9
  • 概述 我們知道,OOP三個基本特征是:封裝短蜕、繼承氢架、多態(tài)。通過繼承朋魔,我們可以基于差異編程岖研,也就是說,對于一個滿足我們...
    陳HHH閱讀 597評論 0 1