策略模式 (Strategy)详解

发布于:2025-02-23 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、什么是策略模式?

  • 定义: 策略模式是一种行为型设计模式。 它定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。
  • 核心思想: 将算法的定义与使用分离。 客户端代码不直接调用具体的算法,而是通过一个统一的接口(策略接口)来访问不同的算法。
  • 意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

二、策略模式的结构

策略模式通常包含以下几个角色:

  1. Context (环境/上下文):

    • 维护一个对 Strategy 对象的引用。
    • 定义客户端使用的接口。
    • 可以包含一些与算法无关的业务逻辑。
    • 不直接与具体策略类交互,而是通过 Strategy 接口与策略类交互。
  2. Strategy (策略接口):

    • 定义所有支持的算法的公共接口。
    • 通常是一个接口或抽象类。
  3. ConcreteStrategy (具体策略):

    • 实现 Strategy 接口,提供具体的算法实现。
    • 可以有多个 ConcreteStrategy 类,每个类实现一种不同的算法。

UML 类图:

+-----------------+         +-----------------+       +---------------------+
|     Context     |-------->|   <<Strategy>>   |       |   ConcreteStrategyA |
+-----------------+         +-----------------+       +---------------------+
| -strategy       |         | +algorithm()    |------>| +algorithm()        |
+-----------------+         +-----------------+       +---------------------+
| +contextInterface()|                                   
+-----------------+                                   
                                                     +---------------------+
                                                     |   ConcreteStrategyB |
                                                     +---------------------+
                                                     | +algorithm()        |
                                                     +---------------------+
                                                     
                                                     +---------------------+
                                                     |   ConcreteStrategyC |
                                                     +---------------------+
                                                     | +algorithm()        |
                                                     +---------------------+

三、策略模式的实现 (Java)

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

// 具体策略类 - 支付宝支付
class AlipayStrategy implements PaymentStrategy {
    private String email;
    private String password;

    public AlipayStrategy(String email, String password) {
        this.email = email;
        this.password = password;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Alipay.");
        // ... 支付宝支付的具体逻辑 ...
    }
}

// 具体策略类 - 微信支付
class WeChatPayStrategy implements PaymentStrategy {
    private String appId;
    private String secretKey;

    public WeChatPayStrategy(String appId, String secretKey) {
        this.appId = appId;
        this.secretKey = secretKey;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using WeChat Pay.");
        // ... 微信支付的具体逻辑 ...
    }
}

// 具体策略类 - 银行卡支付
class CreditCardStrategy implements PaymentStrategy {
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

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

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card.");
        // ... 银行卡支付的具体逻辑 ...
    }
}

// 上下文类
class ShoppingCart {
    private List<Item> items;
    private PaymentStrategy paymentStrategy; // 持有策略接口的引用

    public ShoppingCart() {
        this.items = new ArrayList<>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    // 设置支付策略
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    // 使用支付策略进行支付
    public void pay() {
        int amount = calculateTotal();
        paymentStrategy.pay(amount); // 通过策略接口调用具体算法
    }
}

// 商品类 (示例)
class Item {
    private String upcCode;
    private int price;

    public Item(String upcCode, int price) {
        this.upcCode = upcCode;
        this.price = price;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }
}

// 客户端代码
public class StrategyPatternExample {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // 使用支付宝支付
        cart.setPaymentStrategy(new AlipayStrategy("myemail@example.com", "mypwd"));
        cart.pay();

        // 使用微信支付
        cart.setPaymentStrategy(new WeChatPayStrategy("myAppId", "mySecretKey"));
        cart.pay();

        // 使用银行卡支付
        cart.setPaymentStrategy(new CreditCardStrategy("1234567890123456", "786", "12/24"));
        cart.pay();
    }
}

代码解释:

  • PaymentStrategy (策略接口): 定义了支付的公共接口 pay()
  • AlipayStrategyWeChatPayStrategyCreditCardStrategy (具体策略): 实现了 PaymentStrategy 接口,提供了不同的支付方式。
  • ShoppingCart (上下文): 维护了一个 PaymentStrategy 类型的引用,可以在运行时设置不同的支付策略。 pay() 方法通过 paymentStrategy 对象调用具体的支付算法。

四、策略模式的优缺点

优点:

  • 开闭原则: 增加新的策略非常方便,无需修改原有代码,只需要添加新的具体策略类即可。
  • 避免使用多重条件判断: 将不同的算法封装到不同的策略类中,避免了使用 if-elseswitch-case 等多重条件判断语句。
  • 提高算法的复用性: 不同的策略类可以被多个上下文对象复用。
  • 提高算法的保密性: 客户端不需要知道算法的具体实现细节。
  • 松耦合: 算法的定义与使用分离,降低了代码的耦合度。

缺点:

  • 客户端必须知道所有的策略类: 客户端需要知道所有的策略类,并选择合适的策略类。
  • 增加类的数量: 每增加一个策略,就需要增加一个具体策略类,可能会导致类的数量过多。
  • 只适用于算法或行为的选择: 策略模式只适用于封装算法或行为,不适用于封装对象的状态。
  • 策略间通信: 如果策略之间需要共享数据或进行通信,可能需要额外的机制来处理。

五、策略模式的适用场景

  • 需要在运行时动态地选择算法: 例如,根据用户的选择、系统配置或外部条件来选择不同的算法。
  • 有多种算法或行为可供选择: 例如,排序算法(冒泡排序、快速排序、归并排序)、压缩算法(ZIP、GZIP、RAR)、加密算法(AES、DES、RSA)等。
  • 需要避免使用多重条件判断语句: 如果代码中存在大量的 if-elseswitch-case 语句来根据不同的条件选择不同的算法,可以考虑使用策略模式。
  • 需要对客户端隐藏算法的具体实现细节: 可以将算法的实现细节封装在策略类中,只向客户端暴露策略接口。

六、策略模式的应用示例

  1. 排序算法: 定义一个排序策略接口,不同的排序算法(例如冒泡排序、快速排序、归并排序)实现该接口。 客户端可以根据需要选择不同的排序算法。
  2. 压缩算法: 定义一个压缩策略接口,不同的压缩算法(例如 ZIP、GZIP、RAR)实现该接口。 客户端可以根据需要选择不同的压缩算法。
  3. 加密算法: 定义一个加密策略接口,不同的加密算法(例如 AES、DES、RSA)实现该接口。 客户端可以根据需要选择不同的加密算法。
  4. 支付方式: 定义一个支付策略接口,不同的支付方式(例如支付宝、微信支付、银行卡支付)实现该接口。 客户端可以根据需要选择不同的支付方式。
  5. Java I/O 中的 Comparator 接口: Comparator 接口定义了比较两个对象的方法。 可以创建不同的 Comparator 实现类来定义不同的比较策略。

七、策略模式 vs. 模板方法模式 vs. 状态模式

  • 策略模式 (Strategy): 关注的是 算法的替换,通过组合和接口来实现算法的切换。
  • 模板方法模式 (Template Method): 关注的是 算法的骨架,通过继承和重写来实现算法的可变部分。
  • 状态模式 (State): 关注的是 对象的状态变化,通过状态对象来改变对象的行为。

区别:

特征 策略模式 模板方法模式 状态模式
关注点 算法选择 算法骨架 对象状态
实现方式 组合/委托 继承 组合/委托
可变部分 不同的策略类实现相同的接口 子类实现抽象方法或覆盖钩子方法 不同的状态类实现相同的接口
算法结构 策略类定义算法,客户端选择具体的策略 模板方法定义算法结构,子类实现具体步骤 状态类定义状态下的行为,Context 对象根据状态切换行为
控制反转
适用场景 定义一系列算法,并在运行时动态选择算法 定义算法骨架,部分步骤延迟到子类实现 对象行为取决于状态,且状态变化复杂

总结:

策略模式是一种非常有用的设计模式,它可以将算法的定义与使用分离,提高代码的可扩展性、可维护性和可复用性。