23種設計模式(二)---策略設計模式

盛開的太陽發表於2021-06-21

策略設計模式---透徹講解

一. 什麼是策略設計模式

設計模式有三種:建立型, 行為型, 結構型. 策略設計模式屬於行為型. 為什麼屬於行為型呢? 來看看下面解釋:

1.1 什麼是策略呢?

什麼是策略呢?
舉個例子: 出行方式: 可以騎自行車, 摩托車, 開小汽車, 坐公交車, 坐火車, 輪船, 飛機等等. 這些出行方式都是出行的策略.
再來看商場搞促銷: 打8折, 打7折, 滿100減30, 購物滿500返現50等等, 無論何種打折方式, 其根本都是演算法, 這些演算法就是一種策略. 策略之間是可以隨機互換的. 比如同一件商場,今天可以打8折, 明天可以滿100件30.

策略設計模式: 定義【一組】演算法, 將【每個】演算法進行包裝, 並且他們之間可以隨意【互換】, 來看一下UML圖:

23種設計模式(二)---策略設計模式

從上圖可以看出, 定義一個策略設計模式需要4大步驟

  1. 策略介面類: 是對策略, 演算法的抽象. 定義了每個策略和演算法必須有的演算法和屬性.
  2. 策略實現類: 策略,演算法的具體實現. 策略具體有幾種型別的實現就定義幾個策略類,並實現策略方法
  3. Context上下文類: Context上下文, 起到承上啟下的作用. 遮蔽了上層模組對策略,演算法的訪問, 封裝了可能存在的變化.
    到底當前要呼叫那個策略, 通過定義建構函式傳參決定, 策略方法可以沒有入參, 但必須有構造方法, 構造方法決定類的實際策略. 可擴充套件性強, 增加新的策略, 不需要動用老程式碼.
  4. 客戶端類: 客戶端呼叫Context上下文類, 並指定要呼叫策略的類. 所以, 就要求使用者提前知道有哪些策略類.

以上就是策略設計模式實現的4步驟

二. 策略設計模式程式碼實現

我們就以商場促銷為例, 使用策略設計模式來實現.
現在商場要促銷, 商品促銷方式有: 原價, 折扣(8折, 7折), 滿減(滿100減30, 滿100減20)等. 具體的促銷方式就是策略.
根據實現的4個步驟, 一步一步來實現

2.1 第一步: 定義商場促銷的介面類

package com.lxl.www.designPatterns.strategy;

/**
 * 促銷類
 */
public interface IPromotionStrategy {

    /**
     * 促銷
     *
     * 入參是原價, 出參是促銷價格
     * @return
     */
    Double promotion(PromotionVo promotionVo) throws Exception;
}

這裡定義了一個促銷策略的介面類. 並定義了促銷方案.

2.2 第二步: 促銷方案的實現類

促銷方案一共有3種: 第一種:原價, 第二種: 打折 第三種: 滿減
先來看看第一種: 原價

package com.lxl.www.designPatterns.strategy;

/**
 * 正常售賣價格
 */
public class CommonPromotionStrategy implements IPromotionStrategy {

    @Override
    public Double promotion(PromotionVo promotionVo) {
        return promotionVo.getOriginPrice();
    }
}

原價也就是正常售賣的價格. 所以, 沒有任何計算邏輯

第二種, 打折

package com.lxl.www.designPatterns.strategy;

/**
 * 打折
 */
public class DiscountPromotionStrategy implements IPromotionStrategy {

    @Override
    public Double promotion(PromotionVo promotionVo) throws Exception {
        if (promotionVo.getDiscount() < 0 || promotionVo.getDiscount() >=1 ){
            throw new Exception("請輸入正確的折扣");
        }

        return promotionVo.getOriginPrice() * promotionVo.getDiscount();
    }
}

打折有折扣, 那麼折扣必須是0-1範圍, 並且最後返回折後價

第三種. 滿減

package com.lxl.www.designPatterns.strategy;

/**
* 滿減折扣
*/
public class FullReductionPromotionStrategy implements IPromotionStrategy {
   /**
    * 滿減
    * @return
    * @throws Exception
    */
   @Override
   public Double promotion(PromotionVo promotionVo) throws Exception {
       if (promotionVo.getReduction() == null || promotionVo.getFull() == null) {
           throw new Exception("請檢查滿減引數");
       }
       Double price = promotionVo.getOriginPrice();
       Double canPromotionPrice=promotionVo.getOriginPrice();
       while(canPromotionPrice >= promotionVo.getFull()) {
           price = price - promotionVo.getReduction();
           canPromotionPrice -= promotionVo.getFull();
       }
       return price;
   }
}

這裡實現 滿減邏輯是每滿100減30, 要是300就減90.

2.3 第三步: context商場促銷類

我們商場裡的商品到底是什麼樣的優惠方案呢? 有商場促銷類決定

package com.lxl.www.designPatterns.strategy;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * 商場促銷
 */
public class MallPromotion {
    @Autowired
    private IPromotionStrategy promotion;

    public MallPromotion(IPromotionStrategy promotion) {
        this.promotion = promotion;
    }

    public Double activityPromotion(PromotionVo promotionVo) throws Exception {
        return this.promotion.promotion(promotionVo);
    }
}

促銷類定義了一個建構函式, 用來指定當前的促銷方案.

2.4 具體商品的實際促銷方案

    public static void main(String[] args) throws Exception {
        PromotionVo promotionVo = new PromotionVo(300.0, 0.8, 100.0, 30.0);

        System.out.println("<br><br>==========正常售賣=========");
        MallPromotion mallPromotionContext1 = new MallPromotion(new CommonPromotionStrategy());
        System.out.println(mallPromotionContext1.activityPromotion(promotionVo));


        System.out.println("<br><br>==========8折=========");
        MallPromotion mallPromotionContext2 = new MallPromotion(new DiscountPromotionStrategy());
        System.out.println(mallPromotionContext2.activityPromotion(promotionVo));


        System.out.println("<br><br>==========滿100減30=========");
        MallPromotion mallPromotionContext3 = new MallPromotion(new FullReductionPromotionStrategy());
        System.out.println(mallPromotionContext3.activityPromotion(promotionVo));


    }
}

我們想要什麼樣 促銷方案都是可以的. 只需要呼叫商場促銷類並傳入促銷策略即可.

三. 策略設計模式的應用

3.1 什麼時候使用?

一個系統有許多類,而他們之間的區別是: 行為的不同. 這時候可以使用策略設計模式.

3.2 優點

  1. 策略模式提供了對 “開閉原則” 的完美支援,使用者可以在不修改原有系統的基礎上選擇演算法或行為,也可以靈活地增加新的演算法或行為。
  2. 演算法可以互相切換, 無需知道演算法的具體實現
  3. 使用策略模式可以避免多重條件選擇語句。多重條件選擇語句是硬編碼,不易維護。
  4. 可擴充套件性更好, 可以靈活地增加新的演算法或行為。也可靈活切換演算法或者行為

3.3 缺點

  1. 使用策略設計模式會產生很多具體策略類, 客戶端每次只能使用一個策略類,不支援使用一個策略類完成部分功能後再使用另一個策略類來完成剩餘功能的情況。
  2. 所有的策略類需要對外暴露. 因為客戶端只有知道有哪些策略, 才知道應該是用哪個.這就意味著客戶端必須理解這些演算法的區別,以便適時選擇恰當的演算法。換言之,策略模式只適用於客戶端知道所有的演算法或行為的情況。

4. 使用場景

1、一個系統, 需要動態的在幾個演算法之間選擇, 它們之間的區別僅僅是演算法或者行為的不同,那麼可以使用策略模式, 這樣我們可以動態地讓一個物件在許多行為中選擇一種行為。
2、一個系統需要動態地在幾種演算法中選擇一種。
3、一個物件有很多的行為,如果不用策略設計模式,這些行為就只好使用多重條件選擇語句來實現。而使用策略模式,把這些行為轉移到相應的具體策略類裡面,就可以避免使用難以維護的多重條件選擇語句。

5. 注意事項

如果一個系統的策略多於四個,就需要考慮使用混合模式,解決策略類膨脹的問題。

四. 策略設計模式結合工廠設計模式的應用

上面的商場促銷活動. 最終策略都要暴露給客戶端, 這對客戶端來說不是特別友好. 我們可以結合簡單工廠設計模式, 將策略進行封裝.

package com.lxl.www.designPatterns.strategy;

import org.springframework.beans.factory.annotation.Autowired;

/**
 * 商場促銷
 */
public class MallPromotionContext {
    @Autowired
    private IPromotionStrategy promotion;

    public MallPromotionContext(IPromotionStrategy promotion) {
        this.promotion = promotion;
    }

    public MallPromotionContext(Integer type) {
        switch (type) {
            case 1:
                this.promotion = new CommonPromotionStrategy();
                break;
            case 2:
                this.promotion = new DiscountPromotionStrategy();
                break;
            case 3:
                this.promotion = new FullReductionPromotionStrategy();
                break;
        }
    }

    public Double activityPromotion(PromotionVo promotionVo) throws Exception {
        return this.promotion.promotion(promotionVo);
    }
   
}

這樣封裝以後的好處就是, 客戶端只需要知道自己要什麼型別的促銷策略, 而不用關注具體促銷策略型別.
下面來看一下策略程式碼的實現


    public static void main(String[] args) throws Exception {
        PromotionVo promotionVo = new PromotionVo(300.0, 0.8, 100.0, 30.0);

        System.out.println("<br><br>==========正常售賣=========");
        MallPromotionContext mallPromotionContext1 = new MallPromotionContext(1);
        System.out.println(mallPromotionContext1.activityPromotion(promotionVo));


        System.out.println("<br><br>==========8折=========");
        MallPromotionContext mallPromotionContext2 = new MallPromotionContext(2);
        System.out.println(mallPromotionContext2.activityPromotion(promotionVo));


        System.out.println("<br><br>==========滿100減30=========");
        MallPromotionContext mallPromotionContext3 = new MallPromotionContext(3);
        System.out.println(mallPromotionContext3.activityPromotion(promotionVo));

    }

在基本的策略設計模式中, 選擇所用具體實現的職責有客戶端物件承擔, 並轉給策略模式的Context物件, 並沒有減輕客戶端需要選擇判斷的壓力.而策略設計模式與簡單工廠設計模式的結合, 選擇具體實現的則在有也由Context來承擔, 這就大大的減輕了客戶端的職責.

相關文章