設計模式之策略模式

  今天咱們來看一下策略模式。在真正介紹策略模式以前呢,咱們先經過一個很是生動有趣的例子的引入。算法

  咱們如今有一個模擬鴨子的程序。編程

  一、鴨子父類以下:ide

public abstract class Duck {

    /**
     * 呱呱叫行爲(全部鴨子都會)
     */
    public void quack() {
        System.out.println("呱呱叫");
    }

    /**
     * 游泳行爲(全部鴨子都會)
     */
    public void swim() {
        System.out.println("游泳");
    }

    /**
     * 每一個鴨子額外觀都不一樣  因此display是抽象方法
     */
    abstract void display();
}

  MallardDuck子類測試

public class MallardDuck extends Duck {

    /**
     *頭是綠色的
     */
    @Override
    void display() {
        System.out.println("外觀是綠頭的鴨子");
    }
}

  RedheadDuck子類this

public class RedheadDuck extends Duck {

    /**
     * 外觀是紅頭
     */
    @Override
    void display() {
        System.out.println("外觀是紅頭的鴨子");
    }
}

  二、如今呢,有一個需求,須要讓鴨子具有會飛的功能。因此,理所固然的就給Duck父類添加上了一個fly()方法;可是這裏問題出現了,一些不具有會飛行爲的鴨子也具有了「飛」的行爲(好比,咱們如今有一個橡皮鴨子,它不會飛,而且不會呱呱叫,只會吱吱叫)。spa

  三、這時候,咱們想到,能夠在重寫橡皮鴨子的fiy()方法,讓它什麼都不作,並重寫quack()方法,讓它吱吱叫。代碼以下:設計

public class RubberDuck extends Duck {

    /**
     * 不會飛 什麼都不作
     */
    @Override
    public void display() {
        System.out.println("");
    }

    /**
     * 吱吱叫(不會 呱呱叫)
     */
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}

  四、但是,若是之後咱們加入誘餌鴨(DecoyDuck),它是一隻假鴨子,不會飛也不會叫。難道咱們要接着重寫方法嗎?code

  五、利用接口如何?那好,加入咱們利用接口,把fly()方法從超類中取出來,放進「Fiyable」接口中。這麼一來,只有會飛的鴨子才實現此接口,一樣的方式,也能夠設計一個「Quackable」接口,由於並非全部的鴨子都會叫。orm

  用這種方法確實解決了一部分的問題(不會再有會飛的橡皮鴨子),可是卻形成代碼沒法被複用(由於每個子類都須要實現接口的方法)。對象

  如今咱們想一下,會不會有一種對既有的代碼影響最小的方式來修改程序?

  六、如今咱們有一個設計原則:找出應用中可能須要變化之處,把它們獨立出來,不要和那些不須要變化的代碼混在一塊兒。以此讓咱們的代碼變化更少,系統更加有彈性。

  七、爲了讓程序有彈性,而且咱們還想可以指定行爲到鴨子的實例,讓鴨子能夠在「運行時」改變「鴨子」的行爲。從如今開始,鴨子的行爲將被放在分開的類中,此類專門提供某行爲的接口的實現。這樣,鴨子就再也不須要知道行爲的實現細節。(設計原則:針對接口編程,而不是針對實現編程)。

  因此,咱們先聲明兩個接口,FlyBehavior和QuackBehavior,而行爲的每一個實現都將實現其中的一個接口。直接上代碼。

  FlyBehavior(「飛」行爲接口):

public interface FlyBehavior {

    /**
     * 飛(全部的新的飛行類都必須實現fly方法)
     */
    void fly();
}

  用翅膀飛類:

public class FlyWithWings implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("用翅膀來飛行");
    }
}

  不會飛類:

public class FlyNoWay implements FlyBehavior {

    /**
     * 不會飛
     */
    @Override
    public void fly() {
        System.out.println("");
    }
}

  QuackBehavior(「叫」行爲接口):

public interface QuackBehavior {

    /**
     * 呱呱叫行爲(每個新的叫行爲都必須實現quack方法)
     */
    void quack();
}

  呱呱叫行爲類:

public class Quack implements QuackBehavior {

    /**
     * 呱呱叫
     */
    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}

  吱吱叫行爲類:

public class Squeak implements QuackBehavior {

    /**
     * 吱吱叫
     */
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}

  不會叫類:

public class MuteQuack implements QuackBehavior {

    /**
     * 不會叫
     */
    @Override
    public void quack() {
        System.out.println("");
    }
}

  八、下面,咱們來從新改寫鴨子父類:

public abstract class Duck {

    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    /**
     * 游泳行爲(全部鴨子都會)
     */
    public void swim() {
        System.out.println("游泳");
    }

    /**
     * 每一個鴨子額外觀都不一樣  因此display是抽象方法
     */
    public abstract void display();

    /**
     * 飛行爲
     */
    public void performFly() {
        flyBehavior.fly();
    }

    /**
     * 呱呱叫行爲
     */
    public void performQuack() {
        quackBehavior.quack();
    }

    /**
     * 設置飛行爲
     * @param flyBehavior
     */
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    /**
     * 設置叫行爲
     * @param quackBehavior
     */
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}

  重寫綠頭鴨類:

public class MallardDuck extends Duck {

    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

    /**
     *頭是綠色的
     */
    @Override
    public void display() {
        System.out.println("外觀是綠頭的鴨子");
    }
}

  經過咱們的改寫,會發現,咱們的綠頭鴨子在new一個對象的時候,會呱呱叫,會用翅膀飛,咱們還能夠運行時改寫它的飛行爲的方式,能夠把它設爲不會飛,或者咱們在寫一個FlyBehavior的實現類,用另一種方式來飛,這些都是能夠的。

 咱們寫一個測試類:

public static void main(String[] args) {
        Duck mallardDuck = new MallardDuck();
        mallardDuck.performFly();
        mallardDuck.setFlyBehavior(new FlyNoWay());
        mallardDuck.performFly();
    }

  執行結果:

  九、最後,咱們會發現,若是再來新的需求,咱們不須要改變原來的任何代碼,好比來了一隻會用火箭飛的鴨子,那咱們只須要新寫一個FlyBehavior的實現類,而後在構造器裏寫出來就好了。對修改關閉,對擴展開放。

  十、如今  咱們已經學會了策略模式。哈哈。策略模式:定義了算法族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶。

 

最後來個總結:

意圖:定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。

 

主要解決:在有多種算法類似的狀況下,使用 if...else 所帶來的複雜和難以維護。

 

什麼時候使用:一個系統有許多許多類,而區分它們的只是他們直接的行爲。

 

如何解決:將這些算法封裝成一個一個的類,任意地替換。

 

關鍵代碼:實現同一個接口。

 

應用實例: 一、諸葛亮的錦囊妙計,每個錦囊就是一個策略。 二、旅行的出遊方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。 三、JAVA AWT 中的 LayoutManager。

 

優勢: 一、算法能夠自由切換。 二、避免使用多重條件判斷。 三、擴展性良好。

 

缺點: 一、策略類會增多。 二、全部策略類都須要對外暴露。

 

使用場景: 一、若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態地讓一個對象在許多行爲中選擇一種行爲。 二、一個系統須要動態地在幾種算法中選擇一種。 三、若是一個對象有不少的行爲,若是不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。

 

注意事項:若是一個系統的策略多於四個,就須要考慮使用混合模式,解決策略類膨脹的問題。

相關文章
相關標籤/搜索