策略模式(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,这样做成策略模式。