設計模式之策略模式

定義:

       策略模式定義了一系列算法,並將每一個算法封裝起來,而且使他們可以相互替換,讓算法獨立於使用它的客戶而獨立變化。

結構:

      策略接口角色(Strategy):定義所有支持的算法的公共接口

      具體策略實現角色(ConcreteStrategy):具體的策略實現

      策略上下文角色(StrategyContext):策略上下文負責和具體的策略實現交互,通常策略上下文對象會持有一個真正的策略實現對象,策略上下文還可以讓具體的策略實現從其中獲取相關數據,回調策略上下文對象的方法。

實際場景:

        假如,現在有這樣一個需求,做一個簡單的商場收銀功能,普通用戶不打折,新用戶打五折,VIP用戶打六折,給出一個Java的 demo案例:

public class QuoteManager {

    public BigDecimal quote(BigDecimal originalPrice,String customType){
        if ("新客戶".equals(customType)) {
            System.out.println("抱歉!新客戶沒有折扣!");
            return originalPrice;
        }else if ("老客戶".equals(customType)) {
            System.out.println("恭喜你!老客戶打9折!");
            originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
            return originalPrice;
        }else if("VIP客戶".equals(customType)){
            System.out.println("恭喜你!VIP客戶打8折!");
            originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
            return originalPrice;
        }
        //其他人員都是原價
        return originalPrice;
    }

}

上面的代碼非常好,簡單直接,但有個問題:所有的算法法都在一個方法裏,假如算法複雜點,就會很龐大,(當然現在還看不出來)

改進一:

         剛纔說算法都寫在quote方法裏,非常大,那就提取出來,每個算法定義一個方法,然後去調用這個方法。eg:

 public BigDecimal quote(BigDecimal originalPrice, String customType){
         if ("新客戶".equals(customType)) {
             return this.quoteNewCustomer(originalPrice);
         }else if ("老客戶".equals(customType)) {
             return this.quoteOldCustomer(originalPrice);
         }else if("VIP客戶".equals(customType)){
             return this.quoteVIPCustomer(originalPrice);
         }
         //其他人員都是原價
         return originalPrice;
     }
 
     /**
      * 對VIP客戶的報價算法
      * @param originalPrice 原價
      * @return 折後價
      */
     private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {
         System.out.println("恭喜!VIP客戶打8折");
         originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
         return originalPrice;
     }
 
     /**
      * 對老客戶的報價算法
      * @param originalPrice 原價
      * @return 折後價
      */
     private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {
         System.out.println("恭喜!老客戶打9折");
         originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
         return originalPrice;
     }
 
     /**
      * 對新客戶的報價算法
      * @param originalPrice 原價
      * @return 折後價
      */
     private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {
         System.out.println("抱歉!新客戶沒有折扣!");
         return originalPrice;
     }
 
 }

這種方式,在我們平常寫代碼 的時候經常會用到,整體代碼來看,也相對較整潔。但對於這樣一個功能來講,可擴展性不高。假如現在需求要求我們新增加一種客戶優惠類型,首先,需要加一個算法,然後,再去quote方法里加一個判斷的分支(if else )這樣就很麻煩,也違反設計模式的開閉原則。

開閉原則:

         對於拓展是開放的:即模塊的行爲可擴展,當需求改變,可以對模塊進行擴展

         對於修改是封閉的:即對模塊擴展時,不必改動模塊的源代碼

策略模式實現:(通用模板)

策略接口:

//策略接口
4 public interface IStrategy {
5     //定義的抽象算法方法 來約束具體的算法實現方法
6     public void algorithmMethod();
7 }

具體策略實現:有幾個策略,就寫幾個實現策略接口的實現類

// 具體的策略實現
 4 public class ConcreteStrategy implements IStrategy {
 5     //具體的算法實現
 6     @Override
 7     public void algorithmMethod() {
 8         System.out.println("this is ConcreteStrategy method...");
 9     }
10 }
 // 具體的策略實現2
 4 public class ConcreteStrategy2 implements IStrategy {
 5      //具體的算法實現
 6     @Override
 7     public void algorithmMethod() {
 8         System.out.println("this is ConcreteStrategy2 method...");
 9     }
10 }

策略上下文:

/**
 4  * 策略上下文
 5  */
 6 public class StrategyContext {
 7     //持有一個策略實現的引用
 8     private IStrategy strategy;
 9     //使用構造器注入具體的策略類
10     public StrategyContext(IStrategy strategy) {
11         this.strategy = strategy;
12     }
13 
14     public void contextMethod(){
15         //調用策略實現的方法
16         strategy.algorithmMethod();
17     }
18 }

外部調用:

public static void main(String[] args) {
         //1.創建具體測策略實現
         IStrategy strategy = new ConcreteStrategy2();
         //2.在創建策略上下文的同時,將具體的策略實現對象注入到策略上下文當中
         StrategyContext ctx = new StrategyContext(strategy);
         //3.調用上下文對象的方法來完成對具體策略實現的回調
         ctx.contextMethod();
     }

此時假如增加一個客戶類型,那就是新加一個策略的實現類即可,然後外部直接調用。

策略模式的作用:把具體的算法從實際的業務邏輯中分離出來,成爲獨立類,使得相互之間能夠相互替換。

策略模式在JDK中的實現:

         多線程中的線程池,對於多線程,是用工廠模式來創建一個線程池Executor,而Executor的一個實現類ThreadPoolExecutor ,這個類中的 RejectedExecutionHandler 其實就是一個策略接口,用在當線程池中沒有多餘的線程來執行任務,並且保存任務的多列也滿了(指的是有界隊列),對仍在提交給線程池的任務的處理策略。

 

策略模式的本質是:分離算法,選擇實現。

 

 

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