Head First設(shè)計(jì)模式學(xué)習(xí)筆記一策略模式

假如我們現(xiàn)在有一個(gè)鴨子粉臊,鴨子會(huì)呱呱叫草添,也會(huì)游泳,但是每個(gè)鴨子的外觀不相同(有白顏色的维费,有綠色的)果元,那么你會(huì)怎么設(shè)計(jì)這個(gè)鴨子呢?

我們第一肯定是想到設(shè)計(jì)一個(gè)鴨子超類(lèi)犀盟,這個(gè)超類(lèi)包括swim()和quack()兩個(gè)方法而晒,還有一個(gè)抽象的dispaly()方法。

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

    public abstract void display();
}

白色的鴨子繼承Duck類(lèi)阅畴,并實(shí)現(xiàn)display()方法

public class WhiteDuck extends Duck {
    public void display() {
        System.out.println("I am a white duck!");
    }
}

綠色鴨子也繼承Duck類(lèi)倡怎,實(shí)現(xiàn)自己的display()方法

public class GreenDuck extends Duck {
    public void display() {
        System.out.println("I am a green duck!");
    }
}

如果這個(gè)時(shí)候我們加了一個(gè)需求,要求要鴨子會(huì)飛贱枣,那么你又會(huì)怎么設(shè)計(jì)呢监署?你是不是首先想到在Duck類(lèi)上加上一個(gè)fly()方法,像下面這樣

public abstract class Duck {

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

    public void quack(){
        System.out.println("quack quack");
    }

    public abstract void display();

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

但是如果并不是所有的鴨子都會(huì)飛纽哥,比如橡皮鴨子不會(huì)飛钠乏。這時(shí)候你會(huì)想是不是想可以在橡皮鴨中覆蓋掉fly()方法,讓fly()方法啥也不做春塌,像下面這樣

public class RubberDuck extends Duck{
    public void display() {
        System.out.println("I am a rubber duck");
    }

    @Override
    public void fly() {
        
    }
}

雖然上面這個(gè)方法可以暫時(shí)解決這個(gè)問(wèn)題晓避,但是如果這個(gè)時(shí)候加入了一個(gè)木頭鴨子簇捍,它既不會(huì)呱呱叫,也不會(huì)飛俏拱,那這個(gè)時(shí)候你是不是就會(huì)想到在木頭鴨子里覆蓋掉fly()方法和quack()方法暑塑。但這樣帶來(lái)的問(wèn)題就是如果有成千上百個(gè)鴨子,每次都要檢查quack()方法和fly()方法锅必,這簡(jiǎn)直是無(wú)窮無(wú)盡的噩夢(mèng)事格。

那如果把fly()方法和quack()方法抽出來(lái)呢,放到一個(gè)Flyable接口和一個(gè)Quackable接口當(dāng)中搞隐。讓會(huì)飛的鴨子實(shí)現(xiàn)Flyable接口驹愚,會(huì)呱呱叫的鴨子實(shí)現(xiàn)Quackable()接口。
Flyable接口

public interface Flyable {
    void fly();
}

WhiteDuck類(lèi)

public class WhiteDuck extends Duck implements Flyable{
    public void display() {
        System.out.println("I am a white duck");
    }

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

假如現(xiàn)在有個(gè)火箭鴨尔许,它能以火箭的動(dòng)力飛行么鹤,我們可以這樣

public class RocketDuck extends Duck implements Flyable{
    public void fly() {
        System.out.println("I can fly with rocket");
    }

    public void display() {
        System.out.println("I am a rocket duck");
    }
}

這樣看好像沒(méi)什么問(wèn)題终娃,每個(gè)種類(lèi)的鴨子都可以選擇性的實(shí)現(xiàn)自己想實(shí)現(xiàn)的接口味廊。但是你忽視了一個(gè)非常大的問(wèn)題,假如鴨子一共就有三種飛行方法(用翅膀飛棠耕、不會(huì)飛余佛、以火箭動(dòng)力去飛),這個(gè)時(shí)候如果你有幾十種鴨子的話窍荧,就會(huì)造成大量的代碼冗余辉巡,如果有的鴨子要修改一下飛行行為,就要對(duì)這些鴨子的fly()方法逐一的修改蕊退。

那么我們?cè)賮?lái)看看這種情況策略模式怎么去做的呢郊楣?
策略模式會(huì)將代碼中變化的部分抽取出來(lái)封裝(比如fly,quack),以便以后可以輕易的改動(dòng)或擴(kuò)充此部分瓤荔,而不影響不需要變化的其他部分净蚤。其實(shí)這種思想是每個(gè)設(shè)計(jì)模式背后的精神所在。所有的設(shè)計(jì)模式都提供了一套方法讓“系統(tǒng)中的某部分改變不會(huì)影響其他部分”输硝。

我們利用接口代表每個(gè)行為今瀑,比方說(shuō),F(xiàn)lyBehavior與QuackBehavior点把,而行為的每個(gè)實(shí)現(xiàn)都將實(shí)現(xiàn)其中的一個(gè)接口橘荠。所以這次鴨子類(lèi)不會(huì)負(fù)責(zé)實(shí)現(xiàn)Flyable與Quackable接口,反而是由我們制造的一組其他類(lèi)專(zhuān)門(mén)負(fù)責(zé)實(shí)現(xiàn)FlyBehavior與QuackBehavior郎逃,這種就稱(chēng)為“行為類(lèi)”哥童。所以實(shí)際的行為實(shí)現(xiàn)不會(huì)綁死在鴨子的子類(lèi)當(dāng)中。

FlyBehavior接口

public interface FlyBehavior {
    void fly();
}

FlyWithWing類(lèi)褒翰,用翅膀飛

public class FlyWithWing implements FlyBehavior {
    public void fly() {
        System.out.println("I can fly with wing");
    }
}

FlyWithRocket類(lèi)贮懈,以火箭的動(dòng)力飛

public class FlyWithRocket implements  FlyBehavior {
    public void fly() {
        System.out.println("I can fly with rocket");
    }
}

FlyNoWay類(lèi)压恒,不會(huì)飛

public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("I can not fly");
    }
}

QuackBehavior接口同理,這里就不貼代碼了错邦。

我們看到這樣的設(shè)計(jì)可以使相同的行為代碼能被復(fù)用探赫,即使我們?cè)傩略鲆恍┬袨橐膊粫?huì)影響既有的行為類(lèi),也不會(huì)影響到使用飛行行為的鴨子類(lèi)撬呢。

那么我們?cè)鯓訉⑿袨轭?lèi)和和鴨子類(lèi)進(jìn)行整合呢伦吠?
首先,我們?cè)貲uck類(lèi)中加入兩個(gè)變量魂拦,分別為"flyBehavior"與"quackBehavior"毛仪,申明為接口類(lèi)型而不是具體的實(shí)現(xiàn),每個(gè)鴨子都會(huì)動(dòng)態(tài)的設(shè)置這些變量以在運(yùn)行時(shí)引用正確的行為類(lèi)型芯勘。我們?cè)僖詢(xún)蓚€(gè)相似的方法performFly()和performQuack()取代Duck類(lèi)中的fly()方法和quack()方法箱靴。

改過(guò)之后的Duck類(lèi)

public abstract class Duck {
    FlyBehavior flyBehavior;

    public void performFly(){
        flyBehavior.fly();
    }
    
    public abstract void display();
}

我們看到要在Duck類(lèi)中,我們要實(shí)現(xiàn)飛行的行為荷愕,只需要flyBehavior去飛就行了衡怀,在Duck類(lèi)中,我們不在乎flyBehavior接口的對(duì)象到底是什么安疗,我們只要關(guān)心該對(duì)象如何進(jìn)行飛行就行抛杨。

接下來(lái)我們通過(guò)RocketDuck看一下我們?cè)鯓尤ピO(shè)置flyBehavior變量

public class RocketDuck extends Duck {
    public RocketDuck() {
        flyBehavior = new FlyWithRocket();
    }

    public void display() {
        System.out.println("I am a rocket duck");
    }
}

我們看到,RocketDuck類(lèi)中的默認(rèn)構(gòu)造方法會(huì)設(shè)置flyBehavior變量為一個(gè)FlyWithRocket對(duì)象荐类,當(dāng)調(diào)用performFly()方法時(shí)就會(huì)去調(diào)用FlyWithRocket對(duì)象的fly()方法怖现。我們寫(xiě)一個(gè)Main方法去執(zhí)行看一下:

public class Main {
    public static void main(String[] args) {
        Duck duck = new RocketDuck();
        duck.performFly();
    }
}
輸出:
I can fly with rocket

我們還可以在Duck類(lèi)中通過(guò)setter方法類(lèi)設(shè)定鴨子的行為類(lèi),以達(dá)到隨時(shí)改變鴨子行為的效果玉罐。

public abstract class Duck {
    FlyBehavior flyBehavior;

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void performFly(){
        flyBehavior.fly();
    }

    public abstract void display();
}

假設(shè)現(xiàn)在有一個(gè)模型鴨ModelDuck屈嗤,我們就可以這樣

public class ModelDuck extends Duck {
    public ModelDuck() {
        flyBehavior = new FlyWithWing();
    }

    public void display() {
        System.out.println("I am a model duck");
    }
}

再運(yùn)行看一下:

public class Main {
    public static void main(String[] args) {
        Duck duck = new ModelDuck();
        duck.performFly();
        duck.setFlyBehavior(new FlyNoWay());
        duck.performFly();
    }
}
輸出:
I can fly with wing
I can not fly

我們能看到通過(guò)setter方法可以動(dòng)態(tài)的區(qū)改變鴨子的行為。

那么假設(shè)現(xiàn)在我們的鴨子會(huì)講話吊输,但是每種鴨子會(huì)講的語(yǔ)言不一樣饶号,有的會(huì)講英文,有的會(huì)講中文璧亚,還有的會(huì)講漢語(yǔ)讨韭,這個(gè)時(shí)候我們應(yīng)該怎么做呢?

首先我們可以寫(xiě)一個(gè)SpeakBehavior接口癣蟋,它有一個(gè)speak()方法透硝。

public interface SpeakBehavior {
    void speak();
}

原后會(huì)講漢語(yǔ)的鴨子和會(huì)講英語(yǔ)的鴨子分別實(shí)現(xiàn)SpeakBehavior接口

public class SpeakChinese implements SpeakBehavior {
    public void speak() {
        System.out.println("I can speak chinese");
    }
}
public class SpeakEnglish implements SpeakBehavior {
    public void speak() {
        System.out.println("I can speak english");
    }
}

原后在Duck類(lèi)中加入speakBehavior變量

public abstract class Duck {
    FlyBehavior flyBehavior;
    SpeakBehavior speakBehavior;

    public void setSpeakBehavior(SpeakBehavior speakBehavior) {
        this.speakBehavior = speakBehavior;
    }
    
    public void performBehavior(){
        speakBehavior.speak();
    }

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void performFly(){
        flyBehavior.fly();
    }

    public abstract void display();
}

在RocketDuck類(lèi)中添加說(shuō)話的行為

public class RocketDuck extends Duck {
    public RocketDuck() {
        flyBehavior = new FlyWithRocket();
        speakBehavior = new SpeakChinese();
    }

    public void display() {
        System.out.println("I am a rocket duck");
    }
}

我們運(yùn)行看一下

public class Main {
    public static void main(String[] args) {
        Duck duck = new RocketDuck();
        duck.performFly();
        duck.performSpeak();
        duck.setFlyBehavior(new FlyNoWay());
        duck.performFly();
        duck.setSpeakBehavior(new SpeakEnglish());
        duck.performSpeak();
    }
}
輸出:
I can fly with rocket
I can speak chinese
I can not fly
I can speak english

我們看到新加的行為不會(huì)影響到老的行為的運(yùn)行。

總結(jié)
每一個(gè)鴨子都有一個(gè)FlyBehavior和一個(gè)SpeakBehavior疯搅,好將飛行和講話委托給他們處理濒生。當(dāng)你將兩個(gè)類(lèi)結(jié)合起來(lái)使用,如同本例一般幔欧,這就是組合(composition)罪治。這種做法和“繼承”不同的地方在于丽声,鴨子的行為不是繼承來(lái)的,而是和適當(dāng)?shù)男袨閷?duì)象“組合”來(lái)的觉义。這是一個(gè)很重要的技巧雁社。其實(shí)是使用了我們的第三個(gè)設(shè)計(jì)原則:
多用組合,少用繼承

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末晒骇,一起剝皮案震驚了整個(gè)濱河市霉撵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洪囤,老刑警劉巖徒坡,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瘤缩,居然都是意外死亡喇完,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)剥啤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锦溪,“玉大人,你說(shuō)我怎么就攤上這事铐殃『M荩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵富腊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我域帐,道長(zhǎng)赘被,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任肖揣,我火速辦了婚禮民假,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘龙优。我一直安慰自己羊异,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布彤断。 她就那樣靜靜地躺著野舶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宰衙。 梳的紋絲不亂的頭發(fā)上平道,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音供炼,去河邊找鬼一屋。 笑死窘疮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冀墨。 我是一名探鬼主播闸衫,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诽嘉!你這毒婦竟也來(lái)了楚堤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤含懊,失蹤者是張志新(化名)和其女友劉穎身冬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體岔乔,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酥筝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雏门。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘿歌。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖茁影,靈堂內(nèi)的尸體忽然破棺而出宙帝,到底是詐尸還是另有隱情,我是刑警寧澤募闲,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布步脓,位于F島的核電站,受9級(jí)特大地震影響浩螺,放射性物質(zhì)發(fā)生泄漏靴患。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一要出、第九天 我趴在偏房一處隱蔽的房頂上張望鸳君。 院中可真熱鬧,春花似錦患蹂、人聲如沸或颊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)囱挑。三九已至,卻和暖如春格了,著一層夾襖步出監(jiān)牢的瞬間看铆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工盛末, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弹惦,地道東北人否淤。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像棠隐,于是被迫代替她去往敵國(guó)和親石抡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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

  • 設(shè)計(jì)模式 開(kāi)題先說(shuō)明一下助泽,設(shè)計(jì)模式告訴我們?nèi)绾谓M織類(lèi)和對(duì)象以解決某種問(wèn)題啰扛。讓代碼變得更加優(yōu)雅是我們責(zé)無(wú)旁貸的任務(wù) ...
    tanghuailong閱讀 447評(píng)論 0 2
  • 6:00起床 控制食量 原有的3/4 上午 一次突擊減脂訓(xùn)練 & 日常的減脂K3 午睡30分鐘 下午 爬南山(走車(chē)...
    藍(lán)藍(lán)的精靈閱讀 164評(píng)論 0 0
  • 黃佑鳳6月23日總結(jié):走路上班,忙碌的一天嗡贺,事太多了隐解,過(guò)了就想不起到某個(gè)具體事件了,反正具體到事思路清晰诫睬,如果是發(fā)...
    天命沉淪閱讀 107評(píng)論 0 0
  • ———我的自律是場(chǎng)自救摄凡。 救活我之后倒推出自律的根源—安全感续徽。 1.人群的汲暖與自身的不安 之前的我,因?yàn)楹ε鹿聠?..
    大芹芹閱讀 468評(píng)論 0 1
  • 仙,飄逸床绪,無(wú)拘無(wú)束客情,逍遙自在;圣会涎,沉郁裹匙,體察民情,憂國(guó)憂民末秃。 李白神于詩(shī),杜甫圣于詩(shī)籽御。 青蓮號(hào)...
    好名字可以讓朋友更容易記住你啊閱讀 278評(píng)論 2 7