設計模式 -- 策略模式 (Strategy Pattern)


定義:定義一組算法,將每個算法都封裝起來,并且使它們之間可以互換

策略模式通用類圖:

策略模式.png

Context 封裝角色:也叫上下文角色,起承上啟下封裝作用,屏蔽高層模塊對策略,算法的直接訪問,封裝可能存在的變化。
Strategy 抽象策略角色:抽象算法集合的接口,定義算法必須有的屬性與方法。
ConcreteStrategy 具體策略角色:實現抽象策略中的操作,該類含有具體的算法。

通用代碼演示:
抽象策略類:

public interface Strategy {
   //策略模式的運算法則
   public void doSomething();
}

具體策略類:

public class ConcreteStrategy1 implements Strategy {
   @Override
   public void doSomething() {
      System.out.println("這是第一種算法");
   }
}

public class ConcreteStrategy2 implements Strategy {
   @Override
   public void doSomething() {
      System.out.println("這是第二種算法");
   }
}

策略模式的重點在于封裝角色,它借鑒了代理模式的思路,但跟代理模式有區別:策略模式的封裝角色和被封裝的策略類不用是同一個接口,如果是同一個接口那就成為了代理模式。
封裝類:

public class Context {
   //引入策略
   private Strategy strategy = null;
   //通過構造函數指定策略
   public Context(Strategy strategy) {
      this.strategy = strategy;
   }
   //調用策略的算法
   public void doAnything(){
      this.strategy.doSomething();
   }
}

高層模塊:

public class Client {
   public static void main(String[] args) {
      //高層模塊聲明哪種策略
      Strategy ConcreteStrategy1 = new ConcreteStrategy1();
      Context context1 = new Context(ConcreteStrategy1);
      context1.doAnything();
      
      Strategy ConcreteStrategy2 = new ConcreteStrategy2();
      Context context2 = new Context(ConcreteStrategy2);
      context2.doAnything();
   }
}
----------------output------------------
這是第一種算法
這是第二種算法

從代碼來看,策略模式采用了面向對象的繼承和多態機制。在具體策略類中實現多種具體的算法,然后由高層模塊自己帥選使用哪種算法。

策略模式優點
①算法可以自由切換:只要是在具體策略類中實現的算法,高層模塊都可以自由的切換使用算法。
②避免使用多重條件判斷:假如沒有策略模式,那么在切換算法時需要使用多重判斷條件,這樣下來代碼容易出錯。
③擴展性良好:理論上具體策略類中的算法可以無限擴展,只要系統允許。

策略模式的缺點
①由于具體策略類中所有算法對外公布,高層模塊在切換算法時,必須知道每個算法的區別,這樣會導致使用者分精力去了解不相關的事情,違反迪米特法則。在使用策略模式時,我們可以搭配工廠模式,代理模式等優化這個缺點。
②具體策略類數量多,每個類都是一個具體的算法,復用性低,難于管理。

策略模式的使用場景
①多個類只有某些行為上不同時
②算法需要自由切換的場景
③屏蔽算法規則,只需了解算法的作用與名字,外界傳遞參數,返回結果時

策略模式的注意事項
由策略模式的缺點,很容易碰到多個算法難于管理的問題。一般來講,當策略算法超過4個時,就會考慮搭配其他模式一起使用。

策略模式的擴展
策略枚舉:包含策略模式的枚舉
問題:計算任意兩個整數的和與差,使用策略枚舉實現
策略枚舉實現類:

public enum Calculator {
   ADD() {
      public int exec(int a, int b) {
         return a + b;
      }
   },
   SUB(){
      public int exec(int a,int b){
         return a - b;
      }
   };
   public abstract int exec(int a,int b);
}

客戶端:

public class Client {
   public static void main(String[] args) {
      System.out.println(Calculator.ADD.exec(1,2));
      System.out.println(Calculator.SUB.exec(1,2));
   }
}

注意:由于枚舉類都是public,static,final的類型,因此在擴展性上受到了一定的約束,策略枚舉一般用在對象不經常改變的地方。

參考書籍:設計模式之禪 --- 秦小波 著

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容