設計模式--策略模式的思考


策略模式是一種簡單的設計模式,但是其在業務開發中是一種非常有用的設計模式.舉個例子,當你的業務需要針對不同的場景(可以簡單理解爲枚舉類),執行不同的策略時那麼使用策略模式可以幫助你更好的寫出低耦合與高可擴展的代碼.


標準策略模式

策略模式: 把具體的算法從業務邏輯中分離出來,使得業務類不必膨脹,並且業務與具體算法分離,便於擴展新算法.類圖如下:

使用策略模式往往策略上有着相似的輸入參數以及輸出結果,或者有一個公共的上下文,便於抽象出策略接口Strategy,然後對應的業務Service只需要引用StrategyContext填充具體的策略完成自己的需求.

new StrategyContext(new CouponStrategy()).sendPrize(uid, prize)

這是標準的策略模式,這種模式在如今的IOC下應用場景並不是很多,該模式有不少缺點

  1. 客戶端必須知道所有的策略,然後手動選擇具體的策略放入到Context中執行.
  2. 仍舊無法避免if/else邏輯,針對大多數場景下都是根據條件分支判斷來選擇具體的策略,那麼在客戶端耦合具體策略的情況下這個是沒法避免的

策略枚舉模式

Java的枚舉類可以實現接口,而且枚舉常量天然的可以與具體行爲綁定,那麼兩者結合起來就是一種很棒的策略枚舉模式(筆者自己起的名字).

public enum StrategyEnum implements Strategy{
  COUPON(1) {
    @Override
    public boolean sendPrize(Long uid, String prize) {
      //發放優惠券
      return true;
    }
  },
  RMB(2) {
    @Override
    public boolean sendPrize(Long uid, String prize) {
      //發放RMB
      return true;
    }
  }
  ;
  private int code;
  StrategyEnum(int code) {
    this.code = code;
  }
}

相比標準的模式,該模式省掉了Context類,而且符合大多數場景,比如用戶獲得禮品,該禮品對應的是Mysql的一條記錄,該記錄有type標識是優惠券(Coupon)還是RMB,當DAO從DB查詢出來後根據typeCode,定位到具體的枚舉類,然後執行其sendPrize(Long uid, String prize)完成邏輯.這個流程很清晰. 基於枚舉的策略模式也有一些問題:

  1. 枚舉類無法外部實例化,因此無法被IOC管理,往往策略實現都是複雜的依賴衆多其他服務,那麼這種時候枚舉類就無從下手

IOC配合下的策略模式

實踐中,客戶端往往不關心具體的實現類是如何實現的,他只需要知道有這個實現類的存在,其能幫我完成任務,得到我要的結果,所以在標準的策略模式基礎上,擴展Context類,讓其擔任選擇策略的能力,而不是客戶端手動選擇具體的策略,也就是具體策略實現與客戶端解耦,轉用枚舉常量來代表其所希望的策略.改進後的Context(依賴Spring IOC)如下:

@Component
public class StrategyContext implements InitializingBean {

  @Resource
  private Strategy couponStrategy;

  @Resource
  private Strategy RMBStrategy;
  /**
   * 保存策略與具體實現的Map
   */
  private static Map<StrategyEnum, Strategy> strategyMap = new HashMap<>(2);

  public Strategy getStrategy(StrategyEnum strategyEnum) {
    return strategyMap.get(strategyEnum);
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    strategyMap.put(StrategyEnum.COUPON, couponStrategy);
    strategyMap.put(StrategyEnum.RMB, RMBStrategy);
  }
}

客戶端調用時使用

strategyContext.getStrategy(StrategyEnum.COUPON).sendPrize(uid,prize)

這裏的Context相當於中間層,提供的是外觀模式的功能,當新增策略時只需要新增對應的枚舉類,以及具體的實現策略,在Context中添加一個新的枚舉與實現類關係.客戶端代碼基本不要任何改變. 補充: 更加優雅的做法是利用Spring的事件機制,在Spring初始化完畢後再構建整個策略Map,可以參考我在觀察者模式中所使用到的方法. 設計模式–觀察者模式的思考

策略模式的本質

策略模式的本質是把複雜的算法從一個類中提取出來,用一種合理的方式管理起來,避免業務類的膨脹. 對於擴展只需要新增策略,而不需要怎麼動業務代碼.對於修改也只需要修改具體的策略類.業務類與策略成功的實現了低耦合. 與IOC的配合下可以更加徹底的與業務類解耦,其間只需要枚舉類與策略接口進行聯繫,對於代碼的擴展性更加有力.

與狀態模式的關係

狀態設計模式的類圖結構與策略模式幾乎是一致的.從邏輯上狀態是平行的無法互相替換,但是策略與策略之間是可以完全替換的,只是實現方式的不同.在選擇設計模式的時候是根據這一點來區分,代碼上的體現是對於狀態設計模式以State結尾,對於策略設計模式以Strategy結尾,讓開發人員第一眼看過去就能明白整個設計的思路最佳.

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