設(shè)計(jì)模式之策略模式(Strategy Pattern)

策略模式,是我們接觸到的第一個(gè)設(shè)計(jì)模式,也是較容易理解的一個(gè)模式栅盲。
我們可以給它下一個(gè)定義:
** 定義了算法族,分別封裝起來废恋,讓它們之間可以互相轉(zhuǎn)換谈秫,此模式讓算法的獨(dú)立于使用算法的客戶。**
維基百科上的定義是:** a software design pattern that enables an algorithm's behavior to be selected at runtime. **
維基百科上的強(qiáng)調(diào)了算法行為是在運(yùn)行時(shí)決定的鱼鼓,這正是策略模式很關(guān)鍵的一點(diǎn)拟烫。

引子

假設(shè)我們現(xiàn)在要設(shè)計(jì)一個(gè)鴨子類Duck類,然后讓不同的鴨子繼承于它迄本。我們把目光聚焦到鴨子的行為上硕淑。如果我們要給鴨子增加一個(gè)行為“fly”,第一個(gè)想法,在抽象類duck里添加一個(gè)fly方法就可置媳,其余鴨子繼承實(shí)現(xiàn)這個(gè)方法于樟。
但是這就出現(xiàn)了一個(gè)問題,并不是所有鴨子都會(huì)飛拇囊,我們反而讓一些本不具備這個(gè)fly行為的鴨子也具有該行為迂曲。那怎么辦呢?
利用繼承來提供鴨子的行為寥袭,會(huì)導(dǎo)致下面這些后果:

  • 代碼在多個(gè)子類中重復(fù)路捧,如果兩類不同鴨子需要同一種fly行為,我們就要在兩個(gè)類里分別覆蓋兩次纠永,這樣萬一維護(hù)起來是非常困難的
  • 很難知道所有鴨子的全部行為
  • 運(yùn)行時(shí)的行為不容易改變
  • 改變會(huì)一發(fā)動(dòng)全身鬓长,造成其他鴨子不想要改變

設(shè)計(jì)原則1

軟件開發(fā)中,我們常常需要遵守的設(shè)計(jì)原則是:
** 把可能需要變化的地方獨(dú)立出來尝江,不要和那些不需要變化的代碼混在一起 **
這樣代碼變化引起的不經(jīng)意后果變少涉波,系統(tǒng)變得更有彈性
實(shí)際就是盡量讓系統(tǒng)中某部分的改變不影響其他部分的變化。

提取鴨子的的行為

根據(jù)設(shè)計(jì)原則炭序,鴨子飛行的行為會(huì)發(fā)生變化啤覆,所以我們需要將fly行為單獨(dú)提取出來。同理惭聂,我們提取出兩個(gè)鴨子可能變化的行為fly和quack鴨叫窗声。用兩組類分別代表fly和quack行為。

設(shè)計(jì)原則2

那么我們?nèi)绾文莾山M鴨子行為的類呢辜纲?這里引出第二個(gè)我們提出的設(shè)計(jì)原則:
** 面對(duì)接口編程笨觅,而不是面對(duì)實(shí)現(xiàn)編程 **
這樣就可以實(shí)現(xiàn)在運(yùn)行時(shí)改變鴨子的行為。
我們不會(huì)直接指定特定的行為給鴨子耕腾。而是聲明兩個(gè)接口FlyBehavior和QuackBehavior见剩。我們制造的其他一系列的類專門來實(shí)現(xiàn)FlyBehavior和QuackBehavior,這組就成為行為類扫俺,或者算法類苍苞。
用行為類來實(shí)現(xiàn)接口而不是利用duck類來實(shí)現(xiàn)。

Paste_Image.png

實(shí)現(xiàn)鴨子的行為

根據(jù)設(shè)計(jì)原則2狼纬,可以讓飛行和鴨叫行為的動(dòng)作被其他對(duì)象復(fù)用羹呵,因?yàn)檫@些為行為已經(jīng)與鴨子類無關(guān)了。
而且當(dāng)我們新增一些行為的時(shí)候疗琉,不會(huì)影響到既有的行為類冈欢,也不會(huì)影響鴨子類。太棒了盈简!

Paste_Image.png

** 很多同學(xué)都覺得這里用類來代表行為是不是覺得很奇怪凑耻。在大家默認(rèn)里犯戏,類應(yīng)該是代表某種東西的,類應(yīng)該擁有狀態(tài)與行為拳话。這里,我們需要糾正這個(gè)觀點(diǎn)种吸,一個(gè)行為也可以具有各種屬性和函數(shù)弃衍。類不僅僅是用來代表東西事物的。 **

整合實(shí)現(xiàn)我們?cè)O(shè)計(jì)的鴨子類

首先坚俗,在duck類中加入兩個(gè)實(shí)例變量镜盯,分別聲明為兩個(gè)接口的類型,每個(gè)鴨子對(duì)象都會(huì)動(dòng)態(tài)的設(shè)置這些變量以便在運(yùn)行時(shí)引用正確的行為類型

Paste_Image.png

duck類的實(shí)現(xiàn)

package strategyPattern;

//抽象的Duck類猖败,所有鴨子都繼承
public abstract class Duck {
    
    //為行為接口類型聲明兩個(gè)引用變量速缆,所有鴨子類都繼承它們
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

    public Duck() {
        
    }
    
    public abstract void display();
    
    public void performFly() {
        flyBehavior.fly();  //委托給fly行為類實(shí)現(xiàn)
    }
    
    public void performQuack() {
        quackBehavior.quack();  //委托給quack行為類實(shí)現(xiàn)
    }
    
    //swim是所有鴨子都共同擁有的方法,所以可以直接在在duck類中實(shí)現(xiàn)
    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}

FlyBehavior接口的實(shí)現(xiàn):

package strategyPattern;

//所有飛行行為類必須實(shí)現(xiàn)的接口
public interface FlyBehavior {
    public void fly();
}

兩個(gè)fly行為實(shí)現(xiàn)類恩闻,繼承至FlyBehavior接口

package strategyPattern;

public class FlyWithWings implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("I'm flying with wings!");
    }

}

package strategyPattern;

public class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("I can't fly!");
    }

}

Quack接口及其三個(gè)行為實(shí)現(xiàn)類:

package strategyPattern;

public class Quack implements QuackBehavior {

    @Override
    public void quack() {
        // TODO Auto-generated method stub
        System.out.println("Quack");
    }

}

package strategyPattern;

public class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        // TODO Auto-generated method stub
        System.out.println("<<silence>>");
    }

}

package strategyPattern;

public class Squeak implements QuackBehavior {

    @Override
    public void quack() {
        // TODO Auto-generated method stub
        System.out.println("Squeak");
    }

}

package strategyPattern;

public interface QuackBehavior {
    public void quack();
}

一個(gè)MallardDuck類繼承至Duck類:

package strategyPattern;

public class MallardDuck extends Duck {

    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    
    @Override
    public void display() {
        // TODO Auto-generated method stub
        System.out.println("I'm a real Mallard duck艺糜!");
    }

}

測(cè)試類

package strategyPattern;

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        //這里調(diào)用mallardduck繼承來的performFly方法,進(jìn)而委托給該對(duì)象的quackBehavior對(duì)象處理
        //也就是最后是調(diào)用了繼承來的quackBehavior引用對(duì)象的quack()方法
        mallard.performFly();
        mallard.performQuack();

    }
}

運(yùn)行結(jié)果:

Paste_Image.png

在這里為了實(shí)現(xiàn)動(dòng)態(tài)的改變鴨子的行為幢尚,我們可以新建一個(gè)flyrocketPowered行為類破停,然后動(dòng)態(tài)的改變其行為:

package strategyPattern;

public class FlyRocketPowered implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("I'm flying with a rocket ");
    }

}

package strategyPattern;

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        //這里調(diào)用mallardduck繼承來的performFly方法,進(jìn)而委托給該對(duì)象的quackBehavior對(duì)象處理
        //也就是最后是調(diào)用了繼承來的quackBehavior引用對(duì)象的quack()方法
        mallard.performFly();
        mallard.performQuack();
        
        
        Duck model = new ModelDuck();
        model.performFly();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();
    }
}

運(yùn)行結(jié)果:

Paste_Image.png
Paste_Image.png

每一個(gè)鴨子都有一個(gè)FlyBehavior和一個(gè)quackBehavior尉剩,好將飛行和鴨叫委托給他們代為處理真慢。
當(dāng)你將兩個(gè)類結(jié)合起來使用時(shí),如同本例理茎,這就是組合composition黑界。這種做法和繼承不同的地方在于,鴨子的行為不是繼承來的而是和適當(dāng)行為對(duì)象那個(gè)組合來的皂林。
設(shè)計(jì)原則3:
** 多用組合 少用繼承 **

策略模式總結(jié)

三個(gè)設(shè)計(jì)原則:

  • 封裝變化朗鸠,分開變化與不變
  • 多用組合,少用繼承
  • 面向接口編程式撼,而不是面對(duì)實(shí)現(xiàn)編程

策略模式:
** 定義了算法族童社,分別封裝起來,讓它們之間可以互相轉(zhuǎn)換著隆,此模式讓算法的獨(dú)立于使用算法的客戶扰楼。**

Paste_Image.png

實(shí)現(xiàn)策略模式,我們需要對(duì)行為或算法實(shí)現(xiàn)各自的接口美浦,具體的實(shí)現(xiàn)交給繼承自這些接口的行為類弦赖,不需要在我們的主類鴨子中實(shí)現(xiàn)。主類鴨子聲明兩個(gè)接口的引用的實(shí)例變量浦辨,并設(shè)計(jì)set方法蹬竖,這樣就能在運(yùn)行時(shí)動(dòng)態(tài)的改變行為。實(shí)現(xiàn)獨(dú)立和復(fù)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末币厕,一起剝皮案震驚了整個(gè)濱河市列另,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旦装,老刑警劉巖页衙,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異阴绢,居然都是意外死亡店乐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門呻袭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眨八,“玉大人,你說我怎么就攤上這事左电×啵” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵券腔,是天一觀的道長(zhǎng)伏穆。 經(jīng)常有香客問我,道長(zhǎng)纷纫,這世上最難降的妖魔是什么枕扫? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮辱魁,結(jié)果婚禮上烟瞧,老公的妹妹穿的比我還像新娘。我一直安慰自己染簇,他們只是感情好参滴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锻弓,像睡著了一般砾赔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上青灼,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天暴心,我揣著相機(jī)與錄音,去河邊找鬼杂拨。 笑死专普,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弹沽。 我是一名探鬼主播檀夹,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼筋粗,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了炸渡?” 一聲冷哼從身側(cè)響起娜亿,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚌堵,沒想到半個(gè)月后暇唾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辰斋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瘸味。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宫仗。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旁仿,靈堂內(nèi)的尸體忽然破棺而出藕夫,到底是詐尸還是另有隱情,我是刑警寧澤枯冈,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布毅贮,位于F島的核電站,受9級(jí)特大地震影響尘奏,放射性物質(zhì)發(fā)生泄漏滩褥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一炫加、第九天 我趴在偏房一處隱蔽的房頂上張望瑰煎。 院中可真熱鬧,春花似錦俗孝、人聲如沸酒甸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽插勤。三九已至,卻和暖如春革骨,著一層夾襖步出監(jiān)牢的瞬間农尖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國打工苛蒲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卤橄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓臂外,卻偏偏與公主長(zhǎng)得像窟扑,于是被迫代替她去往敵國和親喇颁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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

  • 一直想把常見的設(shè)計(jì)模式系統(tǒng)地學(xué)習(xí)一遍嚎货,結(jié)果和大多數(shù)人一樣橘霎,過了幾天就沒能堅(jiān)持下去了。我發(fā)現(xiàn)學(xué)習(xí)這件事情急不得殖属,往往...
    Neulana閱讀 563評(píng)論 5 2
  • 設(shè)計(jì)模式 開題先說明一下姐叁,設(shè)計(jì)模式告訴我們?nèi)绾谓M織類和對(duì)象以解決某種問題。讓代碼變得更加優(yōu)雅是我們責(zé)無旁貸的任務(wù) ...
    tanghuailong閱讀 447評(píng)論 0 2
  • 客戶需求 程序設(shè)計(jì) 1洗显、直接利用繼承如何外潜? 將以上四種行為全部寫到Duck這個(gè)基類中,然后子類重寫飛和叫的行為挠唆。但...
    BlainPeng閱讀 468評(píng)論 2 7
  • 昨夜重溫金老的《天龍八部》处窥,然后,失眠了玄组。一直縈繞我心頭的不是武功蓋世氣蓋云天大義凜然的丐幫幫主喬峰滔驾,而是慕容復(fù)。...
    點(diǎn)伴閱讀 783評(píng)論 5 5
  • 這星期有件開心的事俄讹,有位可愛的人說我的背很筆直哆致!當(dāng)時(shí)真的好開心,第一次有人這么說患膛,這算是被表揚(yáng)了嗎摊阀,謝謝你 宿舍很...
    喜歡kitty的女孩閱讀 268評(píng)論 0 0