設計模式之 策略模式(Strategy Pattern)

策略模式(Strategy Pattern)

策略模式定義了算法族,分別封裝起來,讓它們之間可以相互替換,此模式讓算法的變化獨立與使用算法的客戶。

從一個簡單的應用說起。

背景

假如我們要設計一款鴨子模擬器。有綠頭鴨,紅頭鴨,玩具鴨,橡皮鴨等。鴨子的行爲有呱呱叫,游泳,飛翔等。根據簡單的面向對象設計思路。我們先拿出第一版設計圖。

第一版

在這裏插入圖片描述
第一版,我們抽象了一個鴨子超類。其他所有類型鴨子從超類繼承。超類有呱呱叫(quack)和游泳(swim)方法。
好景不長,很快,我們的新需求出現了,鴨子需要飛行技能,於是我們故技重施,給鴨子超類添加fly()方法。
出現第二版:

第二版

在這裏插入圖片描述
此時,所有的鴨子子類都會繼承fly()方法,但問題是,並不是所有但鴨子都能飛。比如橡皮鴨子就不會飛。還發現了其他潛藏的問題,橡皮鴨子也不會呱呱叫。所以橡皮鴨必須將quack()覆蓋成“吱吱叫”(squeak)
當涉及“維護”時,爲了“複用”(reuse)的目的而使用繼承,結局並不完美
臨時方案:各子類根據情況覆蓋超類方法

第三版

在這裏插入圖片描述
此時,發現子類中,各種鴨子的差異很大,冗餘的“什麼都不做”代碼有點多。我們試着將fly()方法和quack()方法抽象爲接口,讓有需要的子類去自行實現這兩個接口如何?

第四版

在這裏插入圖片描述
你覺得這個設計如何?
雖然剝離出的兩個接口Flyable和Quackable從某種程度解決了一些代碼冗餘的問題,但造成了新的問題:代碼無法複用。每新建一個子類,都需要既繼承Duck超類又實現接口。直到。。。。。。。

身騎白馬—策略模式

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

是時候把鴨子的行爲從Duck類中取出來了。
我們知道 Duck類內的fly()和quack()會隨着鴨子的不同而改變。
爲了要不這兩個行爲從Duck類中分開,我們要把它們從Duck類中取出來,建立一組新類來代表每個行爲

那麼如何設計實現飛行和呱呱叫的行爲類呢?

針對接口編程,而不是針對實現編程

爲了靈活的設計,從現在開始,鴨子的子類將使用接口(FlyBehavior與QuackBehavior)所表示的行爲,實際的“實現”不會被綁死在鴨子的子類中。
如圖:

“針對接口編程”真正的意思是“針對超類型(supertype)編程。

在這裏插入圖片描述
這樣的設計,可以讓飛行和呱呱叫的動作被其他對象複用,因爲這些行爲已經與鴨子類無關了。
而我們可以新增一些行爲,不會影響到既有的行爲類,也不會影響“使用”到飛行行爲的鴨子類。

整合鴨子的行爲

關鍵在於,鴨子現在會將飛行和呱呱叫的動作“委託”(delegate)別人處理,而不是使用定義在Duck類(或子類)內的呱呱叫和飛行方法。
在這裏插入圖片描述

代碼實現

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-21:57
 */

public abstract class Duck {

    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public Duck(){

    }

    public abstract void display();

    public void performFly(){
        flyBehavior.fly();  //委託給行爲類
    }

    public void performQuack(){
        quackBehavior.quack();  //委託給行爲類
    }

    public void swim(){
        System.out.println("All ducks float,even decoys!!");
    }

    public void setFlyBehavior(FlyBehavior fb){
        //可隨時通過該方法修改子類鴨子的飛行行爲
        flyBehavior=fb;
    }

    public void setQuackBehavior(QuackBehavior  qb){
//        可隨時通過該方法修改子類鴨子的鳴叫行爲
        quackBehavior=qb;
    }


}

package strategy;

public interface QuackBehavior {

    public void quack();
}

package strategy;

public interface FlyBehavior {
    public void fly();
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:01
 */

public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {

        System.out.println("I cat't fly");
    }
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:21
 *
 * 建立一個利用火箭動力的飛行行爲
 */

public class FlyRocketPowered implements FlyBehavior {
    @Override
    public void fly() {

        System.out.println("I'm flying with a rocket!!");
    }
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-21:59
 */

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I'm Flying!!!");
    }
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:01
 */

public class Quack implements QuackBehavior {
    @Override
    public void quack() {

        System.out.println("strategy.Quack");
    }
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:03
 */

public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {

        System.out.println("<< Slience  >>");
    }
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:08
 */

public class MallarDuck extends Duck {

    public MallarDuck(){
        quackBehavior=new Quack();  //TODO 這裏不夠靈活
        flyBehavior=new FlyWithWings();//TODO  這裏不夠靈活
    }

    @Override
    public void display() {

        System.out.println("I'm a real Mallard duck");
    }
}

package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:18
 */

public class ModelDuck extends Duck {
    public ModelDuck(){
        flyBehavior=new FlyNoWay();//模型鴨是不會飛的
        quackBehavior=new Quack();//模型鴨會叫
    }

    @Override
    public void display() {

        System.out.println("I'm a model duck");
    }
}
package strategy;

/**
 * @author zhangjinglong
 * @date 2019-11-05-22:11
 * 
 * 測試類
 */

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard=new MallarDuck();
        mallard.performFly();
        mallard.performQuack();


        Duck model=new ModelDuck();
        model.performFly();//模型鴨默認不會飛
        model.setFlyBehavior(new FlyRocketPowered());//給模型鴨設定火箭動力的飛行行爲
        model.performFly();//模型鴨 動態改變了飛行行爲
    }
}



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