設計模式(十二)——策略模式

設計模式(十二)——策略模式

新專題:設計模式,我會在博客(http://www.hollischuang.com)及微信公衆號(hollischuang)同步更新,歡迎共同窗習算法

前幾篇文章主要介紹了幾種建立型模式,本文開始介紹行爲型模式。首先介紹一個比較簡單的設計模式——策略模式。編程

概念


學習過設計模式的人大概都知道Head First設計模式這本書,這本書中介紹的第一個模式就是策略模式。把策略模式放在第一個,筆者認爲主要有兩個緣由:一、這的確是一個比較簡單的模式。二、這個模式能夠充分的體現面向對象設計原則中的封裝變化、多用組合,少用繼承、針對接口編程,不針對實現編程等原則。設計模式

策略模式(Strategy Pattern):定義一系列算法,將每個算法封裝起來,並讓它們能夠相互替換。策略模式讓算法獨立於使用它的客戶而變化,也稱爲政策模式(Policy)。微信

用途


結合策略模式的概念,咱們找一個實際的場景來理解一下。ide

假設咱們是一家新開的書店,爲了招攬顧客,咱們推出會員服務,咱們把店裏的會員分爲三種,分別是初級會員、中級會員和高級會員。針對不一樣級別的會員咱們給予不一樣的優惠。初級會員買書咱們不打折、中級會員買書咱們打九折、高級會員買書咱們打八折。
設計模式(十二)——策略模式學習

咱們但願用戶在付款的時候,只要刷一下書的條形碼,會員再刷一下他的會員卡,收銀臺的工組人員就能直接知道應該向顧客收取多少錢。測試

在不使用模式的狀況下,咱們能夠在結算的方法中使用if/else語句來區別出不一樣的會員來計算價格。this

可是,若是咱們有一天想要把初級會員的折扣改爲9.8折怎麼辦?有一天我要推出超級會員怎麼辦?有一天我要針對中級會員可打折的書的數量作限制怎麼辦?設計

使用if\else設計出來的系統,全部的算法都寫在了一塊兒,只要有改動我就要修改整個類。咱們都知道,只要是修改代碼就有可能引入問題。爲了不這個問題,咱們可使用策略模式。。。code

對於收銀臺系統,計算應收款的時候,一個客戶只多是初級、中級、高級會員中的一種。不一樣的會員使用不一樣的算法來計算價格。收銀臺系統其實不關心具體的會員類型和折扣之間的關係。也不但願會員和折扣之間的任何改動會影響到收銀臺系統。

在介紹策略模式的具體實現方式以前,再來鞏固一下幾個面向對象設計原則:封裝變化、多用組合,少用繼承、針對接口編程,不針對實現編程。想想如何運用到策略模式中,而且有什麼好處。

實現方式


策略模式包含以下角色:

Context: 環境類

Strategy: 抽象策略類

ConcreteStrategy: 具體策略類

設計模式(十二)——策略模式
咱們運用策略模式來實現一下書店的收銀臺系統。咱們能夠把會員抽象成一個策略類,不一樣的會員類型是具體的策略類。不一樣策略類裏面實現了計算價格這一算法。而後經過組合的方式把會員集成到收銀臺中。

先定義一個接口,這個接口就是抽象策略類,該接口定義了計算價格方法,具體實現方式由具體的策略類來定義。

/**
 * Created by hollis on 16/9/19. 會員接口
 */
public interface Member {

    /**
     * 計算應付價格
     * @param bookPrice 書籍原價(針對金額,建議使用BigDecimal,double會損失精度)
     * @return 應付金額
     */
    public double calPrice(double bookPrice);
}

針對不一樣的會員,定義三種具體的策略類,每一個類中都分別實現計算價格方法。

/**
 * Created by hollis on 16/9/19. 初級會員
 */
public class PrimaryMember implements Member {

    @Override
    public double calPrice(double bookPrice) {
        System.out.println("對於初級會員的沒有折扣");
        return bookPrice;
    }
}

/**
 * Created by hollis on 16/9/19. 中級會員,買書打九折
 */
public class IntermediateMember implements Member {

    @Override
    public double calPrice(double bookPrice) {
        System.out.println("對於中級會員的折扣爲10%");
        return bookPrice * 0.9;
    }
}

/**
 * Created by hollis on 16/9/19. 高級會員,買書打八折
 */
public class AdvancedMember implements Member {

    @Override
    public double calPrice(double bookPrice) {
        System.out.println("對於中級會員的折扣爲20%");
        return bookPrice * 0.8;
    }
}

上面幾個類的定義體現了封裝變化的設計原則,不一樣會員的具體折扣方式改變不會影響到其餘的會員。

定義好了抽象策略類和具體策略類以後,咱們再來定義環境類,所謂環境類,就是集成算法的類。這個例子中就是收銀臺系統。採用組合的方式把會員集成進來。

/**
 * Created by hollis on 16/9/19. 書籍價格類
 */
public class Cashier {

    /**
     * 會員,策略對象
     */
    private Member member;

    public Cashier(Member member){
        this.member = member;
    }

    /**
     * 計算應付價格
     * @param booksPrice
     * @return
     */
    public double quote(double booksPrice) {
        return this.member.calPrice(booksPrice);
    }
}

這個Cashier類就是一個環境類,該類的定義體現了多用組合,少用繼承、針對接口編程,不針對實現編程兩個設計原則。因爲這裏採用了組合+接口的方式,後面咱們在推出超級會員的時候無須修改Cashier類。只要再定義一個SuperMember implements Member 就能夠了。

下面定義一個客戶端來測試一下:

/**
 * Created by hollis on 16/9/19.
 */
public class BookStore {

    public static void main(String[] args) {

        //選擇並建立須要使用的策略對象
        Member strategy = new AdvancedMember();
        //建立環境
        Cashier cashier = new Cashier(strategy);
        //計算價格
        double quote = cashier.quote(300);
        System.out.println("高級會員圖書的最終價格爲:" + quote);

        strategy = new IntermediateMember();
        cashier = new Cashier(strategy);
        quote = cashier.quote(300);
        System.out.println("中級會員圖書的最終價格爲:" + quote);
    }
}

//對於中級會員的折扣爲20%
//高級會員圖書的最終價格爲:240.0
//對於中級會員的折扣爲10%
//中級會員圖書的最終價格爲:270.0

從上面的示例能夠看出,策略模式僅僅封裝算法,提供新的算法插入到已有系統中,策略模式並不決定在什麼時候使用何種算法。在什麼狀況下使用什麼算法是由客戶端決定的。

  • 策略模式的重心

  • 策略模式的重心不是如何實現算法,而是如何組織、調用這些算法,從而讓程序結構更靈活,具備更好的維護性和擴展性。
  • 算法的平等性

  • 策略模式一個很大的特色就是各個策略算法的平等性。對於一系列具體的策略算法,你們的地位是徹底同樣的,正由於這個平等性,才能實現算法之間能夠相互替換。全部的策略算法在實現上也是相互獨立的,相互之間是沒有依賴的。

  • 因此能夠這樣描述這一系列策略算法:策略算法是相同行爲的不一樣實現。

  • 運行時策略的惟一性

  • 運行期間,策略模式在每個時刻只能使用一個具體的策略實現對象,雖然能夠動態地在不一樣的策略實現中切換,可是同時只能使用一個。
  • 公有的行爲

  • 常常見到的是,全部的具體策略類都有一些公有的行爲。這時候,就應當把這些公有的行爲放到共同的抽象策略角色Strategy類裏面。固然這時候抽象策略角色必需要用Java抽象類實現,而不能使用接口。(《JAVA與模式》之策略模式)

    策略模式的優缺點


優勢

  • 策略模式提供了對「開閉原則」的完美支持,用戶能夠在不修改原有系統的基礎上選擇算法或行爲,也能夠靈活地增長新的算法或行爲。
  • 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族。恰當使用繼承能夠把公共的代碼移到父類裏面,從而避免代碼重複。
  • 使用策略模式能夠避免使用多重條件(if-else)語句。多重條件語句不易維護,它把採起哪種算法或採起哪種行爲的邏輯與算法或行爲的邏輯混合在一塊兒,通通列在一個多重條件語句裏面,比使用繼承的辦法還要原始和落後。

    缺點

  • 客戶端必須知道全部的策略類,並自行決定使用哪個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。
  • 因爲策略模式把每一個具體的策略實現都單獨封裝成爲類,若是備選的策略不少的話,那麼對象的數目就會很可觀。能夠經過使用享元模式在必定程度上減小對象的數量。

    參考資料

    《JAVA與模式》之策略模式

相關文章
相關標籤/搜索