【Java设计模式-4】策略模式,消灭if/else迷宫的利器

news/2025/1/16 16:30:10/

各位Java编程小伙伴们!今天咱们要一起探索一个超级厉害的Java设计模式——策略模式,它就像是一把神奇的魔法剑,专门用来斩断那些让我们代码变得乱糟糟的if/else语句迷宫!
在这里插入图片描述

一、if/else的烦恼

在编程的奇妙世界里,我们常常会遇到这样的情况:要根据不同的条件执行不同的操作,这时候,if/else语句就像我们最先想到的武器,冲上去就一顿猛操作。比如说,你正在开发一个电商系统。在计算商品价格的时候,根据不同的用户类型,有着不同的折扣策略。比如,普通用户可能没有折扣,会员用户打9折,VIP用户打8折。要是按照常规的写法,那代码可能就会是这样的:

java">public class PriceCalculator {public double calculatePrice(double originalPrice, String userType) {if ("普通用户".equals(userType)) {return originalPrice;} else if ("会员用户".equals(userType)) {return originalPrice * 0.9;} else if ("VIP用户".equals(userType)) {return originalPrice * 0.8;} else {throw new IllegalArgumentException("未知的用户类型");}}
}

看着这段代码,一开始可能觉得还行呀,不就几个if/else嘛。但随着我们用户类型和折扣规则越来越多,这个方法就会变得越来越臃肿。每次新增一种用户,都得在这个长长的if/else链里添加新的判断分支,这不仅让代码变得又长又难看,而且维护起来简直是噩梦。要是不小心改错了一个判断条件或者顺序,那可就会出现各种奇怪的错误。
而且,这种写法的代码可读性也越来越差,就好比走进了一个错综复杂的迷宫,别人(包括未来的自己)要看懂这段代码得费好大的劲儿,得在那一堆if/else里来回穿梭,试图搞清楚每个分支到底是干嘛的。这可不行呀,我们的代码应该像一本清晰易懂的故事书,而不是让人头疼的谜题集呢。

二、策略模式来救场啦!

这时候,策略模式就闪亮登场啦!它的核心思想呢,就是把不同的算法或者策略封装成一个个独立的类,然后让这些类可以相互替换,这样就可以根据不同的情况灵活地选择使用哪个策略啦。

咱们就用上面电商系统的例子来看看怎么用策略模式改写吧。

第一步:定义策略接口

首先,我们要定义一个折扣策略的接口,所有具体的折扣策略都要实现这个接口。

java">public interface DiscountStrategy {double calculateDiscount(double originalPrice);
}

这个接口里只有一个方法calculateDiscount,用来根据原价计算出折扣后的价格。

第二步:实现具体策略类

接下来,我们就为不同的用户类型分别创建具体的折扣策略类。

普通用户折扣策略类:

java">public class NormalUserDiscountStrategy implements DiscountStrategy {@Overridepublic double calculateDiscount(double originalPrice) {return originalPrice;}
}

对于普通用户,就是直接返回原价,没有折扣嘛。

会员用户折扣策略类:

java">public class MemberUserDiscountStrategy implements DiscountStrategy {@Overridepublic double calculateDiscount(double originalPrice) {return originalPrice * 0.9;}
}

会员用户就按照9折来计算。

VIP用户折扣策略类:

java">public class VIPUserDiscountStrategy implements DiscountStrategy {@Overridepublic double calculateDiscount(double originalPrice) {return originalPrice * 0.8;}
}

VIP用户则是8折优惠。

第三步:使用策略模式的上下文类

最后,我们还需要一个上下文类,这个类负责持有一个具体的折扣策略对象,并且提供一个方法来执行折扣计算。

java">public class PriceCalculatorContext {private DiscountStrategy discountStrategy;public PriceCalculatorContext(DiscountStrategy discountStrategy) {this.discountStrategy = discountStrategy;}public double calculatePrice(double originalPrice) {return discountStrategy.calculateDiscount(originalPrice);}
}

现在,我们再来看看怎么使用这些类来计算商品价格吧。

java">public class Main {public static void main(String[] args) {double originalPrice = 100;// 普通用户价格计算PriceCalculatorContext normalContext = new PriceCalculatorContext(new NormalUserDiscountStrategy());double normalPrice = normalContext.calculatePrice(originalPrice);System.out.println("普通用户价格:" + normalPrice);// 会员用户价格计算PriceCalculatorContext memberContext = new PriceCalculatorContext(new MemberUserDiscountStrategy());double memberPrice = memberContext.calculatePrice(originalPrice);System.out.println("会员用户价格:" + memberPrice);// VIP用户价格计算PriceCalculatorContext vipContext = new PriceCalculatorContext(new VIPUserDiscountStrategy());double vipPrice = vipContext.calculatePrice(originalPrice);System.out.println("VIP用户价格:" + vipPrice);}
}

这样一看,代码是不是清晰多啦?每个折扣策略都有自己独立的类,而且如果以后要增加新的用户类型或者修改折扣规则,只需要再创建一个新的策略类或者修改对应的策略类就可以了,完全不需要在那个乱糟糟的if/else语句里折腾啦。

三、策略模式的应用场景

其实我们在SpringBoot实际开发中策略模式经常用到,我一说你就有共鸣了,以下是一些用到常见的地方:

1. 数据源配置(DataSource)

在Spring Boot中,当配置多个数据源时会涉及到策略模式的应用。例如,你可能有一个主数据源用于核心业务操作,还有一个从数据源用于读取数据等场景。

  • 策略接口javax.sql.DataSource 接口可以看作是一种策略接口,它定义了获取数据库连接等相关操作的规范。不同的数据库驱动厂商(如MySQL、PostgreSQL等)实现这个接口来提供各自特定的数据源实现,这就是不同的“策略”。

  • 具体策略实现:像 com.mysql.cj.jdbc.MysqlDataSource (MySQL数据库的数据源实现)、org.postgresql.ds.PGDataSource(PostgreSQL数据库的数据源实现)等都是具体的策略类,它们根据各自数据库的特性来实现 DataSource 接口中规定的获取连接等操作。

  • 上下文使用:在SpringBoot应用中,当你在配置文件中指定了要使用的数据库类型及相关连接参数后,Spring会根据这些信息自动装配相应的数据源实现(即选择合适的策略)。例如,通过 spring.datasource.urlspring.datasource.username 等配置项,Spring会解析并装配对应的数据源,就如同在策略模式的上下文环境中根据具体情况选用合适的策略一样。

2. 消息队列集成(Message Queuing)

SpringBoot可以方便地与多种消息队列进行集成,如RabbitMQ、Kafka等,这里也用到了策略模式

  • 策略接口:例如,org.springframework.amqp.core.AmqpTemplate (用于RabbitMQ的消息发送和接收操作的接口)和 org.springframework.kafka.core.KafkaTemplate(用于Kafka的消息发送和接收操作的接口)等类似接口可以看作是不同消息队列的策略接口,它们定义了各自与对应消息队列进行交互的操作规范。

  • 具体策略实现:像 org.springframework.amqp.rabbit.core.RabbitTemplate(RabbitMQ的具体实现)、org.springframework.kafka.core.KafkaTemplate(本身就是Kafka的具体实现)等都是不同的消息队列的具体策略类,它们根据各自消息队列的特性来实现相应接口规定的操作。

  • 上下文使用:在应用的配置文件中指定要集成的消息队列类型及相关参数(如 spring.rabbitmq.host 等对于RabbitMQ的配置,spring.kafka.bootstrap.servers 等对于Kafka的配置),Spring Boot会根据这些信息自动装配相应的消息队列实现(即选择合适的策略),并在需要进行消息发送、接收等操作的地方应用该策略。

这些只是SpringBoot中应用策略模式的一部分例子,实际上在很多涉及到多种可选方案、根据不同条件选择不同处理方式的场景下,都可能会运用到策略模式来使得代码更加清晰、可维护和可扩展。

四、总结

策略模式真的是一个非常实用的设计模式,帮我们把那些杂乱无章的if/else语句统统消灭掉,让我们的代码更加清晰、可维护、可扩展。以后在遇到类似根据不同情况选择不同算法或者行为的问题时,大家可别忘了试试策略模式哦。

好啦,今天关于策略模式的分享就到这里啦,希望小伙伴们都能在自己的代码世界里用好这个神奇的模式,写出更加优秀的程序哟!


上述内容只代表个人观点,不代表啥组织。本人致力于宣扬正能量,若因浏览出啥岔子,纯属无心之举,我马上就改正,可千万别上纲上线哈,愿大家开心每一天,peace & love~😜


http://www.ppmy.cn/news/1563656.html

相关文章

《Java核心技术II》实现服务器

实现服务器 这节实现简单服务器,可以向客户端发送信息。 服务器套接字 ServerSocket用于建立套接字 var s new ServerSocket(8189); 建立一个监听端口8189的服务器。 Socket incoming s.accept(); 此对象可以得到输入流和输出流。 InputStream inStream incomin…

LabVIEW光流算法的应用

该VI展示了如何使用NI Vision Development Module中的光流算法来计算图像序列中像素的运动矢量。通过该方法,可以实现目标跟踪、运动检测等功能,适用于视频处理、机器人视觉和监控领域。程序采用模块化设计,包含图像输入、算法处理、结果展示…

【DevOps】Pipeline功能语法

Pipeline功能语法 一、options全局配置 # 在pipeline下一层添加即可 options {timestamps () // 打印日志时间timeout(time: 10, unit: MINUTES) // 设置流水线执行超时时间 天(DAYS) 时(HOURS) 分钟(MINUTES) 秒(SECONDS)}二、tools全局工具 tools { maven "M…

redis监控会不会统计lua里面执行的命令次数

问题:redis lua里面执行的命令会不会计算到监控的qps中 假设: lua 脚本中对数据库操作了1w次。 执行一次lua 脚本, 虽然内部对数据库操作了1w次, 但是从redis 监控上看只是执行了一次lua脚本, lua内部对数据库的1w次不…

小程序如何引入腾讯位置服务

小程序如何引入腾讯位置服务 1.添加服务 登录 微信公众平台 注意:小程序要企业版的 第三方服务 -> 服务 -> 开发者资源 -> 开通腾讯位置服务 在设置 -> 第三方设置 中可以看到开通的服务,如果没有就在插件管理中添加插件 2.腾讯位置服务…

(STM32笔记)十二、DMA的基础知识与用法 第二部分

我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话,用的也是这个板子和教程。 DMA的基础知识与用法 二、DMA传输设置1、数据来源与数据去向外设到存储器存储器到外设存储器到存储器 2、每次传输大小3、传…

网络层协议-----IP协议

目录 1.认识IP地址 2.IP地址的分类 3.子网划分 4.公网IP和私网IP 5.IP协议 6.如何解决IP地址不够用 1.认识IP地址 IP 地址(Internet Protocol Address)是指互联网协议地址。 它是分配给连接到互联网的设备(如计算机、服务器、智能手机…

lqb.key按键全套

#include "stc15.h" #define FOSC 11059200L //#define T1MS (65536-FOSC/1000) //1T模式 #define T1MS (65536-FOSC/12/1000) //12T模式typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32;#define LY 1 //…