策略模式:告别臃肿代码,实现算法的自由切换

2025/10/09 Design Patterns 共 3567 字,约 11 分钟

策略模式:告别臃肿代码,实现算法的自由切换

在软件开发中,我们经常会遇到一个需求可以通过多种算法或策略来实现的情况。例如,一个支付功能可能支持支付宝、微信支付和银联支付;一个数据排序功能可能需要根据不同的场景选择快速排序、归并排序或冒泡排序;一个导航系统可能需要为不同的出行方式(驾车、公交、步行)计算不同的路线。

面对这样的需求,一个常见的、但不够优雅的做法是使用冗长的 if-elseswitch-case 语句。这种做法虽然直接,却存在诸多弊端:代码臃肿、难以维护、违反开闭原则(对扩展开放,对修改关闭)。而策略模式正是为了解决这些问题而生的。

什么是策略模式?

策略模式 是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

简单来说,它就像是一个工具箱,里面有锤子、螺丝刀、扳手等各种工具(策略)。你需要拧螺丝时就拿出螺丝刀,需要敲钉子时就拿出锤子。策略模式让你可以自由地选择和更换“工具”,而无需改变你的“手”(客户端代码)。

模式的核心角色

策略模式通常包含三个核心角色:

  1. 策略:一个接口或抽象类,定义了所有支持算法的公共接口。
  2. 具体策略:实现了策略接口的具体算法类。
  3. 上下文:持有一个策略对象的引用,并用该策略对象来执行具体的算法。上下文通常提供一个接口,让客户端可以设置或切换策略。

策略模式的结构与代码示例

让我们通过一个经典的例子——电商系统中的支付功能——来深入理解策略模式。

步骤1:定义策略接口

首先,我们定义一个支付策略的接口,它声明了所有支付方式都必须实现的方法 pay

// 策略接口
public interface PaymentStrategy {
    void pay(double amount);
}

步骤2:实现具体策略类

接着,我们实现几种不同的支付方式,它们都实现了 PaymentStrategy 接口。

// 具体策略类:支付宝支付
public class AlipayStrategy implements PaymentStrategy {
    private String account;

    public AlipayStrategy(String account) {
        this.account = account;
    }

    @Override
    public void pay(double amount) {
        System.out.printf("使用支付宝账号 %s 支付了 %.2f 元。\n", account, amount);
        // 这里调用支付宝的实际支付API...
    }
}

// 具体策略类:微信支付
public class WechatPayStrategy implements PaymentStrategy {
    private String openId;

    public WechatPayStrategy(String openId) {
        this.openId = openId;
    }

    @Override
    public void pay(double amount) {
        System.out.printf("使用微信OpenID %s 支付了 %.2f 元。\n", openId, amount);
        // 这里调用微信支付的实际支付API...
    }
}

// 具体策略类:信用卡支付
public class CreditCardStrategy implements PaymentStrategy {
    private String cardNumber;
    private String cvv;

    public CreditCardStrategy(String cardNumber, String cvv) {
        this.cardNumber = cardNumber;
        this.cvv = cvv;
    }

    @Override
    public void pay(double amount) {
        System.out.printf("使用信用卡 %s 支付了 %.2f 元。\n", cardNumber, amount);
        // 这里调用银行网关进行支付...
    }
}

步骤3:创建上下文类

上下文类 PaymentContext 负责与策略交互。它持有一个支付策略,并提供一个执行支付的方法和一个可以动态设置策略的方法。

// 上下文类
public class PaymentContext {
    private PaymentStrategy strategy;

    // 通过构造器注入策略
    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    // 通过Setter方法动态改变策略
    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    // 执行支付
    public void executePayment(double amount) {
        strategy.pay(amount);
    }
}

步骤4:客户端使用

最后,我们看看客户端如何灵活地使用这些策略。

public class Client {
    public static void main(String[] args) {
        double orderAmount = 100.50;

        // 用户选择支付宝支付
        PaymentContext context = new PaymentContext(new AlipayStrategy("zhangsan@alipay.com"));
        context.executePayment(orderAmount);

        System.out.println("-------------");

        // 用户中途切换为微信支付(动态切换策略)
        context.setStrategy(new WechatPayStrategy("wx_openid_123456"));
        context.executePayment(orderAmount);

        System.out.println("-------------");

        // 新订单使用信用卡支付
        PaymentContext newContext = new PaymentContext(new CreditCardStrategy("1234-5678-9012-3456", "123"));
        newContext.executePayment(200.00);
    }
}

输出结果:

使用支付宝账号 zhangsan@alipay.com 支付了 100.50 元。
-------------
使用微信OpenID wx_openid_123456 支付了 100.50 元。
-------------
使用信用卡 1234-5678-9012-3456 支付了 200.00 元。

通过这个例子,我们可以看到,当需要新增一种支付方式(如Apple Pay)时,我们只需要创建一个新的 ApplePayStrategy 类实现 PaymentStrategy 接口即可,完全不需要修改 PaymentContext 或客户端的其他代码。这完美地遵循了开闭原则

策略模式的优缺点

优点

  1. 开闭原则:无需修改上下文即可引入新的策略。
  2. 消除条件判断:避免了冗长的条件判断语句(如 if-else)。
  3. 代码复用:可以在系统的不同部分复用相同的策略。
  4. 算法隔离:将算法的实现细节与使用它的代码隔离开来。

缺点

  1. 客户端必须了解策略:客户端需要知道不同策略之间的区别,以便选择合适的策略。
  2. 增加对象数量:如果策略很多,会产生大量的具体策略类。
  3. 通信开销:策略和上下文之间可能需要通过接口传递数据,增加了通信开销。

实际应用场景

策略模式在现实世界的框架和库中无处不在:

  • Java Collections FrameworkCollections.sort() 方法接受一个 Comparator 参数,这就是一个策略接口,允许我们为排序定义不同的策略。
  • Spring Framework:在Spring中,ResourceLoader 用于加载资源(如类路径资源、文件系统资源、URL资源),不同的 Resource 实现就是不同的策略。
  • 支付网关集成:如上例所示,是策略模式的经典应用。
  • 日志框架:如SLF4J,它作为一个门面,背后可以绑定Logback、Log4j2等不同的日志实现策略。
  • 数据验证:一个字段的验证规则(非空、邮箱格式、手机号格式)可以定义为不同的验证策略。

总结

策略模式通过将算法封装成独立的类,提供了一种清晰、灵活的方式来管理和切换行为。它是应对算法频繁变化场景的利器,能够显著提高代码的可读性、可维护性和扩展性。

当你发现代码中充满了条件分支,且每个分支都执行着一种类似的但细节不同的操作时,不妨考虑使用策略模式来重构它。记住,优秀的软件设计不是一蹴而就的,而是在不断地识别变化、封装变化的过程中演进而来的。策略模式正是封装变化这一核心设计原则的完美体现。

文档信息

Search

    Table of Contents