策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互 相替换,此模式让算法的变化不会影响到使用算法的用户。

应用场景
  1. 假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
  2. 一个系统需要动态地在几种算法中选择一种
支付案例

支付抽象类Payment

public abstract class Payment {

    //支付类型
    public abstract String getName();

    //查询余额
    protected abstract double queryBalance(String uid);

    //扣款支付
    public MsgResult pay(String uid, double amount) {
        if(queryBalance(uid) < amount){
            return new MsgResult(500,"支付失败","余额不足");
        }
        return new MsgResult(200,"支付成功","支付金额:" + amount);
    }

}

支付宝

public class AliPay extends Payment {

    public String getName() {
        return "支付宝";
    }

    protected double queryBalance(String uid) {
        return 900;
    }

}

微信

public class WechatPay extends Payment {

    public String getName() {
        return "微信支付";
    }

    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<String,Payment>();
    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);
    }

订单支付

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 MsgResult pay(){
        return pay(PayStrategy.DEFAULT_PAY);
    }

    public MsgResult pay(String payKey){
        Payment payment = PayStrategy.get(payKey);
        System.out.println("欢迎使用" + payment.getName());
        System.out.println("本次交易金额为:" + amount + ",开始扣款...");
        return payment.pay(uid,amount);
    }
}

测试类

public static void main(String[] args) {
        Order order = new Order("1","20180311001000009",324.45);
        //在支付的时候才决定用哪个值
        System.out.println(order.pay(PayStrategy.ALI_PAY));
    }
源码体现

JDK源码 Comparator

比较器 Comparator 接口,常用的 compare()方法,就是一个策略抽象实现

@FunctionalInterface
public interface Comparator<T> {
	int compare(T o1, T o2);
}

Comparator 抽象下面有非常多的实现类,我们经常会把 Comparator 作为参数传入作 为排序策略

  • Arrays 类的 parallelSort 方法
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;
    }

Spring源码

  • Resource
    我们虽然没有直接使用 Resource 类,但是我们经常使用它的子类
    设计模式系列6-策略模式_策略模式

  • Spring 的初始化
    Spring 的初始化也采用了策略模式,不同的类型的类采用不 同的初始化策略。首先有一个 InstantiationStrategy 接口

顶层的策略抽象非常简单,但是它下面有两种策略 SimpleInstantiationStrategyCglibSubclassingInstantiationStrategy

设计模式系列6-策略模式_初始化_02

打开类图我们还发现 CglibSubclassingInstantiationStrategy 策略类还继承了 SimpleInstantiationStrategy 类,说明在实际应用中多种策略之间还可以继承使用

优缺点
  • 优点
  1. 策略模式符合开闭原则。
  2. 避免使用多重条件转移语句,如 if...else...语句、switch 语句
  3. 使用策略模式可以提高算法的保密性和安全性。
  • 缺点
  1. 客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
  2. 代码中会产生非常多策略类,增加维护难度。