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


  • 設(shè)計(jì)模式概述
    • 定義
      設(shè)計(jì)模式是一套被反復(fù)使用,多數(shù)人知曉的,經(jīng)過(guò)分類的,代碼設(shè)計(jì)經(jīng)驗(yàn)總結(jié)

      簡(jiǎn)單來(lái)說(shuō):經(jīng)驗(yàn)復(fù)用!

    • 使用語(yǔ)言:

      并不要求一定用某種語(yǔ)言,理論上適合于任何oo語(yǔ)言(面向?qū)ο缶幊陶Z(yǔ)言)(Java,C#等)

    • 意義&目的:

      代碼復(fù)用,增加可維護(hù)性辆沦。每種模式在現(xiàn)時(shí)中都有相應(yīng)的原理來(lái)與之對(duì)應(yīng)昼捍,每一個(gè)模式描述了一個(gè)在我們周?chē)粩嘀貜?fù)發(fā)生的問(wèn)題,以及該問(wèn)題的核心解決方案肢扯,這也是它能被廣泛應(yīng)用的原因妒茬。


  • 策略模式概述
    • 預(yù)備知識(shí):本文以java舉例,請(qǐng)先了解 類蔚晨、復(fù)用乍钻、繼承、多態(tài)铭腕、接口银择,抽象類等
    • 策略模式( Strategy Pattern):定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。本模式使得算法的變化可獨(dú)立于使用它的客戶累舷。
    • (劃重點(diǎn):瓶肌!1挥)最主要用法:許多相關(guān)的類僅僅是行為有異怀挠∥龊“策略”提供了一種用多個(gè)行為中的一個(gè)行為來(lái)配置一個(gè)類的方法

正文!

用例子理解:

有一個(gè)Duck()類

鴨子會(huì)叫

所以有一個(gè)Quack()方法

因?yàn)榧t頭鴨和綠頭鴨會(huì)飛绿淋,現(xiàn)在闷畸,要新加入一種 fly()方法。

方法一:繼承

繼承是指一個(gè)對(duì)象直接使用另一對(duì)象的屬性和方法吞滞。

即直接在Duck類里面加入fly()方法佑菩,再由子類繼承。

但是2迷5钅!

不是所有的鴨子都會(huì)飛

(橡皮鴨問(wèn)號(hào)佩捞?绞幌??)

那么一忱,對(duì)于每一個(gè)Duck子類的實(shí)例對(duì)象莲蜘,都必須覆蓋fly()方法(會(huì)飛的不會(huì)飛的,乃至用不同姿態(tài)飛的鴨子)

然而帘营,如果還有一個(gè)誘餌鴨:

不會(huì)飛也不會(huì)叫票渠。。芬迄。问顷。。禀梳。

那么杜窄,又得覆蓋Quack()方法。

代碼:

public class StrategyModel1 {
    public static void main(String[] args) {
        Duck1 duck=new Duck1();
        System.out.println("Duck1:");
        duck.fly1();
        duck.Quack1();
        duck.Swim1();
        Duck1 rubberduck=new rubberDuck1();
        System.out.println("RubberDuck1:");
        rubberduck.fly1();
        rubberduck.Quack1();
        rubberduck.Swim1();
        Duck1 Decoyduck=new DecoyDuck1();
        System.out.println("DecoyDuck1:");
        Decoyduck.fly1();
        Decoyduck.Quack1();
        Decoyduck.Swim1();
    }
}
class Duck1{
     public void fly1() {
         System.out.println("I'm flying!");
    }
     public void Quack1() {
         System.out.println("呱呱呱");
     }
     public void Swim1() {
         System.out.println("All the Ducks are swimming!");
     }
}
class rubberDuck1 extends Duck1{
    public void fly1() {
        System.out.println("I can't fly!");//point1
    }
    public void Quack1() {
        System.out.println("吱吱吱");
    }
}
/*看似可行算途,但是可能不同的類有相同的方法塞耕,直接繼承會(huì)繼承不相同不想要的方法。

比如DecoyDuck1想要用RubberDuck1的fly1方法郊艘,要么繼承Duck1荷科,再重寫(xiě)fly1方法唯咬;

要么不繼承Duck1而繼承RubberDuck1纱注,再重寫(xiě)Quack1方法。

無(wú)論哪種繼承胆胰,都會(huì)導(dǎo)致代碼在多個(gè)子類中重復(fù)狞贱。*/
class DecoyDuck1 extends Duck1{
    public void fly1() {
        System.out.println("I can't fly!");//point2
    }
    public void Quack1() {
        System.out.println("呱呱呱");
    }
}

結(jié)論:

1.利用繼承的方法,改動(dòng)會(huì)牽一發(fā)而動(dòng)全身蜀涨,造成其他不想要的改變

2.代碼在多個(gè)子類中重復(fù)瞎嬉。

簡(jiǎn)單來(lái)說(shuō):有新方法子類就要覆蓋父類蝎毡。覆蓋其實(shí)沒(méi)有邏輯問(wèn)題,問(wèn)題是同樣的方法在不同類里面要重寫(xiě)多次氧枣,代碼繁瑣沐兵。

方法二:接口

繼承不行。因?yàn)轼喿訒?huì)不斷更新便监,這樣每當(dāng)有新的鴨子類出現(xiàn)扎谎,就得檢查甚至覆蓋fly()和Quack()方法。

那么
我們可以把fly()從父類中取出來(lái)烧董,放到Flyalbe接口中毁靶,這樣,只有會(huì)飛的鴨子才能實(shí)現(xiàn)此接口逊移。Quackable接口同理预吆。

接口:(interface),是一系列方法的聲明胳泉,是一些方法特征的集合,一個(gè)接口只有方法的特征沒(méi)有方法的實(shí)現(xiàn)拐叉,因此這些方法可以在不同的地方被不同的類實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)胶背。

【注意與抽象類區(qū)分巷嚣!】(抽象類是對(duì)一種事物的抽象,即對(duì)類抽象{is-a}(全部方法和屬性繼承)钳吟。而接口是對(duì)行為的抽象{has-a}廷粒。(指定方法繼承))

代碼:

public class StrategyModel2 {
    public static void main(String[] args) {
        Duck2 duck=new Duck2();
        duck.fly2();
        Duck2 rubberduck=new rubberDuck2();
        rubberduck.fly2();
    }
}
class rubberDuck2 extends Duck2 implements Flyable2 {
    public void fly2() {
        System.out.println("I don't wanna to fly!");
    }
}
class Duck2 implements Flyable2{
    public void fly2() {
        System.out.println("I'm flying!");
    }
}
interface Flyable2{
    public abstract void fly2();
}
//Flyalbe2導(dǎo)致fly2完全不能復(fù)用

現(xiàn)在雖然Flyable接口可以解決會(huì)飛的橡皮鴨問(wèn)題,

但是:烨摇0泳ァ!

這是一種更蠢的方法暇番,只有方法聲明嗤放,沒(méi)有方法實(shí)現(xiàn)!

代碼將完全無(wú)法復(fù)用壁酬,無(wú)論方法是否一樣次酌,每次必須重寫(xiě)。

方法三:策略模式舆乔!

繼承不能解決問(wèn)題岳服,因?yàn)轼喿拥男袨樵谧宇惱锊粩喔淖儯易屗凶宇惗加羞@些行為是不恰當(dāng)?shù)摹?/p>

接口似乎可行希俩,只有會(huì)飛的鴨子繼承Flyable接口吊宋,但是Java接口不具有實(shí)現(xiàn)代碼,繼承接口無(wú)法使代碼復(fù)用颜武。

有一個(gè)設(shè)計(jì)原則璃搜,恰好適用于此:(第一個(gè)設(shè)計(jì)原則拖吼!)

找出變化之處,把它獨(dú)立出來(lái)这吻,與無(wú)需變化的代碼隔離

這個(gè)概念很簡(jiǎn)單吊档,

幾乎是每個(gè)設(shè)計(jì)模式背后的精神所在!

哪些可變哪些不可變唾糯?

每次新的需求一來(lái)籍铁,都會(huì)使某方面的代碼發(fā)生變化,那么這部分代碼就要被抽出來(lái)趾断,區(qū)分與其他穩(wěn)定代碼拒名。

Fly()和Quack()會(huì)隨著鴨子的類不同而改變。
我們把它們從Duck類中取出芋酌,新建一組類來(lái)代表每個(gè)行為

第二個(gè)設(shè)計(jì)原則:針對(duì)接口編程增显!而不是針對(duì)實(shí)現(xiàn)編程

我們利用接口代表每個(gè)行為,比方說(shuō)脐帝,F(xiàn)lyBehavior同云,而行為的每個(gè)實(shí)現(xiàn)都將實(shí)現(xiàn)其中的一個(gè)接口。

這樣的做法迥異于以往堵腹,以前的做法是:行為來(lái)自Duck父類的具體實(shí)現(xiàn)炸站,或是繼承某個(gè)接口并由子類自行實(shí)現(xiàn)而來(lái)。這兩種做法都是依賴于“實(shí)現(xiàn)”疚顷,我們被實(shí)現(xiàn)綁得死死的旱易,沒(méi)辦法更改行為(除非寫(xiě)更多代碼)。

在我們的新設(shè)計(jì)中腿堤,鴨子的子類將使用接口FlyBehavior所表示的行為阀坏,所以實(shí)際的“實(shí)現(xiàn)”不會(huì)被綁死在鴨子的子類中。

換句話說(shuō)笆檀,特定的具體行為編寫(xiě)在實(shí)現(xiàn)了FlyBehavior的類中忌堂。

這樣設(shè)計(jì),甚至可以讓fly被其他對(duì)象復(fù)用酗洒,因?yàn)檫@個(gè)fly動(dòng)作已經(jīng)與鴨子類無(wú)關(guān)了士修。

代碼:

public class StrategyModel3 {
    public static void main(String[] args) {
        Duck3 duck=new Duck3();
        duck.Swim3();
        Duck3 rubberduck=new RubberDuck3();
        rubberduck.Swim3();
        rubberduck.PerformFly();
        Duck3 decoyduck=new DecoyDuck3();
        decoyduck.PerformFly();
    }
}
class Duck3{
    public FlyBehavior flybehavior;
    public void Swim3() {
        System.out.println("All the ducks are swimming!");
    }
    public void PerformFly() {
        flybehavior.fly3();
    }
}
class RubberDuck3 extends Duck3{
    public RubberDuck3() {
    flybehavior=new FlyWithWings();
    }
}
class DecoyDuck3 extends Duck3{
    public DecoyDuck3() {
        flybehavior=new FlyNoWay();
    }
}
interface FlyBehavior{
    public void fly3();
}
class FlyWithWings implements FlyBehavior{
    public void fly3() {
        System.out.println("I'm flying!");
    }
}
class FlyNoWay implements FlyBehavior{
    public void fly3() {
        System.out.println("I can't fly!");
    }
}

(Quack同理)

將兩個(gè)類結(jié)合起來(lái)使用,如同本例樱衷,就是組合棋嘲。這種做法和“繼承”不同的地方在于,鴨子的行為不是繼承來(lái)的箫老,而是和適當(dāng)?shù)男袨閷?duì)象組合來(lái)的封字。

第三個(gè)設(shè)計(jì)原則:多用組合黔州,少用繼承耍鬓!


復(fù)習(xí):

策略模式:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換阔籽。本模式使得算法的變化可獨(dú)立于使用它的客戶。

概念圖示:


結(jié)束
------By@YYSir

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牲蜀,一起剝皮案震驚了整個(gè)濱河市笆制,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涣达,老刑警劉巖在辆,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異度苔,居然都是意外死亡匆篓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)寇窑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸦概,“玉大人,你說(shuō)我怎么就攤上這事甩骏〈笆校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵饮笛,是天一觀的道長(zhǎng)咨察。 經(jīng)常有香客問(wèn)我,道長(zhǎng)福青,這世上最難降的妖魔是什么摄狱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮无午,結(jié)果婚禮上二蓝,老公的妹妹穿的比我還像新娘。我一直安慰自己指厌,他們只是感情好刊愚,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著踩验,像睡著了一般鸥诽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箕憾,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天牡借,我揣著相機(jī)與錄音,去河邊找鬼袭异。 笑死钠龙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碴里,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沈矿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了咬腋?” 一聲冷哼從身側(cè)響起羹膳,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎根竿,沒(méi)想到半個(gè)月后陵像,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寇壳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年醒颖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壳炎。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡图贸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冕广,到底是詐尸還是另有隱情疏日,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布撒汉,位于F島的核電站沟优,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏睬辐。R本人自食惡果不足惜挠阁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溯饵。 院中可真熱鬧侵俗,春花似錦、人聲如沸丰刊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)啄巧。三九已至寻歧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秩仆,已是汗流浹背码泛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澄耍,地道東北人噪珊。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓晌缘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親痢站。 傳聞我的和親對(duì)象是個(gè)殘疾皇子磷箕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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