简简单单设计模式-策略

策略模式是一种行为型模式,核心是将不同的算法/业务逻辑封装成为独立的策略类,使它们可以相互替换,且策略的变化不影响使用策略的客户端。

代码实现

比如说在支付业务中,需要根据不同的支付方式处理支付结果。如以下代码所示:

csharp 复制代码
public class PaymentService {
​
    /**
     * 统一支付方法:通过if-else分支区分支付方式
     */
    public String pay(String payType, BigDecimal amount) {
        // 校验金额(通用逻辑)
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return "支付金额必须大于0!";
        }
​
        // 核心分支:不同支付方式的逻辑耦合在这里
        if ("alipay".equals(payType)) {
            // 支付宝支付逻辑(硬编码)
            String alipayResult = "调用支付宝接口,订单号:" + generateOrderNo()
                    + ",支付金额:" + amount + "元 → 支付成功";
            // 支付宝特有逻辑:如签名验证、回调处理等
            verifyAlipaySign();
            return alipayResult;
        } else if ("wxpay".equals(payType)) {
            // 微信支付逻辑(硬编码)
            String wxpayResult = "调用微信支付接口,订单号:" + generateOrderNo()
                    + ",支付金额:" + amount + "元 → 支付成功";
            // 微信特有逻辑:如appid校验、签名生成等
            generateWxPaySign();
            return wxpayResult;
        } else if ("unionpay".equals(payType)) {
            // 银联支付逻辑(硬编码)
            String unionpayResult = "调用银联支付接口,订单号:" + generateOrderNo()
                    + ",支付金额:" + amount + "元 → 支付成功";
            // 银联特有逻辑:如证书校验等
            verifyUnionPayCert();
            return unionpayResult;
        } else {
            return "不支持的支付方式:" + payType;
        }
    }
​
​
    // 通用工具方法:生成订单号
    private String generateOrderNo() {
        return "ORDER_" + System.currentTimeMillis();
    }
​
    // 支付宝特有方法
    private void verifyAlipaySign() {
        System.out.println("校验支付宝签名...");
    }
​
    // 微信支付特有方法
    private void generateWxPaySign() {
        System.out.println("生成微信支付签名...");
    }
​
    // 银联特有方法
    private void verifyUnionPayCert() {
        System.out.println("校验银联证书...");
    }
}
​
class PaymentServiceTest {
    public static void main(String[] args) {
        PaymentService paymentService = new PaymentService();
        // 支付宝支付
        System.out.println(paymentService.pay("alipay", new BigDecimal("100")));
        // 微信支付
        System.out.println(paymentService.pay("wxpay", new BigDecimal("200")));
    }
}
​

如果以后加入新的处理方式,这个支付实现类需要改变,且不利于扩展。如果出现组合支付,这个逻辑代码还要改动。根据设计原则:

找出应用中可能需要变化之处,把它们独立出来,不要和那些需要变化的代码混在一起

把变化的部分取出并"封装"起来,好让其他部分不受影响。代码变化引起的不经意后果变少,系统会变得更有弹性。所以将if代码块里面的逻辑抽出来,形成不同的支付方式,这就是把将来可能变化的代码独立出来。

独立出来的支付方法怎么设计呢,支付可以认为是一种行为方式,不同的支付方式则代表具体的行为。所以可以用接口代表每个行为,实现接口就是表示具体的行为。这里用到了一个设计原则:

针对接口编程,而不是针对实现编程

然后在支付实现类添加一个容器,配置具体支付策略对象,定义支付方法,支付实现类通过容器获取支付策略,如下面代码所示:

typescript 复制代码
public class PaymentService {
​
    private static final Map<String, PaymentStrategy> paymentStrategies = new HashMap<>();
​
    static {
        paymentStrategies.put("alipay", new AlipayStrategy());
        paymentStrategies.put("wxpay", new WechatPayStrategy());
        paymentStrategies.put("unionpay", new UnionPayStrategy());
    }
​
    /**
     * 统一支付方法:通过if-else分支区分支付方式
     */
    public String pay(String payType, BigDecimal amount) {
        // 校验金额(通用逻辑)
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return "支付金额必须大于0!";
        }
        PaymentStrategy paymentStrategy = paymentStrategies.get(payType);
        // 核心分支:不同支付方式的逻辑耦合在这里
        if (paymentStrategy != null) {
            // 支付宝支付逻辑(硬编码)
            return paymentStrategy.pay(amount);
        } else {
            return "不支持的支付方式:" + payType;
        }
    }
}
​
class PaymentServiceTest {
    public static void main(String[] args) {
        PaymentService paymentService = new PaymentService();
        // 支付宝支付
        System.out.println(paymentService.pay("alipay", new BigDecimal("100")));
        // 微信支付
        System.out.println(paymentService.pay("wxpay", new BigDecimal("200")));
        // 银联支付
        System.out.println(paymentService.pay("unionpay", new BigDecimal("300")));
        // 不支持的支付方式
        System.out.println(paymentService.pay("applepay", new BigDecimal("400")));
        // 金额校验失败
        System.out.println(paymentService.pay("alipay", new BigDecimal("0")));
    }
}
​
​
interface PaymentStrategy {
​
    String pay(BigDecimal amount);
​
    String generateOrderNo();
​
    void verifySign();
}
​
class WechatPayStrategy implements PaymentStrategy {
    @Override
    public String pay(BigDecimal amount) {
        String wxpayResult = "调用微信支付接口,订单号:" + generateOrderNo()
                + ",支付金额:" + amount + "元 → 支付成功";
        verifySign();
        return wxpayResult;
    }
​
    @Override
    public String generateOrderNo() {
        return "ORDER_" + System.currentTimeMillis();
    }
​
    @Override
    public void verifySign() {
        System.out.println("生成微信支付签名...");
    }
}
​
class AlipayStrategy implements PaymentStrategy {
    @Override
    public String pay(BigDecimal amount) {
        String alipayResult = "调用支付宝接口,订单号:" + generateOrderNo()
                + ",支付金额:" + amount + "元 → 支付成功";
        verifySign();
        return alipayResult;
    }
​
    @Override
    public String generateOrderNo() {
        return "ORDER_" + System.currentTimeMillis();
    }
​
    @Override
    public void verifySign() {
        System.out.println("校验支付宝签名...");
    }
}
​
class UnionPayStrategy implements PaymentStrategy {
    @Override
    public String pay(BigDecimal amount) {
        // 银联支付逻辑(硬编码)
        String unionpayResult = "调用银联支付接口,订单号:" + generateOrderNo()
                + ",支付金额:" + amount + "元 → 支付成功";
        verifySign();
        return unionpayResult;
    }
​
    @Override
    public String generateOrderNo() {
        return "ORDER_" + System.currentTimeMillis();
    }
​
    @Override
    public void verifySign() {
        System.out.println("校验银联证书...");
    }
}

这样在我们新增支付方式时,就不需要改动支付实现类的代码了,只需要添加一个新的支付策略类实现接口方法。

将未来需要改变的和不改变的代码分离,并用实现而非继承的方式,这符合OO设计思想。后续可以添加策略工厂,降低策略对象的耦合。

核心结构(UML类图)

通过以上的例子对策略模式有了了解,现在再看策略模式的UML类图就容易理解了,如下:

  • Strategy(策略,如PaymentStrategy)

    • 定义所有支持的算法的公共接口,如支付算法。Context使用这个接口来调用某个ConcreteStrategy 定义的算法。
  • ConcreteStrategy(具体策略,如WechatPayStrategy,AlipayStrategy)

    • 以Strategy接口实现某个具体算法,如微信的支付算法,支付宝的支付算法。
  • Context(上下文,如PaymentService)

    • 用一个ConcreteStrategy 对象来配置,如定义一个HashMap来具体算法对象。
    • 定义一个接口来让Strategy访问它的数据。
    • 维护一个对Strategy 对象的引用。

Strategy和Context 可以相互组合实现选定的算法,就像文中实现的支付策略一样。

框架中的应用

MyBatis的Interceptor接口是框架最重要的扩展点之一,框架实现了不同的拦截器,也可以自定义实现Interceptor,这就是策略模式的体现:

less 复制代码
public interface Interceptor {
    Object intercept(Invocation var1) throws Throwable;
​
    Object plugin(Object var1);
​
    void setProperties(Properties var1);
}
​
// 具体策略实现
@Component
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {...})
})
public class PerformanceMonitorInterceptor implements Interceptor {
    // 性能监控策略
}
​
@Component  
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {...})
})
public class DataPermissionInterceptor implements Interceptor {
    // 数据权限策略
}
​
@Component
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {...})
})
public class PaginationInterceptor implements Interceptor {
    // 分页策略
}
总结

如果说只有几种策略,并且未来是不会被改变的,那么可以选择不用策略模式。否则建议还是使用策略模式,它的优势在于:客户端可以动态的切换策略而无需改变代码、新增策略也只添加新的策略类不会改变原来代码、替换 if-elseswitch 语句、策略类逻辑清晰职责单一。


相关推荐
源代码•宸25 分钟前
GoLang八股(Go语言基础)
开发语言·后端·golang·map·defer·recover·panic
czlczl2002092525 分钟前
OAuth 2.0 解析:后端开发者视角的原理与流程讲解
java·spring boot·后端
颜淡慕潇33 分钟前
Spring Boot 3.3.x、3.4.x、3.5.x 深度对比与演进分析
java·后端·架构
布列瑟农的星空33 分钟前
WebAssembly入门(一)——Emscripten
前端·后端
小突突突2 小时前
Spring框架中的单例bean是线程安全的吗?
java·后端·spring
iso少年2 小时前
Go 语言并发编程核心与用法
开发语言·后端·golang
掘金码甲哥2 小时前
云原生算力平台的架构解读
后端
码事漫谈2 小时前
智谱AI从清华实验室到“全球大模型第一股”的六年征程
后端
码事漫谈2 小时前
现代软件开发中常用架构的系统梳理与实践指南
后端
Mr.Entropy3 小时前
JdbcTemplate 性能好,但 Hibernate 生产力高。 如何选择?
java·后端·hibernate