策略模式(Strategy Pattern):封裝變化,靈活應對需求變動

GitHub源碼分享

微信搜索:碼農StayUpjava

主頁地址:gozhuyinglong.github.iogit

源碼分享:github.com/gozhuyinglo…github

1. 一個簡單的模擬鴨子游戲

咱們先來看一個模擬鴨子的遊戲:遊戲中會出現各類鴨子,它們一邊游泳戲水,一邊呱呱叫。算法

通過一番調研後: 已知的鴨子種類有:野鴨(Mallard Duck)、紅頭鴨(Redhead Duck)、橡皮鴨(Rubber Duck)。 已知的鴨子行爲有:游泳(Swim)、嘎嘎叫(Quack)、顯示鴨子的樣子(Display)。編程

下面是這些鴨子的外觀:設計模式

野鴨

紅頭鴨

橡皮鴨

需求明確,開搞!微信

1.1 是時候展現OO技術了~

爲了可複用性,設計了一個鴨子超類Duck,並讓各類鴨子繼承此超類:markdown

  • 該超類中實現了swim()quack()方法,因爲每一種鴨子外觀不一樣,因此display()指定爲抽象方法(固然該類也是抽象類)。
  • 各個子類具體實現了display()方法
  • 因爲橡皮鴨不會「嗄嗄叫」,因此重寫了quack()方法爲「吱吱叫」。

下面是UML類圖: 簡版UML類圖ide

1.2 Change!!!

咱們知道軟件開發的一個不變的真理是:「變化」!oop

如今要求增長一種鴨子行爲:飛行(Fly),該怎麼作呢?

若是繼續使用繼承,那麼橡皮鴨是不會飛行的,仍是須要重寫fly()方法。以下:

簡版UML類圖

那若是再增長一種鴨子:誘餌鴨(Decoy Duck),這是隻木頭鴨子,它即不會叫,也不會飛行...... 誘餌鴨

看來繼承是不能知足需求了!

1.3 使用接口怎麼樣?

fly()方法和quack()抽離出來,作成接口,讓擁有該行爲的鴨子對其進行實現。以下:

簡版UML類圖

這彷佛解決了如今的問題!

但若是再增長100只鴨子呢?豈不是全部會飛行或會叫的鴨子,都要實現一遍,沒有達到代碼的複用性。並且一旦要修改某個行爲將是一件痛苦的事(好比將全部「吱吱叫」改爲「仿真呱呱叫」)……

1.4 封裝變化

有一個設計原則,剛好適用於上面模擬鴨子游戲的情況。

找出應用中可能須要變化之處,把它們獨立出來,不要和那些不須要變化的代碼混在一塊兒。

換句話說,若是每次來新的需求,都會使某方面的代碼發生變化,那麼你就能夠肯定,這部分的代碼須要被抽出來,和其餘穩定的代碼有所區分。

這即是策略模式的精神所在,下面咱們來看該模式的詳細介紹。

2. 策略模式

策略模式(Strategy Pattern)是一種行爲型模式。該模式定義一系列的算法,把它們一個個封裝起來,並使它們能夠相互替換。該模式讓算法的變化獨立於使用它的客戶。

Define a family of algorithms, encapsulate each one, and make them interchangeable.

該設計模式體現了幾個設計原則:

  • 封裝變化
  • 針對接口編程,而不是實現類
  • 多用組合,少用繼承

策略模式由三部分組成:

  • Strategy(策略)

定義了全部策略的公共接口。上下文(Context)使用這個接口來調用某個具體策略(ConcreteStrategy)。

  • ConcreteStrategy(具體策略)

Strategy接口的實現,定義了一個具體的策略實現。

  • Context(上下文)

定義了Strategy對象如何來使用,是策略算法的調用者。

策略模式原理圖

3. 代碼實現

咱們使用策略模式實現上面模擬鴨子游戲。

標準UML類圖(使用策略模式實現模擬鴨子游戲)

3.1 飛行行爲實現

定義飛行行爲接口

public interface Fly {
    void fly();
}
複製代碼

用翅膀飛行實現類

public class FlyWithWings implements Fly {
    @Override
    public void fly() {
        System.out.println("用翅膀飛行");
    }
}
複製代碼

不會飛行實現類

public class FlyNoWay implements Fly {
    @Override
    public void fly() {
        System.out.println("不會飛行");
    }
}

複製代碼

3.2 鴨叫行爲實現

定義鴨叫行爲接口

public interface Quack {
    void quack();
}
複製代碼

呱呱叫實現類

public class QuackGuaGua implements Quack {
    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}
複製代碼

吱吱叫實現類

public class QuackZhiZhi implements Quack {
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}
複製代碼

不會叫實現類

public class QuackNoWay implements Quack {
    @Override
    public void quack() {
        System.out.println("不會叫");
    }
}
複製代碼

3.3 鴨子類的實現

定義鴨子抽象類

public abstract class Duck {

    protected Fly fly;
    protected Quack quack;

    public void swim() {
        System.out.println("正在游泳...");
    }

    public abstract void display();

    public Fly getFly() {
        return fly;
    }

    public Quack getQuack() {
        return quack;
    }
}
複製代碼

野鴨實現類

public class MallardDuck extends Duck {

    // 野鴨用翅膀飛行,呱呱叫
    public MallardDuck() {
        this.fly = new FlyWithWings();
        this.quack = new QuackGuaGua();
    }

    @Override
    public void display() {
        System.out.println("外觀是綠頭鴨");
    }
}
複製代碼

紅頭鴨實現類

public class RedheadDuck extends Duck {

    // 紅頭鴨用翅膀飛行,呱呱叫
    public RedheadDuck() {
        this.fly = new FlyWithWings();
        this.quack = new QuackGuaGua();
    }

    @Override
    public void display() {
        System.out.println("外觀是紅頭鴨");
    }
}
複製代碼

橡皮鴨實現類

public class RubberDuck extends Duck {

    // 橡皮鴨不會飛行,吱吱叫
    public RubberDuck() {
        this.fly = new FlyNoWay();
        this.quack = new QuackZhiZhi();
    }

    @Override
    public void display() {
        System.out.println("外觀是橡皮鴨");
    }
}
複製代碼

誘餌鴨實現類

public class DecoyDuck extends Duck {

    // 誘餌鴨不會飛行,也不會叫
    public DecoyDuck() {
        this.fly = new FlyNoWay();
        this.quack = new QuackNoWay();
    }

    @Override
    public void display() {
        System.out.println("外觀是誘餌鴨");
    }
}
複製代碼

3.4 測試

編寫簡單測試類

public class Test {

    public static void main(String[] args) {
        MallardDuck mallardDuck = new MallardDuck();
        mallardDuck.display();
        mallardDuck.swim();
        mallardDuck.getFly().fly();
        mallardDuck.getQuack().quack();

        System.out.println("-------------------");

        DecoyDuck decoyDuck = new DecoyDuck();
        decoyDuck.display();
        decoyDuck.swim();
        decoyDuck.getFly().fly();
        decoyDuck.getQuack().quack();
    }
}
複製代碼

輸出結果

外觀是綠頭鴨
正在游泳...
用翅膀飛行
呱呱叫
-------------------
外觀是誘餌鴨
正在游泳...
不會飛行
不會叫
複製代碼

4. 完整代碼

完整代碼請訪問個人Github,若對你有幫助,歡迎給個Star,謝謝!

github.com/gozhuyinglo…

5. 參考

推薦閱讀

相關文章
相關標籤/搜索