HeadFirst設計模式(一) - 策略模式

鴨子游戲例子java

    Joe上班的公司作了一套至關成功的模擬鴨子游戲:SimUDuck。遊戲中出現各類鴨子,一邊游泳戲水(swim),一邊呱呱叫(quack),注意是呱呱叫,而不是吱吱叫。此係統的內部設計使用了標準的OO技術,設計了一個鴨子超類(Superclass),並讓各類鴨子繼承此超類。編程

    公司需求有新增,主管要求讓讓鴨子能夠飛。編程語言

    Joe固然以爲這沒有什麼問題,只須要在Duck類中加入fly()方法就能夠,他也是這麼作的。ide

    在產品發佈會上,主管打來電話說,他在發佈會看到不少橡皮鴨子在屏幕飛來飛去……   
this

    Joe了忽略一件事情,並不是全部的子類均可以擁有fly()方法,好比後來新加入的橡皮鴨(RubberDuck)就不能飛,並且橡皮鴨也不會呱呱叫,只能吱吱叫!在超類中加入新的行爲,會使得某些並不合適該行爲的子類也具備該行爲。對代碼所作的局部修改,影響層面可不僅是局部。編碼

利用繼承的特性   
spa

    Joy想,既然在父類加入方法行不通,那麼可使用繼承的特性,在子類中覆蓋fly()方法,讓它什麼都不作,同時覆蓋quack()方法,讓它吱吱叫。.net

    但,若是之後又加入新的鴨子類型,好比誘餌鴨(DecoyDuck),即不會飛也不會叫……還有不少,咱們能夠本身想獲得。這種設計方式有一下幾種缺點:設計

  1. 代碼在多個子類中重複;code

  2. 運行時的行爲不容易改變;

  3. 很難知道全部鴨子的所有行爲;

  4. 改變會牽一髮而動全身,形成其餘鴨子不想要的改變;

利用接口的特性

    Joe認識到繼承可能不是答案,Joe知道規格會經常改變,每當有新的鴨子子類出現,他就要被迫檢查並儘量覆蓋fly()方法和quark()方法……這簡直是無窮的噩夢。

    因此,他須要一個更清晰的方法,讓「某些」(而不是所有)鴨子類型能夠飛或叫。

    他決定把fly()方法從超類中取出來,放進一個Flyable接口中。這麼依賴,只有會飛的鴨子才實現此接口。一樣的方式,也能夠用來設計一個Quackable接口,由於不是全部的鴨子都會叫。

    

    Joe的主管告訴他,這真是一個超笨的主意,這麼一來重複的代碼會變多,若是認爲覆蓋幾個方法就算是差勁,那麼對於48的Duck的子類都要稍微修改一下飛行的行爲,又怎麼說?

若是你的Joe,你要怎麼辦?

    咱們知道,並不是「全部」的子類都具備飛行和呱呱叫的行爲,因此繼承並非適當的解決方式。雖然Flyable與Quackable能夠解決「一部分」問題(不會再有會飛的橡皮鴨),但卻形成了代碼沒法複用,這隻能算是從一個噩夢跳進另外一個噩夢。身子,在會飛的鴨子中,飛行的動做可能還有多種變化……

    無論你在何處工做,構建些什麼,用何種編程語言,在軟件開發上,一致伴隨你的哪一個不變的真理就是需求變動!

    幸運的是,有一個設計原則,剛好適用於此情況。

設計原則

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

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

    也就是說,把會變化的部分取出並封裝起來,以便之後能夠輕易地改動或拓展此部分,而不影響不須要變化的其餘部分。

    好,該是把鴨子的行爲從Duck類中取出來的時候了!

    咱們知道Duck類內的fly()和quack()會隨着鴨子的不一樣而改變。爲了要把這兩個行爲從Duck類中分開,咱們將把它們從Duck類中取出來,創建一組心累來表明每一個行爲。

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

    咱們但願一切都有彈性,還向可以「指定」行爲到鴨子的實例。避讓說,產生一個新的綠頭鴨實例,並指定特定類型的飛行行爲給它。乾脆順便讓鴨子的行爲能夠動態地改變好了。換句話說,應該在鴨子類中包涵設定行爲的方法,這樣就能夠在「運行時」動態地「改變」綠頭鴨的飛行行爲。

    有了這些目標要實現,接着看看第二個設計原則:

設計原則

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

    利用接口表明每一個行爲,設計兩個接口,FlyBehavior與QuackBehavior,而行爲的每一個實現都將實現其中的一個接口。

    這樣的設計,可讓飛行和呱呱叫的動做被其餘的對象複用,由於這些行爲已經與鴨子類無關了。來個天鵝也能夠用。而咱們能夠新增一些行爲,不會影響到既有的行爲類,也不會影響「使用」到飛行行爲的鴨子類。

    下面開始編碼,首先建立鴨子類、飛行行爲和呱呱叫行爲。

    飛行行爲的接口代碼:

package cn.net.bysoft.strategy;

/**
 * 飛行行爲接口。
 * */
public interface FlyBehavior {
    /**
     * 飛行動做。
     * */
    void fly();
}

    飛行行爲的實現類,目前有兩個,分別是使用翅膀飛行和不能飛行:

package cn.net.bysoft.strategy;

/**
 * 飛行行爲的實現類。 該類實現飛行行爲,可使用翅膀飛行。
 * */
public class FlyWithWings implements FlyBehavior {
    /**
     * 可使用翅膀飛行。
     * */
    public void fly() {
        System.out.println("使用翅膀飛行");
    }
    
}

package cn.net.bysoft.strategy;

/**
 * 飛行行爲的實現類。 該類實現飛行行爲,不能飛行。
 * */
public class FlyNoWay implements FlyBehavior {
    /**
     * 不能飛行。
     * */
    public void fly() {
        System.out.println("不能飛行。");
    }
}

    呱呱叫行爲的接口:

package cn.net.bysoft.strategy;

/**
 * 呱呱叫行爲接口。
 * */
public interface QuackBehavior {
    /**
     * 呱呱叫動做。
     * */
    void quack();
}

    呱呱叫行爲的實現類,目前有三個,分別是呱呱叫、吱吱叫和不能叫:

package cn.net.bysoft.strategy;

/**
 * 呱呱叫行爲的實現類。 該類實現呱呱叫行爲,能夠呱呱叫。
 * */
public class Quack implements QuackBehavior {
    /**
     * 呱呱叫。
     * */
    public void quack() {
        System.out.println("呱呱叫。");
    }

}

package cn.net.bysoft.strategy;

/**
 * 呱呱叫行爲的實現類。 該類實現呱呱叫行爲,能夠吱吱叫。
 * */
public class Squeak implements QuackBehavior {
    /**
     * 吱吱叫。
     * */
    public void quack() {
        System.out.println("吱吱叫");
    }
    
}

package cn.net.bysoft.strategy;

/**
 * 呱呱叫行爲的實現類。 該類實現呱呱叫行爲,不能叫。
 * */
public class QuackNoWay implements QuackBehavior {
    /**
     * 不能叫。
     * */
    public void quack() {
        System.out.println("不能叫。");
    }

}

    鴨子類的代碼:

package cn.net.bysoft.strategy;

/**
 * 鴨子超類。
 * */
/**
 * @author Administrator
 * 
 */
public abstract class Duck {
    /**
     * 游泳。
     * */
    public void swim() {
        System.out.println("在水裏游泳。");
    }

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

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

    /**
     * 動態設定飛行行爲。
     * */
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    
    /**
     * 動態設定呱呱叫行爲。
     * */
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

    /**
     * 顯示外觀。
     * */
    public abstract void display();

    // 飛行行爲。
    FlyBehavior flyBehavior;
    // 呱呱叫行爲。
    QuackBehavior quackBehavior;
}

package cn.net.bysoft.strategy;

/**
 * 綠頭鴨。
 * */
public class DuckOfMallard extends Duck {
    
    @Override
    public void display() {
        System.out.println("綠頭鴨。");
    }

}

package cn.net.bysoft.strategy;

/**
 * 玩具鴨。
 * */
public class DuckOfToys extends Duck {

    @Override
    public void display() {
        System.out.println("玩具鴨。");
    }

}

    最後是客戶端調用,動態指令行爲:

package cn.net.bysoft.strategy;

public class Main {

    public static void main(String[] args) {
        // 建立一個玩具鴨。
        Duck toysDuck = new DuckOfToys();
        // 玩具鴨不會飛。
        toysDuck.setFlyBehavior(new FlyNoWay());
        // 玩具鴨不會呱呱叫,可是能夠吱吱叫。
        toysDuck.setQuackBehavior(new Squeak());

        // 玩具鴨開始活動。
        toysDuck.display();
        toysDuck.performFly();
        toysDuck.performQuack();
        toysDuck.swim();

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

        // 建立一個綠頭鴨。
        Duck mallardDuck = new DuckOfMallard();
        // 綠頭鴨能夠飛。
        mallardDuck.setFlyBehavior(new FlyWithWings());
        // 綠頭鴨是真鴨子,能夠呱呱叫。
        mallardDuck.setQuackBehavior(new Quack());

        // 綠頭鴨開始活動。
        mallardDuck.display();
        mallardDuck.performFly();
        mallardDuck.performQuack();
        mallardDuck.swim();

        /**
         * output: 
         * 玩具鴨。 
         * 不能飛行。 
         * 吱吱叫 在水裏游泳。
         * 
         * -------------------------
         * 
         * 綠頭鴨。 
         * 使用翅膀飛行 呱呱叫。
         *  在水裏游泳。
         * */
    }

}

    下面是從新設計後的類結構,所指望的一切都有:鴨子繼承Duck類,飛行行爲和呱呱叫行爲能夠在客戶端調用時指派。

    請特別注意類之間的關係。關係有兩種,能夠是IS-A(是一個)和HAS-A(有一個)或IMPLEMENTS(實現)。「有一個」可能比「是一個」更好。

    這是一個很重要的技巧。也是第三個設計原則:

設計原則

多用組合,少用繼承。

    以上就是策略模式的內容。

相關文章
相關標籤/搜索