策略模式 (Strategy)详解

news/2025/2/22 5:52:18

一、什么是策略模式?

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

二、策略模式的结构

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

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

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

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

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

UML 类图:

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

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

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 对象根据状态切换行为
控制反转
适用场景定义一系列算法,并在运行时动态选择算法定义算法骨架,部分步骤延迟到子类实现对象行为取决于状态,且状态变化复杂

总结:

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


http://www.niftyadmin.cn/n/5861696.html

相关文章

Vue Axios

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue 使用 - Axios 目录 Axios 请求方法 功能 安装 使用CDN 引入axios 使用axios 赋值 总结 Axios Axios&#xff0c;基于 Promise 的 HTTP 客户端&#xff0c;可以工作于浏览器中&#xff0c;也可以在 node.js 中使用…

微信小程序——访问服务器媒体文件的实现步骤

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;趣享先生的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&…

使用Trae AI IDE,轻松实现交友聊天功能

目录 前言 开发者的日常痛点 1、开发效率瓶颈 2、AI在开发中的潜力 3、AI IDE和插件的区别 初识Trae&#xff1a;一个开发者的意外发现 1、关于Trae 2、Trae的核心功能 3、下载安装Trae指南 1.选择主题与语言 2.导入现有 IDE 配置&#xff08;可选&#xff09; 3.安…

ABC 391

目录 C. Make it Simple D. Swap to Gather E. GCD of Subset C. Make it Simple 看当前输入的两个点作为一对是否被标记过&#xff0c;用 set 判重就可以了 #include<bits/stdc.h> #define int long long using namespace std; const int N 1e5 5, INF 1e18;int …

人工智能任务23-天文领域的超亮超新星能源机制结合深度神经网络的研究方向

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能任务23-天文领域的超亮超新星能源机制结合深度神经网络的研究方向。 文章目录 一、研究背景阐述超亮超新星的定义与发现历程超亮超新星能源机制的主要理论模型1. 56Ni衰变模型2. 超新星抛射物与致密星周介…

游戏引擎学习第115天

仓库:https://gitee.com/mrxiao_com/2d_game_3 打开程序&#xff0c;查看我们在性能方面的进展 这段内容主要介绍了优化代码以利用处理器中的SIMD&#xff08;单指令多数据&#xff09;向量单元的基本概念。具体流程如下&#xff1a; 讲解了SIMD的基本原理&#xff0c;如何通…

Flask flash() 消息示例

目录 安装 Flask 入门:Flask flash() 基本示例 进阶:使用 Flask-WTF Flash 登录结果消息 详解:get_flashed_messages() 详解:flash() 消息的完整生命周期 Flask 提供 flash() 用于向 用户传递临时消息,通常用于: • 表单提交成功或失败 • 用户登录、注册、退出提…

什么是 Vue 的自定义事件?如何触发和监听?

Vue 的自定义事件详解 什么是自定义事件&#xff1f; 在 Vue 中&#xff0c;自定义事件是组件之间通信的重要机制。自定义事件允许子组件向父组件发送消息&#xff0c;通常用于处理用户交互或异步操作的结果。这种机制使得组件间的通信更加灵活和解耦。 自定义事件的基本概念…