简简单单设计模式-策略

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

代码实现

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

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 语句、策略类逻辑清晰职责单一。


相关推荐
止语Lab2 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活2 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困2 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn2 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530142 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908402 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金2 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒3 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境3 小时前
提升 Python 代码健壮性的方法大盘点
后端·python