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


一直想把常見的設(shè)計(jì)模式系統(tǒng)地學(xué)習(xí)一遍仇轻,結(jié)果和大多數(shù)人一樣鸟妙,過了幾天就沒能堅(jiān)持下去了焦人。我發(fā)現(xiàn)學(xué)習(xí)這件事情急不得,往往你越急就越容易半途而廢重父。反而是沉住氣花椭,一天學(xué)習(xí)一點(diǎn),然后把它放在腦子里面房午,在碎片時(shí)間里“拿”出來捋一捋矿辽,像發(fā)酵一樣,漸漸地吸收消化為自己的東西郭厌,然后再開始學(xué)習(xí)下一個(gè)知識點(diǎn)袋倔。好了,閑話說了這么多折柠,我們開始吧宾娜!


昨天學(xué)習(xí)了設(shè)計(jì)模式中的策略模式,這個(gè)模式的思想就是:

  • 面向接口編程
  • 把應(yīng)用中可能需要變化之處獨(dú)立出來扇售,不要和那些不需要變化的代碼混在一起前塔。

我們再來看看維基百科對于c策略模式的定義:

策略模式作為一種軟件設(shè)計(jì)模式,指對象有某個(gè)行為承冰,但是在不同的場景中华弓,該行為有不同的實(shí)現(xiàn)算法偏形。比如每個(gè)人都要“交個(gè)人所得稅”爸舒,但是“在美國交個(gè)人所得稅”和“在中國交個(gè)人所得稅”就有不同的算稅方法泌豆。
策略模式:
定義了一族算法(業(yè)務(wù)規(guī)則)伤为;
封裝了每個(gè)算法解总;
這族的算法可互換代替(interchangeable)


看的似懂非懂的漠秋,我們通過一個(gè)例子來深入的揣摩這個(gè)神奇的設(shè)計(jì)模式肉盹。
情景:一個(gè)模擬游戲中需要用到很多不同類型的鴨子俺陋,請編寫代碼以生成不同的鴨子對象百宇。

你可能想到編寫一個(gè)Duck抽象超類然后讓具體的鴨子類去繼承它欧引,在子類中重寫超類中的方法,實(shí)現(xiàn)“個(gè)性化”恳谎,于是你寫的代碼可能就是下面這個(gè)樣子的:

public class Duck{
  public void quack(){
//鴨子的叫聲
}
  public void swim(){
//鴨子會游泳
}
  public display(){
//鴨子的羽毛的顏色
}

然后再定義一個(gè)子類:

public class MallardDuck extends Duck{
  public void display(){
    System.out.println("我的羽毛是綠色的");
}
}

乍一看好像感覺并沒有什么不對爸ゴ恕憋肖?確實(shí)上面這種寫法在項(xiàng)目的初期可能確實(shí)能夠達(dá)到用戶突出的需求,但是需求是可能變化的婚苹,你應(yīng)該思考的是:當(dāng)需求改變的時(shí)候你的代碼是否能夠靈活應(yīng)對岸更,這在工程上叫做低耦合。

比如膊升,現(xiàn)在需求改變了怎炊,我要有一種會飛的鴨子,怎么辦廓译?你可能會說這有什么難的评肆,在Duck類里面添加一個(gè)fly方法不就可以了嗎?但是新的問題又來了:如果你在超類設(shè)置了fly方法非区,就意味著每一個(gè)子類都會擁有fly方法瓜挽,這肯定不能忍啊,你可能又會想到補(bǔ)救措施:在那些不會飛的鴨子子類中重寫一遍fly方法征绸。這樣可以是可以久橙,如果400種鴨子有200種不會飛的鴨子子類,可能你整天都在那里做重寫工作了管怠。

那么用接口呢淆衷?讓那些會飛的鴨子實(shí)現(xiàn)接口中的fly方法?這就是典型的從一個(gè)坑跳到了另外一個(gè)坑渤弛,你現(xiàn)在就要把會飛的那200種鴨子都要實(shí)現(xiàn)一遍fly方法祝拯。


好了,上面我們把問題都梳理了一遍她肯。我們重新審視一次我們遇到的問題佳头。在例子中,總共有兩種飛行行為:會飛的和不會飛的辕宏。我們可以不把它寫進(jìn)類里面畜晰,因?yàn)槲覀円呀?jīng)看到砾莱,不同鴨子的飛行行為是不一樣的瑞筐。這就是我們上面提到的把要變化的部分提取出來。其實(shí)這是設(shè)計(jì)模式中很重要的一個(gè)設(shè)計(jì)原則:多用組合腊瑟,少用繼承聚假。我們設(shè)計(jì)一個(gè)飛行接口,讓具體的飛行行為類去實(shí)現(xiàn)它闰非,到底怎么飛由類自己實(shí)現(xiàn)膘格。而我們的Duck類將飛行接口當(dāng)成是鴨子的一個(gè)成員變量。由于Duck類知道這個(gè)飛行行為接口肯定有一個(gè)fly方法财松,所以我可以大膽的在Duck類里面調(diào)用這個(gè)變量的fly方法(這就是面向接口編程)瘪贱,而具體怎么飛要由具體的飛行行為來決定纱控,這在Java中叫做后期綁定

原理解釋清楚了菜秦,上代碼:

鴨子類:

public abstract class Duck {
    protected FlyBehavior flyBehavior;   
    protected QuackBehavior quackBehavior; 
    public abstract void display(); 
    public void performQuack(){ 
    quackBehavior.quack();  
   }    
    public void performFly(){  
      flyBehavior.fly(); 
   }    
    public void swim(){   
     System.out.println("所有的鴨子都能漂浮于水面甜害。");  
   } 
    public void setFlyBehavior(FlyBehavior flyBehavior){ 
        this.flyBehavior=flyBehavior;  //為了代碼更加靈活,這里添加設(shè)置 
 飛行行為的方法球昨,下同
   }  
    public void  setQuackBehavior(QuackBehavior quackBehavior){  
      this.quackBehavior=quackBehavior;  //修改鴨子的叫聲尔店,有的呱呱叫,有的小鴨還不會叫
  }
}

飛行行為接口:

public interface FlyBehavior {
    void fly();
}

叫聲接口:

public interface QuackBehavior { 
   void quack();
}

具體的飛行行為類one:

public class FlyWithWings implements FlyBehavior {
    public void fly(){
        System.out.println("我可以飛主慰。");
    }
}

具體的飛行行為類two:

public class FlyNoWay implements FlyBehavior {
    public void fly(){
        System.out.println("我不能飛");
    }
}

具體的叫聲類one:

public class Quack implements QuackBehavior {
    public void quack(){
        System.out.println("呱呱叫");
    }
}

具體的叫聲類two:

public class MuteQuack implements QuackBehavior {
    public void quack(){
        System.out.println("<我不能叫>");
    }
}

好了嚣州,部件代碼都寫好了,我們來講它組裝組裝:

野鴨:

public class MallardDuck extends Duck {
    public MallardDuck(){
        quackBehavior=new Quack();
        flyBehavior=new FlyWithWings();
    }
    public void display(){
        System.out.println("我是野鴨");
    }
}

模型鴨(假的鴨子):

public class ModelDuck extends Duck {
    public ModelDuck(){
        setFlyBehavior(new FlyNoWay());
        setQuackBehavior(new MuteQuack());
    }
    public void display(){
        System.out.println("我是模型鴨");
    }
}

我們可以感受到共螺,經(jīng)過改寫后的代碼靈活多了该肴,這種靈活主要是來源于可以動態(tài)的組合鴨子的行為,而非死板的繼承方式璃谨。


我們再次復(fù)習(xí)一下策略模式的幾個(gè)設(shè)計(jì)原則作為結(jié)尾吧:

  • 面向接口編程沙庐,而非面向?qū)崿F(xiàn)編程
  • 多用組合,少用繼承
  • 抽離易改變的代碼

我是在校學(xué)生佳吞,目前大三拱雏,如有寫的不對的地方,歡迎指正底扳,謝謝铸抑!另外,設(shè)計(jì)模式文章準(zhǔn)備寫成一個(gè)系列,歡迎follow( _ )

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末衷模,一起剝皮案震驚了整個(gè)濱河市鹊汛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阱冶,老刑警劉巖刁憋,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異木蹬,居然都是意外死亡至耻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進(jìn)店門镊叁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尘颓,“玉大人,你說我怎么就攤上這事晦譬“唐唬” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵敛腌,是天一觀的道長卧土。 經(jīng)常有香客問我惫皱,道長,這世上最難降的妖魔是什么尤莺? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任逸吵,我火速辦了婚禮,結(jié)果婚禮上缝裁,老公的妹妹穿的比我還像新娘扫皱。我一直安慰自己,他們只是感情好捷绑,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布韩脑。 她就那樣靜靜地躺著,像睡著了一般粹污。 火紅的嫁衣襯著肌膚如雪段多。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天壮吩,我揣著相機(jī)與錄音进苍,去河邊找鬼。 笑死鸭叙,一個(gè)胖子當(dāng)著我的面吹牛觉啊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沈贝,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼杠人,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宋下?” 一聲冷哼從身側(cè)響起嗡善,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎学歧,沒想到半個(gè)月后罩引,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枝笨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年袁铐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伺帘。...
    茶點(diǎn)故事閱讀 40,438評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昭躺,死狀恐怖忌锯,靈堂內(nèi)的尸體忽然破棺而出伪嫁,到底是詐尸還是另有隱情,我是刑警寧澤偶垮,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布张咳,位于F島的核電站帝洪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脚猾。R本人自食惡果不足惜葱峡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望龙助。 院中可真熱鬧砰奕,春花似錦、人聲如沸提鸟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽称勋。三九已至胸哥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赡鲜,已是汗流浹背空厌。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留银酬,地道東北人嘲更。 一個(gè)月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像揩瞪,于是被迫代替她去往敵國和親哮内。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評論 2 359

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

  • 策略模式壮韭,是我們接觸到的第一個(gè)設(shè)計(jì)模式北发,也是較容易理解的一個(gè)模式。我們可以給它下一個(gè)定義:** 定義了算法族喷屋,分別...
    六尺帳篷閱讀 717評論 0 8
  • 客戶需求 程序設(shè)計(jì) 1琳拨、直接利用繼承如何? 將以上四種行為全部寫到Duck這個(gè)基類中屯曹,然后子類重寫飛和叫的行為狱庇。但...
    BlainPeng閱讀 474評論 2 7
  • 思考: 假設(shè)有個(gè)需求,模擬鴨子游戲:在游戲中會出現(xiàn)各種各樣的鴨子恶耽,一邊游泳戲水密任,一邊呱呱叫。開始我們的設(shè)計(jì)吧: 這...
    MarksGui閱讀 183評論 0 0
  • 模擬鴨子 模擬一個(gè)鴨子的游戲SimUDuck偷俭,游戲中會出現(xiàn)各種鴨子浪讳,一邊游戲戲水,一邊呱呱叫∮坑現(xiàn)采用oo技術(shù)淹遵,設(shè)計(jì)...
    yaSecrets閱讀 259評論 0 0
  • 使用模式最好的方式是:“把模式裝進(jìn)腦子里口猜,然后在你的設(shè)計(jì)和已有的應(yīng)用中,尋找何處可以使用他們透揣〖醚祝”,以往是代碼復(fù)用辐真,...
    秒贊不是偶然閱讀 279評論 0 0