Java設計模式--策略模式


  《Head First設計模式》看了一部分才對設計模式有了初步的了解:它其實是開發過程中很多前人的經驗與智慧的總結,幫助你在開發時采取更好的方式去設計各個類、方法、以及它們之間的調用、實現方式,讓代碼保持靈活性的同時又能更好地復用。基於學過一塊知識一定要用文字記錄、總結、鞏固,而不是走馬觀花的原則,趁最近終於有空,特將前一段時間看的關於“策略模式”的內容總結於此。

        

一、場景描述

  A公司要做一套模擬鴨子的游戲,游戲中會出現各種鴨子,一邊游泳戲水,一邊呱呱叫,還有一些會飛。

方案1 繼承

  設計一個超類Duck,包含方法quack()、swim()、fly() 分別模擬鴨子的叫、游泳、飛行等行為,再包含一個抽象類 display() ,用於展示各個鴨子不同的外觀,讓每個鴨子子類繼承父類時實現display();

弊端:

  這樣做雖然每個鴨子子類繼承父類時就同時擁有了父類的方法,可以達到代碼復用的目的,但是這樣會使某些並不適合該行為的子類也具有該行為。如果某些子類鴨子,如“橡皮鴨”,它不具備某些功能(如飛行),它就不應該擁有這個飛行的功能。當然,你可以在子類中通過 @Override 覆蓋這個方法。但是當各個不同的子類都需要通過覆蓋修改不同的方法時,就會非常繁瑣,而且容易出現紕漏,且這些新覆蓋的方法不能被復用。如“橡皮鴨”只會叫不會飛,“木頭鴨”不會叫也不會飛。以后每當有新的鴨子子類出現,你都要去檢查並可能需要覆蓋這些方法,想想都讓人抓狂。

方案2 接口

在超類 Duck 中將quack()、fly() 等可變的方法用接口 QuackBehavior 接口中 Quackable(),FlyBehavior 接口中 Flyable() 來代替,然后在每個鴨子子類中,如果具有“飛行”或“叫”這個功能就實現“飛行“或”叫“這個接口。

弊端:

代碼無法復用,如果有100個子類,都具有飛行的行為,你就需要重復100次代碼。

二、設計模式來幫忙

設計原則一:找出程序中可能需要變化的地方和不需要變化的地方,將它們獨立開來。讓系統中的某部分改變不會影響其他部分;

  由於 fly() 和 quack() 會隨着鴨子的不同而改變,所以把這兩個行為從 Duck 類中分開,建一組新類來代表各個行為。

設計原則二:針對接口編程,而不是針對實現;

  利用多態,針對超類型編程,執行時根據實際對象執行到真正的行為,不會被綁死在超類型的行為上。以前的做法是:行為來自超類的具體實現或是繼承某個接口並由子類自行實現。這兩種方法都捆綁於”實現“,無法方便地更改行為。現在我們利用接口代表每個行為,比如FlyBehavior,QuackBehavior,然后讓各個行為類實現這些接口,然后在 Duck 類中只要定義這個接口的實例變量即可,這樣在各個鴨子子類中如果想擁有某種特定的行為,只要用這個接口實例變量去引用具體的行為類即可。

設計原則三:多用組合,少用繼承;

  飛行和叫這兩種不同的行為,我們分別為其建立兩組不同的行為類,然后在 Duck 類中通過接口實例變量結合起來,這就是”組合“。使得系統具有很大的彈性,還可以”在運行時動態地改變行為“。

三、代碼如下

(1)目錄結構

        

 

(2)接口 FlyBehavior 和 QuackBehavior

public interface FlyBehavior {

    void fly();

}

public interface QuackBehavior {

    void quack();

}

(3)FlyBehavior 實現類【QuackBehavior 實現類和 FlyBehavior 類似】

public class FlyNotWay implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("I can not fly");
    }
}

public class FlyWithSwings implements FlyBehavior {
    
    @Override
    public void fly() {
        System.out.println("I can fly");
    }
    
}

public class FlyRocketPowered implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("I can fly with rocket");
    }
}

(4)鴨子外觀實現類 MallardDuck 野鴨子和 ModelDuck 模型鴨子

public class MallardDuck extends Duck {

    public MallardDuck() {
        flyBehavior = new FlyWithSwings();
        quackBehavior = new QuackWithGaGa();
    }

    @Override
    public void display() {
        System.out.println("I am a MallardDuck");
    }
}

public class ModelDuck extends Duck {

    public ModelDuck() {
        flyBehavior = new FlyNotWay();
        quackBehavior = new QuackNotWay();
    }

    @Override
    public void display() {
        System.out.println("I am a ModelDuck");
    }
}

(5)Duck 抽象類

public abstract class Duck {

    protected FlyBehavior flyBehavior;

    protected QuackBehavior quackBehavior;

    /**
     * 鴨子外觀方法
     */
    public abstract void display();

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

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

    public void setFlyBehavior(FlyBehavior fb) {
        this.flyBehavior = fb;
    }

    public void setQuackBehavior(QuackBehavior qb) {
        this.quackBehavior = qb;
    }
}

(6)執行結果

I am a MallardDuck
I can fly
I can quack

I am a ModelDuck
I can not fly
I can not quack
I can fly with rocket

四、總結

  策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。這是書里給出的策略模式的定義,依我個人的理解,這種模式的關鍵就是要將系統中的可變行為抽象出來,單獨進行封裝,用一組行為類(算法族)來實現該特定的接口,這樣任何類(如 Duck)如果想擁有這些算法族中的某個算法,都可以通過定義接口實例變量而擁有該整個算法族,在子類中再對該變量進行賦值。

      像這個例子里,通過定義了FlyBehavior,這樣以后任何想有飛行行為的類如飛機,都可以調用這個接口及實現它的各個飛行類。且飛行行為的變化可以通過增加新的飛行類來實現,不會對其他部分(如quack()、display() 等行為,亦或是已擁有某些特定飛行行為的對象)造成任何影響,即”算法的變化獨立於使用算法的客戶“。而且各個飛行行為之間也可以互相替換;即 setFlyBehavior(FlyBehavior fb)。

      可見,設計模式的應用讓整個項目的代碼擁有了極大的靈活性,且達到了代碼復用的效果。設計模式其實是一種設計上的思維方式,是前人的智慧和經驗的總結,其真正的精髓不是看過就能學會的,還是需要在實際應用中不斷地實踐摸索,慢慢體會。

 策略模式和橋接模式的區別

(1)策略模式和橋接模式經過學習發現,都是將一個事物的抽象和它的實現分離開來,使其兩者具有一定的獨立擴展的可能性,但是兩者有什么區別呢?

(2)橋接模式強調接口僅提供基本操作,而這些基本操作定義更高層次的操作,內部可以獨立擴展,可不屬於一個整體;策略模式強調抽象接口提供的是一種算法,Context簡單調用這些算法完成操作;

  參考橋接模式:https://www.cnblogs.com/blogtech/p/12874663.html

  策略模式深層了解:https://www.cnblogs.com/kubixuesheng/p/5155644.html

(3)橋接模式通過繼承、聚合的方式組合類和對象形成更大的結構;而策略模式則通過接口之間的協作完成不同功能的組合,是一種行為模式(重點理解)

(4)橋接模式要表達的內容比較多,結構也比較復雜,其實是接口隔離的原則,本質上把布局類的兩種體系區別開來,使得他們可以松散的組合,而策略模式在解耦只是某一個算法級別的層次。客觀上講:策略模式只是橋接模式的 一部分;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM