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

    一个常见的应用场景就是大家在支付时会提示选择支付方式,如果用户未选,系统也会使用默认的支付方式进行结算。来看一下类图,如下图所示。

    下面我们用策略模式来模拟此业务场景。

    创建 Payment 抽象类,定义支付规范和支付逻辑:

    1. package com.yjw.demo.pattern.strategy.pay;
    2. import com.yjw.demo.pattern.strategy.PayState;
    3. /**
    4. * 支付渠道
    5. */
    6. public abstract class Payment {
    7. /**
    8. * 支付类型
    9. */
    10. public abstract String getName();
    11. /**
    12. * 查询余额
    13. *
    14. * @param uid
    15. * @return
    16. */
    17. protected abstract double queryBalance(String uid);
    18. /**
    19. * 扣款支付
    20. *
    21. * @param uid
    22. * @param amount
    23. */
    24. public PayState pay(String uid, Double amount) {
    25. if (queryBalance(uid) < amount) {
    26. return new PayState(500, "支付失败", "余额不足");
    27. }
    28. return new PayState(200, "支付成功", "支付金额:" + amount);
    29. }
    30. }

    分别创建具体的支付方式,支付宝支付类 AliPay:

    1. package com.yjw.demo.pattern.strategy.pay;
    2. public class AliPay extends Payment {
    3. @Override
    4. public String getName() {
    5. return "支付宝";
    6. }
    7. @Override
    8. protected double queryBalance(String uid) {
    9. return 900;
    10. }
    11. }

    微信支付类 WechatPay:

    1. package com.yjw.demo.pattern.strategy.pay;
    2. public class WechatPay extends Payment {
    3. @Override
    4. public String getName() {
    5. return "微信支付";
    6. }
    7. @Override
    8. protected double queryBalance(String uid) {
    9. return 256;
    10. }
    11. }

    银联支付类 UnionPay:

    1. package com.yjw.demo.pattern.strategy.pay;
    2. public class UnionPay extends Payment {
    3. @Override
    4. public String getName() {
    5. return "银联支付";
    6. }
    7. @Override
    8. protected double queryBalance(String uid) {
    9. return 120;
    10. }
    11. }

    创建支付状态的包装类 PayState:

    1. package com.yjw.demo.pattern.strategy;
    2. public class PayState {
    3. private int code;
    4. private Object data;
    5. private String msg;
    6. public PayState(int code, Object data, String msg) {
    7. this.code = code;
    8. this.data = data;
    9. this.msg = msg;
    10. }
    11. @Override
    12. public String toString() {
    13. return ("支付状态:[" + code + "]," + msg + ",交易详情:" + data);
    14. }
    15. }

    创建支付策略管理类 PayStrategy:

    1. package com.yjw.demo.pattern.strategy;
    2. import com.yjw.demo.pattern.strategy.pay.AliPay;
    3. import com.yjw.demo.pattern.strategy.pay.Payment;
    4. import com.yjw.demo.pattern.strategy.pay.UnionPay;
    5. import com.yjw.demo.pattern.strategy.pay.WechatPay;
    6. import java.util.HashMap;
    7. import java.util.Map;
    8. public class PayStrategy {
    9. public static final String DEFAULT_PAY = "AliPay";
    10. public static final String ALI_PAY = "AliPay";
    11. public static final String WECHAT_PAY = "WechatPay";
    12. public static final String UNION_PAY = "UnionPay";
    13. private static Map<String, Payment> payStrategy = new HashMap<>();
    14. static {
    15. payStrategy.put(DEFAULT_PAY, new AliPay());
    16. payStrategy.put(ALI_PAY, new AliPay());
    17. payStrategy.put(WECHAT_PAY, new WechatPay());
    18. payStrategy.put(UNION_PAY, new UnionPay());
    19. }
    20. public static Payment get(String payKey) {
    21. if (!payStrategy.containsKey(payKey)) {
    22. return payStrategy.get(DEFAULT_PAY);
    23. }
    24. return payStrategy.get(payKey);
    25. }
    26. }

    创建订单类 Order:

    1. package com.yjw.demo.pattern.strategy;
    2. import com.yjw.demo.pattern.strategy.pay.Payment;
    3. /**
    4. * 模拟订单支付场景
    5. *
    6. * @author yinjianwei
    7. * @date 2018/12/13
    8. */
    9. public class Order {
    10. private String uid;
    11. private String orderId;
    12. private Double amount;
    13. public Order(String uid, String orderId, Double amount) {
    14. this.uid = uid;
    15. this.orderId = orderId;
    16. this.amount = amount;
    17. }
    18. public PayState pay() {
    19. return pay(PayStrategy.DEFAULT_PAY);
    20. }
    21. public PayState pay(String payKey) {
    22. Payment payment = PayStrategy.get(payKey);
    23. System.out.println("欢迎使用" + payment.getName());
    24. System.out.println("本次交易金额为:" + amount + ",开始扣款。。。");
    25. return payment.pay(uid, amount);
    26. }
    27. }

    测试代码如下:

    1. package com.yjw.demo.pattern.strategy;
    2. public class PayStrategyTest {
    3. public static void main(String[] args) {
    4. Order order = new Order("1", "20191119163020300", 300.87);
    5. System.out.println(order.pay(PayStrategy.ALI_PAY));
    6. }
    7. }

    运行结果如下图所示。

    image.png

    摘录:《Spring 5 核心原理与30个类手写实战》来自文艺界的Tom老师的书籍。

    看到组内分享的一篇描述策略模式的文章(https://blog.csdn.net/qq_23934475/article/details/83088332),使用 Java 8 的函数式接口、Lambda 表达式改写策略模式,这种方式可以使用在平时的业务代码中,它类似匿名内部类的实现,省略了一些策略类的编写,减少了类的数量,但是如果是编写框架,需要制定严格的策略,不建议使用这种方式,还是自定义策略类比较好,便于归类、理解,大家一看到以 Strategy 结尾的类就知道了这个类是策略类了。

    作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/hbhayl 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。