策略模式

需求

展示一只鴨子秃踩,鴨子會(huì)叫缎患,會(huì)游泳橄仍,還有不同的外觀巴粪。

初步設(shè)計(jì)

public abstract class Duck
{
    /**
     * 所有的鴨子都會(huì)有外觀澡为,只是每只鴨子的外觀都可能不一樣
     */
    public abstract void display();

    public void swim()
    {
        System.out.println("鴨子會(huì)游泳");
    }

    public void quack()
    {
        System.out.println("鴨子會(huì)呱呱叫");
    }
}

看起來(lái)似乎不錯(cuò)隐锭,?鴨子只要繼承自 Duck携悯,然后實(shí)現(xiàn)自己的外觀即可了获询。

一段時(shí)間后雳灾,需求改了漠酿,現(xiàn)在需要鴨子會(huì)飛,叫聲也可能有不一樣的谎亩。

于是就在父類中加了 fly 的方法

public abstract class Duck
{
    public void fly()
    {
        System.out.println("讓鴨子飛");
    }

    // 省略其他的
}

叫聲在這里不用修改炒嘲,因?yàn)樽宇愔恍枰采wquack方法即可。

然后可怕的事情發(fā)生了匈庭,現(xiàn)在發(fā)現(xiàn)夫凸,橡皮鴨子也會(huì)飛了,原本是不應(yīng)該會(huì)飛的阱持。

可見(jiàn)為了復(fù)用而使用繼承會(huì)導(dǎo)致問(wèn)題:原本某些子類不需要的能力被父類強(qiáng)行賦予了

思考以上代碼會(huì)導(dǎo)致的問(wèn)題:

  1. 代碼在多個(gè)子類中重復(fù)(不會(huì)飛的鴨子也有了飛的方法)
  2. 牽一發(fā)動(dòng)全身夭拌,后續(xù)需要加入新功能,會(huì)導(dǎo)致所有的子類都受影響
  3. 不能動(dòng)態(tài)改變鴨子的行為衷咽,比如讓他豎著飛或橫著飛鸽扁,也很難知道鴨子的全部行為

既然繼承?不好,那就使用接口怎么樣镶骗?

考慮到“飛”献烦,“叫聲”行為是?可變的,因此封裝成接口卖词,讓需要飛和叫的鴨子實(shí)現(xiàn)這些接口不就好了嗎巩那?來(lái)試試!

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

/**
 * 所有鴨叫行為必須實(shí)現(xiàn)的接口
 */
public interface QuackBehavior
{
    public void quack();
}

那么鴨子父類就變成:

public abstract class Duck
{
    /**
     * 所有的鴨子都會(huì)有外觀此蜈,只是每只鴨子的外觀都可能不一樣
     */
    public abstract void display();

    public void swim()
    {
        System.out.println("鴨子會(huì)游泳");
    }
}

而需要叫聲或者飛行行為的鴨子之類即横,就可以通過(guò)實(shí)現(xiàn)接口來(lái)完成,然后實(shí)現(xiàn)自己的飛和叫的行為裆赵,不管是橫著飛豎著飛东囚,你自己都可以決定。

但是也會(huì)導(dǎo)致以下問(wèn)題:

  • 如果需要修改行為战授,那么就要具體到每個(gè)鴨子子類的源碼页藻,一不小心可能就會(huì)出現(xiàn)問(wèn)題桨嫁。
  • 代碼無(wú)法復(fù)用,比如說(shuō)份帐,定義了 A 鴨子會(huì)?橫著飛璃吧,,B 鴨子會(huì)橫著飛废境,那么橫著飛這個(gè)行為就會(huì)在 A 和 B 中重復(fù)畜挨。

軟件開(kāi)發(fā)的真理

需求永遠(yuǎn)在變

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

找出可能變化的地方,并將可能會(huì)變化的獨(dú)立出來(lái)噩凹,不要和不變的那些?代碼混在一起巴元。
也就是說(shuō):如果有新需求?來(lái)了,就會(huì)使某一塊變化驮宴,那么就可以確定逮刨,這塊是需要被?抽出來(lái)的。確保系統(tǒng)中某一部分的改變堵泽,不會(huì)導(dǎo)致影響到其他部分

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

針對(duì)接口編程修己,而不是針對(duì)實(shí)現(xiàn)編程,是針對(duì)超類型編程落恼,這里的接口不一定是語(yǔ)法意義上的接口箩退。也就是利用多態(tài)。

OK ?佳谦,現(xiàn)在可以將飛和叫聲這兩個(gè)會(huì)變化的獨(dú)立出來(lái)了

我們可以將飛這個(gè)行為歸為一組戴涝,將叫聲這個(gè)行為歸為一組。它們將和鴨子類完全隔離钻蔑。

/**
 * 飛行行為的實(shí)現(xiàn)類啥刻,給不會(huì)飛的鴨子用
 */
public class FlyNoWay implements FlyBehavior
{
    @Override
    public void fly()
    {
        System.out.println("我不會(huì)飛");
    }
}

/**
 * 飛行實(shí)現(xiàn)類,用火箭來(lái)飛
 */
public class FlyRocketPowered implements FlyBehavior
{
    @Override
    public void fly()
    {
        System.out.println("我可以和火箭一起飛");
    }
}

/**
 * 飛行行為的實(shí)現(xiàn)咪笑,給真會(huì)飛的鴨子用
 */
public class FlyWithWings implements FlyBehavior
{

    @Override
    public void fly()
    {
        System.out.println("我會(huì)飛");
    }
}
/**
 * 聲音實(shí)現(xiàn)類可帽,給會(huì)吱吱叫的鴨子用
 */
public class Squeak implements QuackBehavior
{
    @Override
    public void quack()
    {
        System.out.println("我會(huì)吱吱叫");
    }
}

/**
 * 叫聲實(shí)現(xiàn)類,給不會(huì)叫的鴨子用
 */
public class MuteQuack implements QuackBehavior
{
    @Override
    public void quack()
    {
        System.out.println("我不會(huì)叫");
    }
}

整合鴨子的行為

?如何?讓鴨子和他們的行為發(fā)生關(guān)聯(lián)呢窗怒?我們的目的是行為可以被動(dòng)態(tài)賦予映跟,因此可以讓行為成為鴨子的一個(gè)實(shí)例變量,暴露出相應(yīng)的方法去觸發(fā)行為扬虚。新的鴨子類如下:

/**
 * 鴨子父類
 */
public abstract class Duck
{
    // 所有鴨子子類都實(shí)現(xiàn)這兩個(gè)接口類型
    protected FlyBehavior flyBehavior;
    protected QuackBehavior quackBehavior;

    /**
     * 所有的鴨子都會(huì)有外觀努隙,只是每只鴨子的外觀都可能不一樣
     */
    public abstract void display();

    // 委托給飛行行為類
    public void performFly()
    {
        flyBehavior.fly();
    }

    // 委托給鴨叫聲類
    public void perfirmQuack()
    {
        quackBehavior.quack();
    }

    /**
     * 只要是鴨子都會(huì)游泳
     */
    public void swim()
    {
        System.out.println("所有的鴨子都會(huì)游泳");
    }

    /**
     * 動(dòng)態(tài)設(shè)定鴨子的飛行方式
     */
    public void setFlyBehavior(FlyBehavior fb)
    {
        flyBehavior = fb;
    }

    /**
     * 動(dòng)態(tài)設(shè)定鴨子的叫聲
     */
    public void setQuackBehavior(QuackBehavior qb)
    {
        quackBehavior = qb;
    }
}

測(cè)試

現(xiàn)在可以創(chuàng)建你想要的鴨子了。

/**
 * 綠頭鴨類辜昵,一繼承了Duck類荸镊,從一開(kāi)始就是會(huì)游泳的,而外觀可以自己定義,飛行方式和叫聲都可以根據(jù)需要自己定義
 */
public class MallardDuck extends Duck
{
    public MallardDuck()
    {
        // 聲明這個(gè)鴨子是飛行和叫聲行為
        flyBehavior = new FlyWithWings();
        quackBehavior = new Quack();
    }

    @Override
    public void display()
    {
        System.out.println("我是一只綠頭鴨");
    }
}
/**
 * 創(chuàng)建一個(gè)模型鴨躬存,一開(kāi)始是不會(huì)飛的
 */
public class ModelDuck extends Duck
{
    public ModelDuck()
    {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Quack();
    }

    @Override
    public void display()
    {
        System.out.println("我是一只模型鴨");
    }
}
public static void main(String[] args)
{
    // 創(chuàng)建一個(gè)綠頭鴨
    Duck mallardDuck = new MallardDuck();
    // 調(diào)用繼承而來(lái)的performFly方法张惹,委托給FlyBehavior對(duì)象處理
    mallardDuck.performFly();
    // 調(diào)用繼承而來(lái)的performQuack方法,委托給QuackBehavior對(duì)象處理
    mallardDuck.perfirmQuack();

    // 創(chuàng)建一個(gè)模型鴨
    Duck modelDuck = new ModelDuck();
    modelDuck.performFly();
    modelDuck.perfirmQuack();
    // 動(dòng)態(tài)設(shè)定鴨子的飛行方式岭洲,這里讓鴨子和火箭一起飛
    modelDuck.setFlyBehavior(new FlyRocketPowered());
    // 這樣就可以動(dòng)態(tài)的設(shè)定鴨子的行為了宛逗,如果綁定在鴨子類中,就無(wú)法這樣做
    modelDuck.performFly();
}

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

多用組合钦椭,少用繼承拧额。

上面可以看到的是碑诉,鴨子的行為并不是通過(guò)繼承而來(lái)的彪腔,而是通過(guò)組合而來(lái)的。

鴨子的飛行行為可以看做是一族算法进栽,叫聲行為也可以看做是一族算法德挣,在一族算法內(nèi),族內(nèi)的行為是可以互相替換的快毛,比如一族飛行算法格嗅,橫著飛和豎著飛是可以互相替換的。

策略模式的定義:

定義了算法族唠帝,分別封裝起來(lái)屯掖,讓他們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算法的客戶襟衰。

最終代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贴铜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瀑晒,更是在濱河造成了極大的恐慌绍坝,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苔悦,死亡現(xiàn)場(chǎng)離奇詭異轩褐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)玖详,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)把介,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蟋座,你說(shuō)我怎么就攤上這事拗踢。” “怎么了蜈七?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵秒拔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)砂缩,這世上最難降的妖魔是什么作谚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮庵芭,結(jié)果婚禮上妹懒,老公的妹妹穿的比我還像新娘。我一直安慰自己双吆,他們只是感情好眨唬,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著好乐,像睡著了一般匾竿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蔚万,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天岭妖,我揣著相機(jī)與錄音,去河邊找鬼反璃。 笑死昵慌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的淮蜈。 我是一名探鬼主播斋攀,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼梧田!你這毒婦竟也來(lái)了淳蔼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柿扣,失蹤者是張志新(化名)和其女友劉穎肖方,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體未状,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俯画,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了司草。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艰垂。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖埋虹,靈堂內(nèi)的尸體忽然破棺而出猜憎,到底是詐尸還是另有隱情,我是刑警寧澤搔课,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布胰柑,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏柬讨。R本人自食惡果不足惜崩瓤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望踩官。 院中可真熱鬧却桶,春花似錦、人聲如沸蔗牡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辩越。三九已至嘁扼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間区匣,已是汗流浹背偷拔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工蒋院, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亏钩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓欺旧,卻偏偏與公主長(zhǎng)得像姑丑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辞友,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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

  • 模擬鴨子游戲的需求 SimUDuck游戲中會(huì)出現(xiàn)各種鴨子栅哀,一邊游泳戲水,一邊呱呱叫称龙。通過(guò)標(biāo)準(zhǔn)的OO技術(shù)留拾,設(shè)計(jì)一個(gè)超...
    一縷陽(yáng)憶往昔閱讀 461評(píng)論 2 0
  • 設(shè)計(jì)模式 開(kāi)題先說(shuō)明一下,設(shè)計(jì)模式告訴我們?nèi)绾谓M織類和對(duì)象以解決某種問(wèn)題鲫尊。讓代碼變得更加優(yōu)雅是我們責(zé)無(wú)旁貸的任務(wù) ...
    tanghuailong閱讀 447評(píng)論 0 2
  • 定義 定義了算法族痴柔,分別封裝起來(lái),讓它們之間可以互相替換疫向,此模式讓算法的變化獨(dú)立于使用算法的客戶咳蔚。 Define ...
    狐尼克朱迪閱讀 257評(píng)論 0 0
  • 設(shè)計(jì)模式入門(mén) 設(shè)計(jì)模式是人們?cè)诿鎸?duì)同類型軟件工程設(shè)計(jì)問(wèn)題所總結(jié)出來(lái)的一些有用的經(jīng)驗(yàn)。模式不是代碼搔驼,而是某類問(wèn)題的通...
    在南方的北方人_Elijah閱讀 346評(píng)論 0 3
  • 本文解決問(wèn)題 什么是策略模式谈火? 策略模式的優(yōu)缺點(diǎn)以及策略模式解決了什么痛點(diǎn) 策略模式的適用環(huán)境 什么是策略模式? ...
    慕久久閱讀 620評(píng)論 0 1