策略模式(Strategy Pattern)是指定義了算法家族病分別封裝起來,讓他們之間可以互相替換,此模式是的算法的變化不會影響使用算法的用戶。
一、策略模式的應用場景
策略模式的應用場景如下:
- 系統中有很多類,而他們的區別僅僅在於行為不同。
- 一個系統需要動態的在幾種算法中選擇一種
二、用策略模式實現選擇支付方式的業務場景
一個常見的應用場景就是大家在支付時會提示選擇支付方式,如果用戶未選,系統會使用默認的支付方式進行結算。下面我們用策略模式來模擬此業務場景:
/** * 定義支付規范和支付邏輯 */ public abstract class Payment { //支付類型 public abstract String getName(); //查詢余額 protected abstract double queryBalance(String uid); //扣款支付 public PayState pay(String uid, double amount) { if (queryBalance(uid) < amount) { return new PayState(500, "支付失敗", "余額不足"); } return new PayState(200, "支付成功", "支付金額:" + amount); } }
扣款支付邏輯簡單做一下:
/** * 支付邏輯 */ public class PayState { private int code; private Object data; private String msg; public PayState(int code, String msg, String data) { this.code = code; this.data = data; this.msg = msg; } public String toString() { return ("支付狀態:[" + code + "]," + msg + ",交易詳情:" + data); } }
定義兩種支付方式:
/** * 支付寶 */ public class AliPay extends Payment { @Override public String getName() { return "支付寶"; } @Override protected double queryBalance(String uid) { return 900; } } /** * 微信支付 */ public class WechatPay extends Payment { @Override public String getName() { return "微信支付"; } @Override protected double queryBalance(String uid) { return 256; } }
支付策略管理類
/** * 支付策略管理類 */ public class PayStrategy { public static final String ALI_PAY = "AliPay"; public static final String WECHAT_PAY = "WechatPay"; public static final String DEFAULT_PAY = ALI_PAY; private static Map<String, Payment> payStrategy = new HashMap<>(); static { payStrategy.put(ALI_PAY, new AliPay()); payStrategy.put(WECHAT_PAY, new WechatPay()); } public static Payment get(String payKey) { if (!payStrategy.containsKey(payKey)) { return payStrategy.get(DEFAULT_PAY); } return payStrategy.get(payKey); } }
創建訂單:訂單支付時因為支付方式已經在集合內並且可以通過key得到,所以不用switch 和if else進行贅述
/** * 訂單類 */ public class Order { private String uid; private String orderId; private Double amount; public Order(String uid, String orderId, Double amount) { this.uid = uid; this.orderId = orderId; this.amount = amount; } //完美解決了switch的過程,不需要寫switch和if...else if public PayState pay() { return pay(PayStrategy.DEFAULT_PAY); } public PayState pay(String payKey) { Payment payment = PayStrategy.get(payKey); System.out.println("歡迎使用:" + payment.getName()); System.out.println("本次交易金額為:" + amount + ",扣款中..."); return payment.pay(uid, amount); } }
測試類:
public class PayStrategyTest { public static void main(String[] args) { //創建訂單 Order order = new Order("1", "202001080001", 324.45); //選擇支付方式:支付寶支付 System.out.println(order.pay(PayStrategy.ALI_PAY)); } }
三、策略模式在源碼中的體現
1.JDK中的應用
首先看比較常見的比較器——Compare接口,常用的compare()方法就是常見的策略模式的抽象實現,Comparator接口下面有非常多的實現類,我們把Comparator接口作為傳入實現排序策略例如:
public static <T> void parallelSort(T[] a, Comparator<? super T> cmp) { if (cmp == null) cmp = NaturalOrder.INSTANCE; int n = a.length, p, g; if (n <= MIN_ARRAY_SORT_GRAN || (p = ForkJoinPool.getCommonPoolParallelism()) == 1) TimSort.sort(a, 0, n, cmp, null, 0, 0); else new ArraysParallelSortHelpers.FJObject.Sorter<T> (null, a, (T[])Array.newInstance(a.getClass().getComponentType(), n), 0, n, 0, ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ? MIN_ARRAY_SORT_GRAN : g, cmp).invoke(); }
還有TreeMap類的構造方法:
public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }
2.Spring源碼中的應用
Spring的初始化采用了策略模式,不同類型的類采用不同的初始化策略。
public interface InstantiationStrategy { Object instantiate(RootBeanDefinition var1, String var2, BeanFactory var3) throws BeansException; Object instantiate(RootBeanDefinition var1, String var2, BeanFactory var3, Constructor<?> var4, Object... var5) throws BeansException; Object instantiate(RootBeanDefinition var1, String var2, BeanFactory var3, Object var4, Method var5, Object... var6) throws BeansException; }
四、策略模式的優缺點
策略模式的優點如下:
- 策略模式符合開閉原則。
- 策略模式可避免使用多重條件語句,如switch if else。
- 使用策略模式可以提高算法的保密性和安全性。
策略模式的缺點如下:
- 客戶端必須知道所有的策略,並且自行決定使用哪一個策略類。
- 代碼中會產生非常多的策略類,增加了代碼的維護難度。
五、委派模式與策略模式綜合應用
委派模式:DispatcherServlet 根據url對應Controller 用的是if else, 應該將對應關系放入容器中,利用URL找到對應的Controller,這樣做成策略模式。