深入理解 Java 设计模式之策略模式

news2025/1/12 6:26:02

一、引言

在 Java 编程的世界里,设计模式就如同建筑师手中的蓝图,能够帮助我们构建出更加健壮、灵活且易于维护的代码结构。而策略模式作为一种经典的行为型设计模式,在诸多实际开发场景中都发挥着至关重要的作用。它能够让算法的定义与使用分离,使得程序可以在运行时根据不同的需求灵活切换算法,就像我们出行时可以根据不同的路况选择步行、骑车、开车或乘坐公共交通等不同策略一样。本文将深入探讨 Java 设计模式中的策略模式,剖析其原理、结构、应用场景以及实际代码示例,希望能帮助读者深入理解并熟练运用这一强大的设计工具。

二、策略模式的定义与概念

策略模式(Strategy Pattern),也叫政策模式(Policy Pattern),它定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。此模式让算法的变化独立于使用算法的客户,通过这种方式,算法的切换变得更加灵活,不会影响到客户端代码的稳定性。简单来说,就是把一个类中经常改变或者可能改变的部分提取出来,作为一个接口的实现类,而客户端代码则通过这个接口来调用相应的算法,而不用关心具体算法的实现细节。

三、策略模式的结构剖析

策略模式主要包含以下几个角色:

抽象策略角色(Strategy)

  • 通常是一个接口或抽象类,定义了一个公共的策略方法,用于声明算法的规范。例如,在一个电商系统的折扣计算场景中,它可以定义一个calculateDiscount方法,用来计算订单的折扣金额。
  • 代码示例:
public interface DiscountStrategy {

double calculateDiscount(double totalPrice);

}

具体策略角色(ConcreteStrategy)

  • 实现了抽象策略角色所定义的接口或抽象类,具体实现了算法的细节。继续以电商系统为例,可能有NormalDiscountStrategy(无折扣策略,直接返回 0)、MemberDiscountStrategy(根据会员等级计算折扣)、SeasonalDiscountStrategy(根据季节推出不同折扣)等多个具体策略类。
  • 代码示例:
public class NormalDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double totalPrice) {

return 0;

}

}

public class MemberDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double totalPrice) {

// 假设根据会员等级打9折

return totalPrice * 0.1;

}

}

public class SeasonalDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double totalPrice) {

// 假设旺季无折扣,淡季打8折

return totalPrice * (isOffSeason()? 0.2 : 0);

}

private boolean isOffSeason() {

// 简单模拟判断是否淡季,实际应用中需更精准判断

return false;

}

}

环境角色(Context)

  • 持有一个抽象策略角色的引用,用于在运行时调用具体的策略算法。它维护了对策略对象的引用,并且负责将客户端的请求转发给对应的策略对象。在电商系统中,它可以是订单类,负责根据当前的业务规则选择合适的折扣策略并计算最终价格。
  • 代码示例:
public class Order {

private DiscountStrategy discountStrategy;

public Order(DiscountStrategy discountStrategy) {

this.discountStrategy = discountStrategy;

}

public double calculateFinalPrice(double totalPrice) {

double discount = discountStrategy.calculateDiscount(totalPrice);

return totalPrice - discount;

}

}

通过这样的结构设计,各个部分职责分明。当业务需求发生变化,比如新增一种折扣策略时,只需要实现抽象策略接口,编写具体的策略类,而无需大规模改动环境角色和客户端代码,遵循了开闭原则。

四、策略模式的优势剖析

算法的可替换性

  • 由于策略模式将算法封装在独立的策略类中,当需要更换算法时,只需切换具体策略类的实例,而不会影响到客户端代码的其他部分。例如在游戏开发中,不同角色的移动行为可以定义为不同的策略,当设计新角色或修改角色移动方式时,轻松替换移动策略类即可,不会牵一发而动全身。

易于扩展

  • 新增算法非常方便,只需实现抽象策略接口,创建新的具体策略类。比如电商系统要增加新的促销折扣方式,按照策略模式的结构,简单几步就能集成到现有系统中,系统的可扩展性得到极大提升。

代码的复用性

  • 多个不同的业务场景可能用到相同的算法,将算法抽象为策略类后,可以在不同的环境角色中复用。以数据排序为例,无论是对用户列表排序,还是对商品列表排序,如果有多种排序算法(如冒泡排序、快速排序等)作为策略,这些排序算法类可以在不同的数据处理模块中重复利用。

符合开闭原则

  • 对扩展开放,对修改关闭。当业务需求变化导致算法调整时,通过新增具体策略类实现扩展,而无需修改原有稳定运行的代码,使得系统的维护成本降低,稳定性增强。

五、策略模式的应用场景实例

电商系统的促销策略

  • 如前文所述,电商平台在不同时期、针对不同用户群体有多样的促销活动。在 “双 11”“618” 等大促期间,全品类商品打折;对于新注册用户给予首单优惠;特定品牌或品类商品在淡季促销等。通过策略模式,将各种促销折扣算法封装,订单结算模块根据当前活动规则选择合适的折扣策略,轻松应对复杂多变的促销场景,保证系统的灵活性与可维护性。

游戏角色行为控制

  • 在角色扮演游戏中,不同角色具有不同的攻击、防御、移动方式。战士角色攻击可能是近身强力一击,法师角色则是远程魔法攻击;刺客角色移动敏捷,靠瞬移或高速奔跑接近敌人。将这些不同的行为定义为策略,角色类作为环境角色持有相应行为策略的引用,根据游戏进程和玩家操作切换策略,让游戏的设计更加模块化,方便后续新增角色或调整角色技能。

文件压缩工具

  • 一款支持多种格式压缩和解压缩的工具,如支持 ZIP、RAR、7Z 等格式。每种格式的压缩和解压缩算法不同,将这些算法封装成具体策略类,实现共同的压缩和解压缩接口(抽象策略),工具的主程序(环境角色)根据用户选择的文件格式调用对应的算法策略,实现了对不同格式文件的高效处理,同时方便后续添加新的压缩格式支持。

六、策略模式与其他设计模式的对比与关联

与工厂模式的对比

  • 工厂模式主要关注对象的创建过程,它根据不同的条件创建不同类型的对象,将对象的创建和使用分离。而策略模式侧重于算法的封装与切换,在运行时根据需求选择不同的算法实现。例如,在一个图形绘制系统中,工厂模式负责创建不同形状(圆形、矩形、三角形等)的图形对象,而策略模式可以用于处理不同图形的绘制算法(如使用不同的线条样式、填充颜色策略等)。二者虽然侧重点不同,但都有助于解耦代码,提高系统的灵活性。

与状态模式的关联

  • 状态模式中,对象的行为取决于它的内部状态,不同状态下有不同的行为表现,这与策略模式有相似之处,都是通过多态来实现不同行为的切换。但状态模式的重点在于状态的变迁,一个对象的状态改变会导致其行为自动切换,且状态之间可能存在转换逻辑;而策略模式更强调算法的平等替换,算法之间通常没有这种内在的状态转换关系。在一个订单处理系统中,订单的状态(已下单、已支付、已发货等)不同,处理流程不同,这适合用状态模式;而订单计算折扣时采用不同的折扣算法,就是策略模式的应用场景,二者可以结合使用,比如在不同订单状态下应用不同的折扣策略。

七、策略模式在 Java 核心库与开源框架中的应用

Java 集合框架中的排序算法

  • 在java.util.Collections类中,sort方法用于对列表进行排序。它采用了策略模式,允许用户传入自定义的比较器(Comparator接口,相当于抽象策略)来定义排序规则。如果要按照对象的某个特定属性排序,只需实现Comparator接口,编写具体的比较逻辑(具体策略),就可以灵活地对集合中的元素进行排序,这充分体现了策略模式的算法可替换性优势。

Spring 框架中的事务管理策略

  • Spring 在事务管理方面运用了策略模式。它定义了事务策略的抽象接口,不同的事务实现(如基于 JDBC 的事务、基于 JTA 的事务等)作为具体策略类。当应用配置事务时,Spring 容器根据配置信息选择合适的事务策略,为业务代码提供统一的事务支持,使得在不同的运行环境和需求下都能高效管理事务,保证数据的一致性和完整性。

八、策略模式的实践注意事项

策略类的数量控制

  • 虽然策略模式方便扩展,但如果过度使用,导致策略类数量过多,会使代码变得复杂难以维护。在设计时,要综合考虑业务的复杂性和未来的可扩展性,避免为了微小的算法差异就创建独立的策略类。例如,一些简单的数值计算调整,如果可以通过参数配置解决,就不一定非要新增策略类。

策略类的共享与复用

  • 要注意识别可以复用的策略类,避免重复创建功能相同的策略。可以将一些通用的策略类提取到工具类库中,供不同的模块使用。比如常见的字符串加密解密策略,在多个涉及用户数据安全的模块中都可能用到,统一管理复用能减少代码冗余。

与依赖注入的结合

  • 在实际项目中,通常将策略类通过依赖注入的方式注入到环境角色中,这样可以更好地解耦策略类与环境类,方便在不同场景下切换策略。例如使用 Spring 的依赖注入框架,将折扣策略类注入到订单服务类中,在配置文件或注解中灵活指定要使用的策略,提升代码的灵活性与可测试性。

九、总结

策略模式作为 Java 设计模式中的一颗璀璨明珠,以其灵活的算法封装、强大的可扩展性、良好的代码复用性以及对开闭原则的完美遵循,为 Java 开发者提供了解决复杂业务逻辑的有力武器。通过深入理解策略模式的定义、结构、优势、应用场景以及在实践中的注意事项,我们能够在面对多变的业务需求时,游刃有余地设计出高质量、易维护的代码架构。无论是构建大型企业级应用,还是开发小型工具类项目,策略模式都有着广泛的用武之地,希望本文能成为读者掌握这一设计模式的坚实基石,助力大家在 Java 编程之路上越走越远。

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

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

相关文章

边缘计算网关解决车间数据采集的关键问题

随着工业4.0和智能制造的快速发展,车间数据采集与分析已成为提升生产效率、保证产品质量、优化加工过程的关键环节。传统的数据采集方式,如中心化的数据处理模式,在面对海量数据、实时性要求高的工业场景时,往往显得力不从心。边缘…

C语言之assert断言

1.assert的使用形式 #include <assert.h>assert (表达式); (1)在c语言中&#xff0c;宏&#xff0c;是一种预处理指令。assert(表示式) 就是一个宏 (2)表达式必须是一个能计算出真或假的布尔条件&#xff0c;它通常意味着 该表达式是一个能够返回整数值的表达式&#…

【Linux】正则表达式

正则表达式是一种可供Linux工具过滤文本的自定义模板&#xff0c;Linux工具&#xff08;如sed、gawk&#xff09;会在读取数据时使用正则表达式对数据进行模式匹配。 正则表达式使用元字符来描述数据流中的一个或多个字符。它是由正则表达式引擎实现的。正则表达式引擎是一种底…

hutool糊涂工具通过注解设置excel宽度

import java.lang.annotation.*;Documented Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public interface ExcelStyle {int width() default 0; }/*** 聊天记录*/ Data public class DialogContentInfo {/**…

全面教程:Nacos 2.4.2 启用鉴权与 MySQL 数据存储配置

全面教程&#xff1a;Nacos 2.4.2 启用鉴权与 MySQL 数据存储配置 1. 配置 Nacos 开启鉴权功能 1.1 修改 application.properties 配置文件 在 Nacos 2.4.2 中&#xff0c;开启鉴权功能需要修改 conf/application.properties 文件。按照以下方式配置&#xff1a; # 开启鉴权…

【学习】CMMM智能制造能力成熟度评估的重要性

CMMM认证通过对企业当前生产状态的全面评估&#xff0c;能够精准地确定其智能化生产的程度&#xff0c;并将企业的智能化生产水平划分为五个等级&#xff0c;包括初始级、已定义级、以管理级、卓越级和顶级。这种等级划分使得不同类型的企业能够根据自身实际情况&#xff0c;选…

特制一个自己的UI库,只用CSS、图标、emoji图 第二版

图&#xff1a; 代码&#xff1a; index.html <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>M…

Y3编辑器地图教程:ORPG教程、防守图教程

文章目录 Part1&#xff1a;ORPG教程一、章节人物选择1.1 Logo与界面动画1.2 章节选择与投票1.2.1 设计章节选择完毕后的操作1.2.2 玩家投票统计 1.3 多样化的人物选择系统1.3.1 异步模型显示1.3.2 双击和键盘选人1.3.3 UI选人 1.4 简易存档 二、对话与任务系统2.1对话UI与触发…

Ubuntu问题 -- 硬盘存储不够了, 如何挂载一个新的硬盘上去, 图文简单明了, 已操作成功

需求 我现在有一个ubuntu22.04操作系统的服务器, 但是当前硬盘不够用了, 我买了一个1T的SSD固态硬盘, 且已经安装在服务器上了, 我需要将这个硬盘挂载到当前ubuntu的某个目录上 开始 1. 确认新硬盘是否被系统识别 打开终端&#xff0c;输入以下命令查看系统识别到的硬盘&…

吴恩达 提示词工程 课程笔记

一、Introduction 二、Guidelines Principle1: 清晰&#xff08;不一定是简短的&#xff09;而具体的指令 Tactic1: 使用分隔符 Triple quotes: “”" Triple backticks: Triple dashes: — Angle brackets:< > XML tags: < tag></ tag> Tactic2:…

网络安全设备主要有什么

网络安全设备指的肯定是硬件设备了&#xff0c;国内卖安全硬件的没几家&#xff0c;天融信&#xff0c;启明星辰&#xff0c;绿盟&#xff0c;深信服&#xff0c;就这四家卖的比较齐全吧&#xff0c;上它们官网看一下&#xff0c;就知道市面上主要的网络安全设备有哪些了。分类…

【C++补充】第一弹---位图技术揭秘:内存优化与快速访问

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1 位图 1.1 位图相关面试题 1.2 位图的设计及实现 1.3 C库中的位图 bitset 1.4 位图的模拟实现 1.5 位图的优缺点 1.6 位图相关考察题目 1 …

解决nginx多层代理后应用部署后访问发现css、js、图片等样式加载失败

一般是采用前后端分离部署方式&#xff0c;被上一层ng代理后&#xff0c;通过域名访问报错&#xff0c;例如&#xff1a;sqx.com.cn/应用代理路径。 修改nginx配置&#xff0c;配置前端页面的路径&#xff1a; location / {proxy_pass http://前端页面所在服务器的IP:PORT;pro…

第34天:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法

时间轴&#xff1a; Java反射相关类图解&#xff1a; 反射&#xff1a; 1、什么是 Java 反射 参考&#xff1a; https://xz.aliyun.com/t/9117 Java 提供了一套反射 API &#xff0c;该 API 由 Class 类与 java.lang.reflect 类库组成。 该类库包含了 Field 、 Me…

Qt天气预报系统获取天气数据

Qt天气预报系统获取天气数据 1、获取天气数据1.1添加天气类头文件1.2定义今天和未来几天天气数据类1.3定义一个解析JSON数据的函数1.4在mainwindow中添加weatherData.h1.5创建今天天气数据和未来几天天气数据对象1.6添加parseJson定义1.7把解析JSON数据添加进去1.8添加错误1.9解…

SQL SERVER 2016 创建用户。

一、在实例中创建用户 二、在数据库中创建用户分配表格权限. 三、也可以在表格属性中分配用户权限 四、搜索对象中可以选择表、视图等等内容.

汽车信息安全 -- S32K1如何更新BOOT_MAC

目录 1.安全启动模式回顾 2.为什么要讨论BOOT_MAC 3.S32K1如何更新? 1.安全启动模式回顾 之前提到过,S32K1系列提供了Crypto Service Engine硬件加密模块(简称CSEc),大家可以通过该芯片系统寄存器SDID.FEATURES(System Device Identification Register)来判断自己的片子…

7 分布式定时任务调度框架

先简单介绍下分布式定时任务调度框架的使用场景和功能和架构&#xff0c;然后再介绍世面上常见的产品 我们在大型的复杂的系统下&#xff0c;会有大量的跑批&#xff0c;定时任务的功能&#xff0c;如果在独立的子项目中单独去处理这些任务&#xff0c;随着业务的复杂度的提高…

智慧城市应急指挥中心系统平台建设方案

建设背景与目标 智慧城市应急指挥中心系统平台的建设&#xff0c;源于对城市管理精细化、智能化的迫切需求。平台旨在通过整合各方资源&#xff0c;实现应急事件的快速响应与高效处置&#xff0c;提升城市安全管理水平。 前端设计与信息采集 前端设计注重立体化、全方位信息…

Playwright实战:Locators(定位器)指南

Locators Locators是Playwright自动等待和重试能力的核心部分。简而言之&#xff0c;Locators代表了一种随时在页面上查找元素的方法。 快速指南 这些是推荐的内置定位器。 page.getbyrole()通过显式和隐式可访问性属性进行定位。page.get_by_text()用于按文本内容定位。pa…