鴨子在叫喲,嘎嘎嘎---策略模式

    你看那裏有隻小鴨喲,你看那裏又只小鴨喲~小鴨嘎嘎嘎-ga-喲~小鴨嘎嘎嘎-ga-喲~j_0003.gif

    j_0012.gif矮油我艹,居然被這神曲給洗腦了。現在一看到小動物就莫名其妙的會哼唱起來j_0013.gif,那麼問題來了,長頸鹿怎麼叫?“好長~好長~”j_0011.gif是這樣叫的麼?這個問題真的很逗逼耶……j_0009.gif

    好啦,不扯淡了,原歸正傳。我要講的主題就是小鴨子!哦sorry,準確的應該說是策略模式。因爲策略模式中最經典例子莫過於鴨子模型的設計啦。之前有很多大神都寫過文章講解,講得都非常深入和細緻。但我這篇文章中,只是大略的以個人理解思維去闡述一個故事,如有講得不足之處還望見諒。

    故事的開頭是這樣,會議上老闆說:“最近接了個玩具廠的項目,要我們做個鴨子程序。沒啥需求,就是要有大黃鴨、青頭鴨、醜小鴨、唐老鴨……它們都會叫會飛!好啦,這種小項目就交給剛來實習的小林去做吧。”我胸有成足的接下了這個任務,心想這小兒科so easy ~

    先做個鴨子接口:

public interface Ducks {

    /**
     * 外貌
     */
    abstract void display();

    /**
     * 叫聲
     */
    abstract void quack();

    /**
     * 飛行
     */
    abstract void fly();

    /**
     * 游泳
     */
    public void swim();

}

    在寫鴨子接口的實現類:

public class Green implements Ducks {

    @Override
    public void display() {
        System.out.println("我是青頭鴨~~");
    }

    @Override
    public void quack() {
        System.out.println("gaga~叫聲很好聽");
    }

    @Override
    public void fly() {
        System.out.println("飛得老高了我~~");
    }

    @Override
    public void swim() {
        System.out.println("遊的老快了~");
    }

}

    …………   

    最後呢,在 if-else if- 來判斷我要調用的是那個模型的鴨子:

public void selectDuck(String type) {
        if (type.equals("yellow")) {
            Ducks duck = new Green();
            duck.quack();
        } else if (type.equals("green")) {
            Ducks duck = new Yellow();
            duck.fly();
        }
    }

    恩,很快的項目完成了。我高高興興的把第一版本給提交了。老闆哪會管你是如何實現的,只要你能完成預期的功能效果就ok。正當我還在得意洋洋之時,問題就來了。玩具廠的說要讓有的鴨子不會飛,有的鴨子不會叫,有的鴨子不僅會叫還要回唱歌,唱歌還不夠,要給鴨子分等級,等級高的還會講故事……

    呵呵,我勒個擦!那我豈不是要在每個鴨子接口和實現類中添加好多方法?還得在調用selectDuck中多做好多else if判斷?今天改了這些歌需求,萬一明天又突然給我加過別的需求,我不是又要重新改一遍代碼了麼?憂傷的我,一籌莫展,對着電腦發呆。我該怎麼辦纔好呢,讓我別每次都改動原來寫好的代碼,有新的需求的時候只要多添加個類就好了?

    “今天怎~~~麼不開心~?”老周問我:“不用擔心,時間會給你答案~我的滑板鞋時尚時尚最時尚~”我吧事情跟老周詳細講了一遍後,老周笑道:“騷年,你還太年輕!這完全可以用策略模式去解決啊!”

    於是乎老周就開始很裝逼的給我云云:


一、所謂的策略模式呢就是:

    定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。“親,您能說的直白點嗎?”“哦,抱歉。我向來都是這麼專業的。說白了,就是你不是有很多鴨子嗎?每個鴨子要有不同的功能,比如說飛行的算法,叫聲的算法,游泳的算法,你都統統把它們一個一個的封裝起來。到時候具體需要什麼樣的鴨子,就給客戶自己去選擇咯。”


二、那到底要怎麼去實現呢?

    1. 對策略對象定義一個公共接口(FlyBehavior、QuackBehavior……接口;把所有的行爲都做成接口方便以後對策略的擴展)
    2. 編寫具體策略類,該類實現了上面的接口(FlyNoWay、FlyWithWings、Quack、MuteQuack……以後要是有什麼新功能,就只需要去實現行爲接口,添加新的策略類咯)
    3. 在使用策略對象的類(即:環境角色)中保存一個對策略對象的引用(Duck;最爲關鍵的就是這個引用對象了,既然我們是要創建鴨子,當然就要用鴨子去引用策略啦。)
    4. 在使用策略對象的類中,實現對策略對象的set和get方法(注入)或者使用構造方法完成賦值(引用的小技巧就是用傳說中的"組合")
    5. 客戶端進行調用 (調用不同的鴨子時就分配給它相應的策略咯)

    扯了半天還是雲裏霧裏的感覺。老周啊,你妹圖扯個蛋呀!結果老周不服氣,馬上畫了張圖:

wKioL1R4Gy6C31F4AAEIY8RUODs915.jpg

    抽象策略角色:策略類,通常由一個接口或者抽象類實現。

    具體策略角色:包裝了相關的算法和行爲。

    環境角色:持有一個策略類的引用,最終給客戶端用的。


三、終於開竅了,老周啊,你真是中國好同事喲。按照老周說的硬是把代碼給敲出來了:

    首先,鴨子喊叫和飛行的公共接口。

package com.strategy.quack;

/**
 * 鴨子叫的接口
 * @author linhy
 *
 */
public interface QuackBehavior {

    public void quack();
}
package com.strategy.fly;

/**
 * 鴨子飛行接口
 * @author linhy
 *
 */
public interface FlyBehavior {

    public void fly();

}

    然後,就是這兩個接口的實現類。

package com.strategy.quack;

/**
 * 會叫的鴨子類
 * @author linhy
 *
 */
public class Quack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("嘎嘎嘎~聽得到我在叫你麼……");
    }

}
package com.strategy.quack;

/**
 * 不會叫的鴨子類
 * @author linhy
 *
 */
public class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("我是一隻安靜的沒鴨子,不會叫~");
    }

}
package com.strategy.fly;

/**
 * 會飛的鴨子類
 * @author linhy
 *
 */
public class FlyWithWings implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("我是在用翅膀飛翔,帶你裝B,帶你飛……");
    }

}
package com.strategy.fly;

/**
 * 不會飛的鴨子類
 * @author linhy
 *
 */
public class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("超人不會飛,鴨子也不會飛……");
    }

}

    恩,策略都已經準備好了,接下來要開始引用它了。用什麼引用?當然是鴨子啦!但是,鴨子又有好多種類型的哦,所以要先把鴨子抽象下,方便以後再創建新類型鴨子……

package com.strategy.duck;

import com.strategy.fly.FlyBehavior;
import com.strategy.quack.QuackBehavior;

/**
 * 鴨子抽象類
 * @author linhy
 *
 */
public abstract class Duck {
    //------------組合方式引用鴨子策略--------------------
    //引入鴨子的飛行行爲對象
    private FlyBehavior flyBehavior;
    //引入鴨子的叫喚行爲對象
    private QuackBehavior quackBehavior;

    //通過子類來設置鴨子的飛行行爲
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    //通過子類來設置鴨子的叫喚行爲
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

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

    public void performQuack() {
        quackBehavior.quack();
    }

    // 鴨子的外貌不一樣哦,所以我要抽象~
    abstract void display();

    //鴨子都會游泳哦,游泳的方式就暫且都一樣吧……
    public void swim() {
        System.out.println("All ducks float, even decoys");
    }

}

    抽象的鴨子已經設計好咯,就要寫具體繼承它的子類咯。

package com.strategy.duck;

import com.strategy.fly.FlyBehavior;
import com.strategy.quack.QuackBehavior;

/**
 * 青頭鴨
 * @author linhy
 *
 */
public class GreenDuck extends Duck {
    //通過構造方法來設置引用的鴨子對象的行爲
    public GreenDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super.setFlyBehavior(flyBehavior);
        super.setQuackBehavior(quackBehavior);
    }

    @Override
    public void display() {
        System.out.println("我是一隻美麗動人的青頭鴨~~");
    }

}
package com.strategy.duck;

import com.strategy.fly.FlyBehavior;
import com.strategy.quack.QuackBehavior;

/**
 * 小黃鴨
 * @author linhy
 *
 */
public class YellowDuck extends Duck {
    //通過構造方法來設置引用的鴨子對象的行爲
    public YellowDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super.setFlyBehavior(flyBehavior);
        super.setQuackBehavior(quackBehavior);
    }

    @Override
    public void display() {
        System.out.println("我是一隻美麗動人的小黃鴨~~");
    }

}

    這下鴨子模型準備好了,策略方法也寫好了。那麼就開始調用測試下看看唄。

package test;

import com.strategy.duck.GreenDuck;
import com.strategy.duck.YellowDuck;
import com.strategy.fly.FlyNoWay;
import com.strategy.fly.FlyWithWings;
import com.strategy.quack.MuteQuack;
import com.strategy.quack.Quack;

/**
 * 給不同類型的鴨子設置不同行爲的策略
 * @author linhy
 *
 */
public class TestDuck {

    public static void main(String[] args) {
        //設置小黃鴨會飛和叫
        YellowDuck yellowDuck = new YellowDuck(new FlyWithWings(), new Quack());
        //設置青頭鴨不會飛,也不會叫
        GreenDuck greenDuck = new GreenDuck(new FlyNoWay(), new MuteQuack());
        System.out.println("***************************************");
        yellowDuck.display();
        yellowDuck.performFly();
        yellowDuck.performQuack();
        yellowDuck.swim();
        System.out.println("*****************************************");
        greenDuck.display();
        greenDuck.performFly();
        greenDuck.performQuack();
        greenDuck.swim();

    }

}

    結果是這樣的:

wKiom1R4FH-zKPBYAAGjzLUqgPw503.jpg

    嘿嘿,是這樣的哦。別看上去好像感覺要寫那麼多類啊,接口啊……感覺好麻煩。當你要去維護,去新增功能的時候,你就會體會到一個好的設計模式是多麼的方便。好比,下次老闆要是提出:小林,我要一隻超級鴨(多功能的鴨子),會講故事、會跳舞……我只需要新增個些策略和超級鴨子類,鴨子抽象類稍微修改下就可以。不要像以前那種蠢方法,耦合度太高,要修改太多地方,代碼看上去也不整潔。

    老周最後來了一句:圖羊圖森破~。不裝逼你會死呀,心裏暗罵到。但還是忘分感謝……畢竟成長的道路上就是要在不斷的挫折中學習。


結語:

    嘖嘖,故事終於講完了,我知道自己講的相當爛,有好多東西沒說清。特附上一篇策略模式的好文章:《研磨設計模式之策略模式》

    

 


    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章