策略模式(詳解)

策略模式(來自HeadFirst設(shè)計(jì)模式)

今天看了 Head First 設(shè)計(jì)模式的第一個(gè)模式买喧,居然是策略模式,感覺這種模式很實(shí)用男旗,而且書上寫了20多頁趁啸,我這里做一下精簡强缘,給大家分享一下,在最后也有一些自己的思考不傅。既然是精簡旅掂,所以一定會有一些省略,如果要看詳細(xì)的访娶,看看原書吧商虐,寫得很不錯(cuò)!

需求1崖疤,父類實(shí)現(xiàn)

問題由來

現(xiàn)在要設(shè)計(jì)一只鴨子類秘车,所有的鴨子都可以游泳,不同的鴨子樣外貌可能不一樣劫哼。

剛拿到這個(gè)需求叮趴,很簡答的想法設(shè)計(jì)一個(gè)Duck類,擁有共有的方法swim权烧,并且包含一個(gè)抽象方法display展示形態(tài)眯亦。

Duck.java

abstract class Duck{
    
    public void swim(){
        System.out.println("I am swimming");
    }
    
    public abstract void display();
}

GreenDuck.java

public class GreenDuck extends Duck{

    @Override
    public void display() {
        System.out.println("my color is green");
    }

}

RedDuck.java

public class RedDuck extends Duck{

    @Override
    public void display() {
        System.out.println("my color is red");
    }

}

為什么swim()方法是普通方法,display() 方法是抽象方法呢般码?
這里是為了體現(xiàn)妻率,所有的都會游泳,并且都一樣板祝。所有鴨子都有外貌宫静,但是是由各自決定,所以必須子類來重寫扔字。

需求2囊嘉,接口實(shí)現(xiàn)

這時(shí)候需求變更,有些鴨子可以飛革为。

注意,這里的關(guān)鍵詞是可以舵鳞。如果這個(gè)時(shí)候我們把 fly() 方法寫在子類 Duck 中震檩,那就表示所有的鴨子都會飛。所以這個(gè)時(shí)候我們想到了接口,定義 Flyable 接口抛虏,讓會飛的鴨子實(shí)現(xiàn)接口博其,并重寫方法。

Flyable.java

public interface Flyable {
    
    void fly();

}

GreenDuck.java

public class GreenDuck extends Duck implements Flyable{

    public void fly() {
        System.out.println("I am flying with wing");
    }

    @Override
    public void display() {
        System.out.println("my color is green");
    }
}

假設(shè)GreenDuck會飛迂猴,那么它需要實(shí)現(xiàn)flyable接口慕淡,然后寫自己會飛。RedDuck不會飛沸毁,所以不實(shí)現(xiàn)flyable接口峰髓。

看似問題解決了,但是如果這個(gè)時(shí)候我們系統(tǒng)中有幾十種鴨子息尺,而且會飛只分用翅膀飛携兵,不能飛,用噴氣式火箭飛3種搂誉,如果采用接口來實(shí)現(xiàn)徐紧,幾十種鴨子都需要自己去實(shí)現(xiàn)方法,無法代碼重用炭懊,這種設(shè)計(jì)是不是很不好呢并级,代碼重用率太低。

而且侮腹,如果后面我們還要添加其他的屬性死遭,比如說話Speakable,那我們需要添加一個(gè)新接口凯旋,并且以前的鴨子都要從新實(shí)現(xiàn)一遍呀潭,完全違反了開閉原則。

需求3至非,策略模式實(shí)現(xiàn)

關(guān)子賣完钠署,我們來看看策略模式怎么實(shí)現(xiàn):
策略模式說,我們需要把最公有的相同的方法放在父類中荒椭,將可以變化的方法抽取成接口谐鼎,并通過組合的方式放到父類中,子類通過插入不同的接口實(shí)現(xiàn)趣惠,完成類的配置狸棍。(這個(gè)不是原話,但是我覺得更好理解)

是什么意思呢味悄?

Duck類中草戈,swim() 方法屬于共有并且都相同的方法,display() 屬于都有侍瑟,但是需要自己去實(shí)現(xiàn)的方法唐片。fly和speak是有些子類有的方法丙猬,并且實(shí)現(xiàn)有相同有不同,我們應(yīng)該抽取成接口放在父類中费韭。來看看代碼:

修改Duck類:

abstract class Duck{
    
    protected Flyable flyable;
    
    public void swim(){
        System.out.println("I am swimming");
    }
    
    public abstract void display();
    
    public void performFly(){
        // 通過多態(tài)機(jī)制茧球,動態(tài)決定到底怎么飛
        flyable.fly();
    }
    
    // 設(shè)置飛的具體實(shí)現(xiàn),并隨時(shí)可以改變
    public void setFlayable(Flyable flyable){
        //TODO:添加非空判斷
        this.flyable = flyable;
    }
}

實(shí)現(xiàn)3中飛的行為:

Flyable.java

public interface Flyable {
    void fly();
}

FlyNoWay.java 不能飛

public class FlyNoWay implements Flyable{

    public void fly() {
        System.out.println("can not fly");
    }
}

FlyWithWing.java 用翅膀飛

public class FlyWithWing implements Flyable{

    public void fly() {
        System.out.println("fly with wing");
    }
}

FlyWithRocket 用火箭飛(真是掉渣天星持,居然用火箭飛 -.-)

public class FlyWithRocket implements Flyable{

    public void fly() {
        System.out.println("fly with rocket");
    }
}

如果現(xiàn)在要讓 GreenDuck 有飛的屬性抢埋,應(yīng)該這樣做:
GreenDuck.java

public class GreenDuck extends Duck{
    
    public GreenDuck(){
        this.flyable = new FlyNoWay();
    }

    @Override
    public void display() {
        System.out.println("my color is green");
    }

}

Main.java

public class Main {

    public static void main(String[] args) {

        GreenDuck gDuck = new GreenDuck();
        gDuck.performFly();
        gDuck.setFlayable(new FlyWithWing());
        gDuck.performFly();
    }
}

默認(rèn)GreenDuck是不會飛的,然后可以通過動態(tài)的設(shè)置飛屬性給鴨子督暂,讓他具有各種飛的屬性揪垄。將飛與類解耦,并且也達(dá)到了飛實(shí)現(xiàn)重用的目的损痰。

策略模式大概就是這樣福侈,如果這個(gè)時(shí)候我們要添加 Speak 屬性,怎么做么卢未?

添加新功能

step1: 添加 Speakable接口肪凛,和兩種實(shí)現(xiàn)

Speakable.java

public interface Speakable {
    void speak();
}

SpeakDuckLaguage .java

public class SpeakDuckLaguage implements Speakable {

    public void speak() {
        System.out.println("I can speak duck language");
    }

}

SpeakHumanLaguage.java

public class SpeakHumanLaguage implements Speakable {
    public void speak() {
        System.out.println("I can speak human language");
    }
}

step2: Duck類中添加Speakable屬性,并且提供set方法辽社,然后提供一個(gè)執(zhí)行speak的方法

abstract class Duck{
    
    protected Flyable flyable;
    protected Speakable speakable;
    
    //...
    
    public void performSpeak(){
        speakable.speak();
    }
    
    public void setSpeakable(Speakable speakable){
        //TODO: 非空判斷
        this.speakable = speakable;
    }
}

step3: 給紅鴨子添加會說話屬性

RedDuck.java 默認(rèn)說鴨子語言

public class RedDuck extends Duck{
    
    // 默認(rèn)說鴨子語言
    public RedDuck(){
        this.speakable = new SpeakDuckLaguage();
    }

    @Override
    public void display() {
        System.out.println("my color is red");
    }
}

Main.java 客戶端調(diào)用

public class Main {

    public static void main(String[] args) {
        GreenDuck gDuck = new GreenDuck();
        gDuck.performFly();
        gDuck.setFlayable(new FlyWithWing());
        gDuck.performFly();
        
        RedDuck rDuck = new RedDuck();
        rDuck.performSpeak();
        rDuck.setSpeakable(new SpeakHumanLaguage());
        rDuck.performSpeak();
    }
}

輸出:

I can speak duck language
I can speak human language
can not fly
fly with wing

最后提供一個(gè)UML圖:

e8559941-b728-470b-9111-63214e6630af.png

思考

  1. 感覺用了策略模式伟墙,代碼量和類增加了不少,但是確實(shí)達(dá)到了解耦和復(fù)用的目的滴铅。
  2. 前面的設(shè)計(jì)并不完美戳葵,因?yàn)?Duck 類中的接口屬性,并沒有提供默認(rèn)值汉匙,而是在子類中去完成默認(rèn)配置拱烁。所以在使用時(shí)很可能出現(xiàn) 空指針錯(cuò)誤,更加完善的設(shè)計(jì)應(yīng)該在 performSpeak(), performFly() 中添加非空判斷噩翠∠纷裕或者將默認(rèn)值在 Duck類中設(shè)置,子類實(shí)現(xiàn)構(gòu)造方法都不用寫了伤锚,但是這樣把實(shí)現(xiàn)和 Duck 耦合在一起擅笔,也不是很好。如果有好的解決方案屯援,請留言猛们。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市狞洋,隨后出現(xiàn)的幾起案子弯淘,更是在濱河造成了極大的恐慌,老刑警劉巖徘铝,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耳胎,死亡現(xiàn)場離奇詭異惯吕,居然都是意外死亡惕它,警方通過查閱死者的電腦和手機(jī)怕午,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淹魄,“玉大人郁惜,你說我怎么就攤上這事〖孜” “怎么了兆蕉?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缤沦。 經(jīng)常有香客問我虎韵,道長,這世上最難降的妖魔是什么缸废? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任包蓝,我火速辦了婚禮,結(jié)果婚禮上企量,老公的妹妹穿的比我還像新娘测萎。我一直安慰自己,他們只是感情好届巩,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布硅瞧。 她就那樣靜靜地躺著,像睡著了一般恕汇。 火紅的嫁衣襯著肌膚如雪腕唧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天瘾英,我揣著相機(jī)與錄音枣接,去河邊找鬼。 笑死方咆,一個(gè)胖子當(dāng)著我的面吹牛月腋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓣赂,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼榆骚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了煌集?” 一聲冷哼從身側(cè)響起妓肢,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苫纤,沒想到半個(gè)月后碉钠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纲缓,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年喊废,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祝高。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡污筷,死狀恐怖工闺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瓣蛀,我是刑警寧澤陆蟆,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站惋增,受9級特大地震影響叠殷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诈皿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一林束、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纫塌,春花似錦诊县、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怎披,卻和暖如春胸嘁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凉逛。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工性宏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人状飞。 一個(gè)月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓毫胜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親诬辈。 傳聞我的和親對象是個(gè)殘疾皇子酵使,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

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

  • 模擬鴨子游戲的需求 SimUDuck游戲中會出現(xiàn)各種鴨子,一邊游泳戲水焙糟,一邊呱呱叫口渔。通過標(biāo)準(zhǔn)的OO技術(shù),設(shè)計(jì)一個(gè)超...
    一縷陽憶往昔閱讀 475評論 2 0
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法穿撮,類相關(guān)的語法缺脉,內(nèi)部類的語法痪欲,繼承相關(guān)的語法,異常的語法攻礼,線程的語...
    子非魚_t_閱讀 31,660評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理业踢,服務(wù)發(fā)現(xiàn),斷路器秘蛔,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • 通過這件事深员,我知道了一個(gè)人的心智對一個(gè)人的行為有很大的影響! 最近在看《少有人走的路》提到了幾個(gè)關(guān)于提升心智的好方...
    英倫小兔子閱讀 720評論 0 4
  • 這印度電影《地球上的星星》叠赐,很多人應(yīng)該看過,他告訴我們每個(gè)孩子都是與眾不同的屡江,父母的吼叫毆打式管教與老師的耐心有愛...
    順風(fēng)奔跑的luckydog閱讀 294評論 1 2