设计模式之策略模式(Strategy Pattern)

策略模式定义

策略模式(Strategy Pattern)是一种行为型设计模式,定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式使得算法可以在不影响客户端的情况下发生变化,其主要目的是将算法的实现与使用解耦,提高程序的灵活性和可扩展性。

基本概念

  • 策略(Strategy):定义了算法的通用接口,各种具体策略类实现该接口。
  • 具体策略(Concrete Strategy):实现了策略接口,封装了具体的算法或行为。
  • 上下文(Context):持有一个策略的引用,客户端通过上下文来与策略交互。

策略模式的种类

虽然策略模式本身没有明显的种类划分,但根据实现方式和应用场景的不同,可以有以下变体:

  1. 经典策略模式:使用接口或抽象类定义策略,具体策略类实现该接口,客户端通过上下文来使用策略。
  2. 匿名内部类或 Lambda 表达式:在支持函数式编程的语言中,可以使用匿名类或 Lambda 表达式简化策略的实现。
  3. 策略枚举:当策略数量固定时,可以使用枚举类型来实现不同的策略。

基本上使用1、3两种

使用场景

  • 多种算法需要在运行时切换:如支付方式、排序算法等,根据不同的条件选择合适的算法。
  • 避免大量的条件语句:使用策略模式可以替代 if-elseswitch 语句,提高代码的可读性和可维护性。
  • 行为的变化需要独立于上下文:算法的变化不应影响使用它的代码,遵循开闭原则。

下面以最常用的场景,支付场景作为演示Demo

假设我们有一个电商平台,支持多种支付方式,如银联支付、支付宝和微信支付。
使用策略模式可以让支付方式的选择和实现独立,方便扩展新的支付方式。

1. 定义策略接口

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

2. 实现具体策略

// 银联支付策略
public class UnionPayPayment implements PaymentStrategy {
    private String cardNumber;
    private String cardHolderName;
    private String bankName;

    public UnionPayPayment(String cardNumber, String cardHolderName, String bankName) {
        this.cardNumber = cardNumber;
        this.cardHolderName = cardHolderName;
        this.bankName = bankName;
    }

    @Override
    public void pay(double amount) {
        // 银联支付的具体实现
        System.out.println("使用银联卡支付 " + amount + " 元。");
    }
}

// 支付宝支付策略
public class AlipayPayment implements PaymentStrategy {
    private String alipayId;

    public AlipayPayment(String alipayId) {
        this.alipayId = alipayId;
    }

    @Override
    public void pay(double amount) {
        // 支付宝支付的具体实现
        System.out.println("使用支付宝支付 " + amount + " 元。");
    }
}

// 微信支付策略
public class WeChatPayment implements PaymentStrategy {
    private String weChatId;

    public WeChatPayment(String weChatId) {
        this.weChatId = weChatId;
    }

    @Override
    public void pay(double amount) {
        // 微信支付的具体实现
        System.out.println("使用微信支付 " + amount + " 元。");
    }
}

3. 创建上下文类

// 购物车类,持有支付策略
public class ShoppingCart {
    private List<Item> items;
    private PaymentStrategy paymentStrategy;

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

    // 添加商品
    public void addItem(Item item) {
        items.add(item);
    }

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

    // 结账
    public void checkout() {
        double total = calculateTotal();
        paymentStrategy.pay(total);
    }

    // 计算总价
    private double calculateTotal() {
        double sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }
}

4. 商品类

public class Item {
    private String name;
    private double price;

    public Item(String name, double price) {
        this.name = name;
        this.price = price;
    }

    // 获取价格
    public double getPrice() {
        return price;
    }
}

5. 客户端代码

public class Client {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // 添加商品
        cart.addItem(new Item("苹果", 10.0));
        cart.addItem(new Item("香蕉", 20.0));

        // 选择支付方式并支付

        // 使用银联支付
        PaymentStrategy unionPayPayment = new UnionPayPayment("6222020200000000", "张三", "中国银行");
        cart.setPaymentStrategy(unionPayPayment);
        cart.checkout();

        // 使用支付宝支付
        PaymentStrategy alipayPayment = new AlipayPayment("alipay_user_id");
        cart.setPaymentStrategy(alipayPayment);
        cart.checkout();

        // 使用微信支付
        PaymentStrategy weChatPayment = new WeChatPayment("wechat_user_id");
        cart.setPaymentStrategy(weChatPayment);
        cart.checkout();
    }
}

输出结果

使用银联卡支付 30.0 元。
使用支付宝支付 30.0 元。
使用微信支付 30.0 元。

6. 新增支付方式

如果需要新增一种支付方式,例如 Apple Pay,只需新增一个策略类:

// Apple Pay 支付策略
public class ApplePayPayment implements PaymentStrategy {
    private String appleId;

    public ApplePayPayment(String appleId) {
        this.appleId = appleId;
    }

    @Override
    public void pay(double amount) {
        // Apple Pay 支付的具体实现
        System.out.println("使用 Apple Pay 支付 " + amount + " 元。");
    }
}

在客户端代码中使用:

// 使用 Apple Pay 支付
PaymentStrategy applePayPayment = new ApplePayPayment("apple_user_id");
cart.setPaymentStrategy(applePayPayment);
cart.checkout();

策略模式与抽象工厂模式的区别

虽然策略模式和抽象工厂模式都涉及到对象的创建和使用,但它们的目的和应用场景不同:

  • 策略模式:关注算法的封装和互换,旨在运行时灵活地选择和切换算法。它将行为或算法抽象成独立的策略类,使得不同的策略可以互相替换,客户端通过上下文来使用策略。

  • 抽象工厂模式:关注产品族的创建,旨在提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。抽象工厂模式主要用于创建对象,隐藏对象创建的细节,确保客户端使用的是同一产品族的对象。

区别总结

  • 目的不同:策略模式主要解决算法的选择和切换问题,抽象工厂模式主要解决对象的创建问题。

  • 结构不同:策略模式包含策略接口和具体策略类,客户端通过上下文与策略交互;抽象工厂模式包含抽象工厂、具体工厂、抽象产品和具体产品,客户端通过工厂创建产品。

  • 使用方式不同:策略模式强调算法的可替换性,通常在运行时决定使用哪种策略;抽象工厂模式强调产品族的一致性,通常在编译时就确定了具体的工厂和产品。

在支付场景中

  • 策略模式:我们使用策略模式来封装不同的支付方式(策略),如银联支付、支付宝支付、微信支付等。每种支付方式实现了相同的策略接口,可以互相替换,客户端可以在运行时选择不同的支付策略。

  • 抽象工厂模式:假设我们需要创建一系列相关的支付对象,例如支付方式、支付验证、支付日志等,且这些对象在不同的平台(如国内支付平台、国际支付平台)有不同的实现。这时可以使用抽象工厂模式,提供一个接口来创建相关的支付对象,确保同一平台的对象一起工作。

策略模式的优势

  1. 符合开闭原则:添加新的策略无需修改原有代码,扩展性好。
  2. 避免使用多重条件判断:策略模式通过多态替代了条件判断,代码更简洁。
  3. 提高代码的灵活性:可以在运行时动态更换策略,满足不同的业务需求。
  4. 策略独立,便于维护:每个策略都封装在独立的类中,便于单独修改和测试。
  5. 可复用性高:策略类可以在不同的上下文中复用,减少重复代码。

策略模式的意义

  • 解耦算法和使用场景:将算法的实现和使用它的代码分离,方便独立地更改或扩展算法。
  • 提高系统的可维护性:算法的更改不影响上下文,降低了修改的风险。
  • 满足不同的需求:通过灵活地选择策略,可以满足不同客户或环境的需求。

启发

策略模式的应用超越了代码层面,它体现了一种面向变化、拥抱变化的思想。在快速发展的技术领域,我们需要构建具有弹性和适应性的系统,以应对未来的挑战。通过合理地应用策略模式,我们可以设计出更具生命力的软件,为用户提供更好的体验。


©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容