策略模式:动态切换算法的设计智慧

news2025/4/25 20:06:14

策略模式:动态切换算法的设计智慧

一、模式核心:定义一系列算法并可相互替换

在软件开发中,常常会遇到需要根据不同情况选择不同算法的场景。例如,在电商系统中,根据不同的促销活动(如满减、折扣、赠品)来计算商品的最终价格。

策略模式(Strategy Pattern) 定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端,核心解决:

  • 算法切换:在运行时根据不同条件动态选择合适的算法。
  • 代码复用:将不同算法封装成独立的策略类,提高代码的复用性。
  • 可维护性:算法的修改和扩展不会影响到使用算法的客户端。

核心思想与 UML 类图

策略模式包含策略接口(Strategy)、具体策略类(Concrete Strategy)和上下文类(Context)。上下文类持有一个策略接口的引用,客户端可以在运行时动态设置上下文类所使用的具体策略。

PlantUML Diagram

二、核心实现:电商促销策略

1. 定义策略接口(促销策略)

public interface PromotionStrategy {
    double calculatePrice(double originalPrice); // 计算促销后的价格
}

2. 实现具体策略类

满减策略
public class FullReductionStrategy implements PromotionStrategy {
    private double fullAmount;
    private double reductionAmount;

    public FullReductionStrategy(double fullAmount, double reductionAmount) {
        this.fullAmount = fullAmount;
        this.reductionAmount = reductionAmount;
    }

    @Override
    public double calculatePrice(double originalPrice) {
        if (originalPrice >= fullAmount) {
            return originalPrice - reductionAmount;
        }
        return originalPrice;
    }
}
折扣策略
public class DiscountStrategy implements PromotionStrategy {
    private double discountRate;

    public DiscountStrategy(double discountRate) {
        this.discountRate = discountRate;
    }

    @Override
    public double calculatePrice(double originalPrice) {
        return originalPrice * discountRate;
    }
}
无促销策略
public class NoPromotionStrategy implements PromotionStrategy {
    @Override
    public double calculatePrice(double originalPrice) {
        return originalPrice;
    }
}

3. 实现上下文类(购物车)

public class ShoppingCart {
    private PromotionStrategy promotionStrategy;

    public void setPromotionStrategy(PromotionStrategy promotionStrategy) {
        this.promotionStrategy = promotionStrategy;
    }

    public double checkout(double originalPrice) {
        if (promotionStrategy == null) {
            promotionStrategy = new NoPromotionStrategy();
        }
        return promotionStrategy.calculatePrice(originalPrice);
    }
}

4. 客户端使用策略模式

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

        // 无促销活动
        cart.setPromotionStrategy(new NoPromotionStrategy());
        double price1 = cart.checkout(100);
        System.out.println("无促销活动,最终价格:" + price1);

        // 满 100 减 20
        cart.setPromotionStrategy(new FullReductionStrategy(100, 20));
        double price2 = cart.checkout(120);
        System.out.println("满 100 减 20,最终价格:" + price2);

        // 打 8 折
        cart.setPromotionStrategy(new DiscountStrategy(0.8));
        double price3 = cart.checkout(150);
        System.out.println("打 8 折,最终价格:" + price3);
    }
}

输出结果

无促销活动,最终价格:100.0
满 100 减 20,最终价格:100.0
打 8 折,最终价格:120.0

三、进阶:策略模式与工厂模式结合

在实际应用中,为了更方便地管理和获取策略对象,可以将策略模式与工厂模式结合。

public class PromotionStrategyFactory {
    public static PromotionStrategy getStrategy(String strategyType) {
        switch (strategyType) {
            case "full_reduction":
                return new FullReductionStrategy(100, 20);
            case "discount":
                return new DiscountStrategy(0.8);
            default:
                return new NoPromotionStrategy();
        }
    }
}

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

        // 使用工厂获取策略
        cart.setPromotionStrategy(PromotionStrategyFactory.getStrategy("full_reduction"));
        double price = cart.checkout(120);
        System.out.println("使用工厂获取策略,最终价格:" + price);
    }
}

四、框架与源码中的策略模式实践

1. Java 的 Comparator 接口

在 Java 中,Comparator 接口就是策略模式的典型应用。通过实现不同的 Comparator 接口,可以为不同的排序需求提供不同的排序策略。

import java.util.Arrays;
import java.util.Comparator;

public class ComparatorDemo {
    public static void main(String[] args) {
        Integer[] numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

        // 升序排序
        Arrays.sort(numbers, Comparator.naturalOrder());
        System.out.println("升序排序:" + Arrays.toString(numbers));

        // 降序排序
        Arrays.sort(numbers, Comparator.reverseOrder());
        System.out.println("降序排序:" + Arrays.toString(numbers));
    }
}

2. Spring 的 ResourceLoader

Spring 框架中的 ResourceLoader 接口及其实现类也是策略模式的应用。不同的 ResourceLoader 实现类可以根据不同的资源类型(如文件、类路径资源、URL 资源等)提供不同的资源加载策略。

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringResourceLoaderDemo {
    public static void main(String[] args) {
        ResourceLoader resourceLoader = new ClassPathXmlApplicationContext();
        Resource resource = resourceLoader.getResource("application.properties");
        System.out.println("资源是否存在:" + resource.exists());
    }
}

五、避坑指南:正确使用策略模式的 3 个要点

1. 避免策略类过多

当策略类过多时,会导致类的数量急剧增加,增加系统的复杂度。可以考虑将一些相似的策略类进行合并,或者使用策略枚举来简化策略的管理。

2. 策略的选择逻辑

在使用策略模式时,需要考虑如何选择合适的策略。可以将策略的选择逻辑封装在上下文类中,或者使用工厂模式来管理策略的创建和选择。

3. 策略的可维护性

每个策略类都应该保持独立和单一职责,避免在策略类中添加过多的业务逻辑。同时,要为策略类提供清晰的文档和注释,方便后续的维护和扩展。

六、总结:何时该用策略模式?

适用场景核心特征典型案例
算法的动态切换需要根据不同条件在运行时动态选择算法电商促销、排序算法选择
代码复用和可维护性多个算法具有相似的接口,需要提高代码的复用性和可维护性图形绘制算法、加密算法
避免使用大量条件语句避免在代码中使用大量的 if-elseswitch 语句游戏中的角色技能、状态机

策略模式通过将算法封装成独立的策略类,实现了算法的动态切换和代码的复用,是一种非常实用的设计模式。下一篇我们将深入探讨模板方法模式,解析如何定义算法骨架并延迟实现细节,敬请期待!

扩展思考:策略模式 vs 状态模式

类型核心思想适用场景
策略模式定义一系列算法并可相互替换,客户端主动选择策略算法的动态切换、代码复用
状态模式对象的行为依赖于其状态,状态的改变会导致行为的改变对象的行为随状态变化、状态机

理解这种差异,能帮助我们在不同场景下选择更合适的设计模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342668.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp微信小程序:WIFI设备配网之TCP/UDP开发AP配网

一、AP配网技术原理 1.1 配网模式选择 AP配网(SoftAP模式)是IoT设备配网成功率最高的方案之一 1、其核心原理: ​​设备端​​:启动AP模式(如SSID格式YC3000_XXXX,默认IP192.168.4.1)​​手…

离线-DataX

基本介绍 DataX 是阿里云 DataWorks数据集成的开源版本,在阿里巴巴集团内被广泛使用的离线数据同步工具/平台,它是一个异构数据源离线同步工具,致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源…

第5课:对象与类——JS的“信息收纳盒”

生活从不会亏待每一个努力向上的人,愿你带着满腔热忱,无畏前行,用汗水书写青春的华章,用拼搏铸就人生的辉煌,今日的每一份付出,都将是未来成功的基石! 欢迎来到「JavaScript 魔法学院」第 5 课…

xshell 登录验证失败解决

产生原因:检查防火墙、selinux 、网络模式、对外是否能ping外网 systemctl status firewalld cat /etc/selinux/config #disabled ping 223.5.5.5 ping 8.8.8.8 ping www.baidu.com 一、检查网络连接 确认虚拟机是否在线: 首先,确保虚…

AI 赋能 3D 创作!Tripo3D 全功能深度解析与实操教程

大家好,欢迎来到本期科技工具分享! 今天要给大家带来一款革命性的 AI 3D 模型生成平台 ——Tripo3D。 无论你是游戏开发者、设计师,还是 3D 建模爱好者,只要想降低创作门槛、提升效率,这款工具都值得深入了解。 接下…

AI书籍大模型微调-基于亮数据获取垂直数据集

大模型的开源,使得每位小伙伴都能获得AI的加持,包括你可以通过AIGC完成工作总结,图片生成等。这种加持是通用性的,并不会对个人的工作带来定制的影响,因此各个行业都出现了垂直领域大模型。 垂直大模型是如何训练出来…

Kafka命令行的使用/Spark-Streaming核心编程(二)

Kafka命令行的使用 创建topic kafka-topics.sh --create --zookeeper node01:2181,node02:2181,node03:2181 --topic test1 --partitions 3 --replication-factor 3 分区数量,副本数量,都是必须的。 数据的形式: 主题名称-分区编号。 在…

2020-06-23 暑期学习日更计划(机器学习入门之路(资源汇总)+概率论)

机器学习入门 前言 说实话,机器学习想学好真心不易,很多时候都感觉自己学得云里雾里。以前一段时间自己为了完成毕业设计,在机器学习的理论部分并没有深究,仅仅通过TensorFlow框架力求快速实现模型。现在来看,很多时候…

SQL 时间转换的CONVERT()函数应用说明

目录 1.常用查询使用的几个 2.其他总结 1.常用查询使用的几个 SELECT CONVERT(VARCHAR, GETDATE(), 112) SELECT CONVERT(VARCHAR, GETDATE(), 113)SELECT CONVERT(VARCHAR, GETDATE()-1, 112) SELECT CONVERT(VARCHAR, GETDATE()-1, 113) 2.其他总结 SELECT CONVERT(VARCHA…

SystemWeaver详解:从入门到精通的深度实战指南

SystemWeaver详解:从入门到精通的深度实战指南 文章目录 SystemWeaver详解:从入门到精通的深度实战指南一、SystemWeaver环境搭建与基础配置1.1 多平台安装全流程 二、新手必学的十大核心操作2.1 项目创建全流程2.2 建模工具箱深度解析 三、需求工程与系…

windows中kafka4.0集群搭建

参考文献 Apache Kafka windows启动kafka4.0(不再需要zookeeper)_kafka压缩包-CSDN博客 Kafka 4.0 KRaft集群部署_kafka4.0集群部署-CSDN博客 正文 注意jdk需要17版本以上的 修改D:\software\kafka_2.13-4.0.0\node1\config\server.properties配置文…

【JavaWeb后端开发04】java操作数据库(JDBC + Mybatis+ yml格式)详解

文章目录 1. 前言2. JDBC2.1 介绍2.2 入门程序2.2.1 DataGrip2.2.2 在IDEA执行sql语句 2.3 查询数据案例2.3.1 需求2.3.2 准备工作2.3.3 AI代码实现2.3.4 代码剖析2.3.4.1 ResultSet2.3.4.2 预编译SQL2.3.4.2.1 SQL注入2.3.4.2.2 SQL注入解决2.3.4.2.3 性能更高 2.4 增删改数据…

postman 删除注销账号

一、删除账号 1.右上角找到 头像,view profile https://123456-6586950.postman.co/settings/me/account 二、找回账号 1.查看日志所在位置 三、postman更新后只剩下history 在 Postman 中,如果你发现更新后只剩下 History(历史记录&…

Java发展史及版本详细说明

Java发展史及版本详细说明 1. Java 1.0(1996年1月23日) 核心功能: 首个正式版本,支持面向对象编程、垃圾回收、网络编程。包含基础类库(java.lang、java.io、java.awt)。支持Applet(浏览器嵌入…

React 5 种组件提取思路与实践

在开发时,经常遇到一些高度重复但略有差异的 UI 模式,此时我们当然会把组件提取出去,但是组件提取的方式有很多,怎么根据不同场景选取合适的方式呢?尤其时在复杂的业务场景中,组件提取的思路影响着着代码的可维护性、可读性以及扩展性。本文将以一个[详情]组件为例,探讨…

[java八股文][Java基础面试篇]I/O

Java怎么实现网络IO高并发编程? 可以用 Java NIO ,是一种同步非阻塞的I/O模型,也是I/O多路复用的基础。 传统的BIO里面socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据&#xf…

数据结构-冒泡排序(Python)

目录 冒泡排序算法思想 冒泡排序算法步骤 冒泡排序代码实现 冒泡排序算法分析 冒泡排序算法思想 冒泡排序(Bubble Sort)基本思想: 经过多次迭代,通过相邻元素之间的比较与交换,使值较小的元素逐步从后面移到前面…

深入理解React高阶组件(HOC):原理、实现与应用实践

组件复用的艺术 在React应用开发中,随着项目规模的增长,组件逻辑的复用变得越来越重要。传统的组件复用方式如组件组合和props传递在某些复杂场景下显得力不从心。高阶组件(Higher-Order Component,简称HOC)作为React中…

Neo4j社区版在win下安装教程(非docker环境)

要在 Windows 10 上安装 Neo4j 社区版数据库并且不使用 Docker Desktop,你可以按照以下步骤操作: 1. 安装 Java Development Kit (JDK) Neo4j 需要 Java 运行环境。推荐安装 JDK 17 或 JDK 11(请根据你下载的 Neo4j 版本查看具体的兼容性要…

如何在 Odoo 18 中配置自动化动作

如何在 Odoo 18 中配置自动化动作 Odoo是一款多功能的业务管理平台,旨在帮助各种规模的企业更高效地处理日常运营。凭借其涵盖销售、库存、客户关系管理(CRM)、会计和人力资源等领域的多样化模块,Odoo 简化了业务流程&#xff0c…