Java23种设计模式之策略模式【普通写法以及spring中的写法】

article/2025/10/4 16:05:01

目录

  • 设计模式简介
  • 策略模式的简介
  • 普通写法案例
  • 基于注解式改造的案例
  • 优缺点
  • 策略模式的使用场景

设计模式简介

将设计者的思维融入大家的学习和工作中,更高层次的思考!
• 创建型模式:
– 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。

• 结构型模式:
– 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

• 行为型模式:
– 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

本次文章介绍的是策略模式

策略模式的简介

策略设计模式是一种行为型设计模式,它允许在运行时根据不同的情况选择算法或策略。
这种模式提供了一种灵活的方法来改变算法或策略,而无需修改代码。

定义一系列算法,封装每个算法,并使他们可以互换,不同的策略可以让算法独立于使用它们的客户而变化。 以上定义来自设计模式之美

这个模式主要由三个角色组成:Context(上下文)、Strategy(策略)和 ConcreteStrategy(具体策略)。

  • Context(上下文):它是包含了一个Strategy接口的引用,用来调用具体策略的方法。上下文可以根据需要改变策略。
  • Strategy(策略):它是一个接口,定义了一个算法族,这些算法可以相互替换。具体策略可以实现这个接口,这样就可以提供不同的实现方式。
  • ConcreteStrategy(具体策略):它是具体的策略实现,实现了策略接口中定义的方法。每个具体策略都实现了一种算法。

普通写法案例

假设有一个在线支付系统,支持多种支付方式,如支付宝、微信支付、银联支付等。为了方便用户选择支付方式,系统提供了一个支付方式选择页面,用户可以在该页面选择一种支付方式并进行支付。

为了实现这个功能,可以采用策略设计模式。具体实现过程如下:
1、定义支付策略接口

/*** 策略接口(对扩展开放)*/
public interface PaymentStrategy {// 支付void pay(double amount);// 或者其他业务...
}

2、定义支付方式的具体实现类,如支付宝、微信支付和银联支付

/*** 支付宝支付策略*/
public class AliPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {// 调用支付宝支付接口// ...System.out.println("使用支付宝支付:" + amount + "元");}
}
/*** 微信支付策略*/
public class WeChatPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {// 调用微信支付接口// ...System.out.println("使用微信支付:" + amount + "元");}
}
/*** 银联卡支付策略*/
public class UnionPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {// 调用银联支付接口// ...System.out.println("使用银联支付:" + amount + "元");}
}

3、接下来定义策略上下文

/*** 支付方式上下文*/
public class PaymentStrategyContext {/*** 策略集合*/private Map<String, PaymentStrategy> paymentStrategyMap = new HashMap<>();/*** 通过无参构造将支付所有策略进行注入*/public PaymentStrategyContext() {paymentStrategyMap.put("zfb", new AliPayStrategy());paymentStrategyMap.put("wx", new WeChatPayStrategy());paymentStrategyMap.put("ylk", new UnionPayStrategy());}/*** 返回实际处理对象* @param strategy 支付方式* @return 实际处理对象*/public PaymentStrategy getStrategy(String strategy){return paymentStrategyMap.get(strategy);}}

4、使用策略

public class TestPay {public static void main(String[] args) {// 创建支付策略上下文对象PaymentStrategyContext paymentPage = new PaymentStrategyContext();// 获取支付宝支付策略PaymentStrategy zfbPaymentStrategy = paymentPage.getStrategy("zfb");// 用户进行支付zfbPaymentStrategy.pay(100.0);}
}
// 输出:使用支付宝支付:100.0元

在这里插入图片描述

基于注解式改造的案例

我们还是以上面的支付案例来进行改造,通过@Component和@Autowired注解,实现我们项目中经典使用方式。

1、定义支付策略接口

/*** 策略接口(对扩展开放)*/
public interface PaymentStrategy {// 支付void pay(double amount);// 或者其他业务...
}

2、定义支付方式的具体实现类,如支付宝、微信支付和银联支付

/*** 支付宝支付策略*/
@Component("payStrategy" + "zfb")
public class AliPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {// 调用支付宝支付接口// ...System.out.println("使用支付宝支付:" + amount + "元");}
}
/*** 微信支付策略*/
@Component("payStrategy" + "wx")
public class WeChatPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {// 调用微信支付接口// ...System.out.println("使用微信支付:" + amount + "元");}
}
/*** 银联卡支付策略*/
@Component("payStrategy" + "ylk")
public class UnionPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {// 调用银联支付接口// ...System.out.println("使用银联支付:" + amount + "元");}
}

3、接下来定义策略上下文改造

/*** 支付方式上下文*/
@Component
public class PaymentStrategyContext {/*** 策略集合* 灵魂所在,注入实例名及对应实例* 实例名:即component指定名称,默认为类名首字母小写名称*/@Autowiredprivate Map<String, PaymentStrategy> paymentStrategyMap = new ConcurrentHashMap<>();/*** 初始化* @param paymentStrategyMap*/public PaymentStrategyContext(Map<String, PaymentStrategy> paymentStrategyMap) {this.paymentStrategyMap.clear();paymentStrategyMap.forEach(this.paymentStrategyMap::put);}/*** 返回实际处理对象* @param strategy 支付方式* @return 实际处理对象*/public PaymentStrategy getStrategy(String strategy){return paymentStrategyMap.get(strategy);}}

4、使用策略

/*** 策略设计模式Controller*/
@RestController
@RequestMapping("/api/strategy")
public class StrategyController {@Autowiredprivate PaymentStrategyContext paymentStrategyContext ;/*** 根据类型获取对应的策略*/@GetMapping("/getByType")public void testStrategy(@RequestParam("type") String type) {try {PaymentStrategy zfbPaymentStrategy = paymentStrategyContext.getStrategy("payStrategy"+type);// 用户进行支付zfbPaymentStrategy.pay(100.0);}catch (Exception e){e.printStackTrace();}}
}

然后访问 localhost:8080/api/strategy/getByType?type="zfb"
在这里插入图片描述
提问:那么以上的支付策略是怎么注入到上下文PaymentStrategyContext中的Map的?

  • 首先我们可以注意到每个支付策略类上都有一个@Component("payStrategy" + "XX")

@Component是Spring框架中的注解之一,表示被注解的类是一个组件(Bean),会被Spring框架自动扫描并装配到Spring容器中。该注解通常用于标识Spring中需要进行依赖注入、AOP切面等操作的Java类。

使用@Component注解标注的类默认的Bean名称为类名的小写,例如类名为UserDao的Bean名称为userDao。使用@Component注解的类可以被其他注解所继承,如@Service、@Controller、@Repository等,这些注解都是@Component的派生注解,功能和@Component相同,只是为了更加明确地表明类的作用而设置的。
需要注意的是,@Component注解只是一个标记注解,不会对被注解的类进行额外的处理,如果需要使用其他注解实现更加具体的功能,可以使用派生注解或者组合注解的方式实现。

  • 其次在上下文PaymentStrategyContext类中,有一个对Map自动注入的操作,这里会自动注入实例名及对应实例。
    在这里插入图片描述

  • 当Spring启动时,通过对上下文对象进行初始化操作时,这一步首先会对Map进行清除,然后再将名称为payStrategy开头的bean设置到Map中。
    在这里插入图片描述

优缺点

  • 优点

算法策略可以自由实现切换
扩展性好,加一个策略,只需要增加一个类

  • 缺点

策略类数量多
需要维护一个策略枚举,让别人知道你当前具有哪些策略
(我上面是直接写的字符串,如果是在实际上面中是需要维护成一个枚举类)

策略模式的使用场景

Java中策略模式的应用场景非常广泛,以下是一些常见的例子:

  • 支付方式选择:在电商平台中,用户可以选择多种支付方式,如支付宝、微信支付、银行卡支付等。这些支付方式可以看作是策略,用户选择不同的支付方式就是选择了不同的策略。
  • 排序算法:在Java中,有多种排序算法可以选择,如快速排序、归并排序、冒泡排序等。这些排序算法也可以看作是策略,根据不同的需求选择不同的排序算法就是选择了不同的策略。
  • 表单验证:在Web开发中,通常需要对用户输入的表单数据进行验证。不同的表单数据验证方式可以看作是策略,根据表单的不同需求选择不同的验证策略。
  • 税收计算:在财务软件中,根据不同的国家和地区的税收政策,需要对税收计算方式进行不同的选择,这些税收计算方式也可以看作是策略。

以上这些例子都可以使用策略模式来实现,通过将策略封装成独立的类,可以让策略的实现与使用进行解耦,提高代码的可维护性和扩展性。

Spring框架中的使用场景

Spring框架中运用了策略模式的地方比较多,下面列举一些常见的场景:

  • 验证策略(Validation Strategy):在 Spring MVC 中,我们可以使用 @Valid 注解和 Validator 接口配合使用来进行请求参数的验证。Validator 接口中定义了一个 validate 方法,可以根据不同的策略来进行参数验证。

  • 缓存策略(Caching Strategy):Spring 框架中的缓存抽象,如 Cache 接口、CacheManager 接口和 CacheResolver 接口等,都使用了策略模式来支持不同的缓存策略,如 EhCache、Redis、Guava 等。

  • 数据访问策略(Data Access Strategy):Spring 框架中的 JdbcTemplate 使用了策略模式来支持不同的数据访问策略,如 JDBC、JPA、MyBatis 等。

  • 消息队列策略(Messaging Strategy):Spring 框架中的消息队列抽象,如 JmsTemplate 和 RabbitTemplate 等,都使用了策略模式来支持不同的消息队列策略,如 ActiveMQ、RabbitMQ 等。

以上只是几个常见的例子,实际上在 Spring 框架中运用到策略模式的地方还有很多。总的来说,策略模式可以帮助我们在应对不同的需求时,使用不同的算法或业务逻辑来达到相同的目的。这样可以使代码更加灵活、可维护和可扩展。

其实实际项目中也有很多应用,例如在线商城中有一个会员系统,商品的购买价格以及赠品等等优惠会根据用户会员等级的不同而产生不同的效果,亦或者是公司某软件对接企业微信,根据不同的审批处理请求,拿到对应的策略从而推送模板消息完成指定策略的操作等等


http://chatgpt.dhexx.cn/article/wtZ1Joen.shtml

相关文章

Java设计模式-策略模式(支付业务的优化)

一、什么情况下使用策略模式 功能类似的业务功能&#xff0c;避免创建多个接口&#xff0c;大量if else 判断逻辑&#xff0c;简化代码 二、我以支付为例&#xff08;支付方式有微信&#xff0c;支付宝支付&#xff09; 1、新建策略抽象类或者接口 public interface ThirdP…

java策略模式实战示例

现已放在gitee上,可以不下载直接参考一下即: https://gitee.com/zhang-xiao-xiang/zxx-pattern 日常碰到的业务概述 登录类型,支付类型,供应商渠道,不同等级会员享受的优惠券价格不一样,等等业务判断,大量if else导致拓展(侧重新增)极其困难,维护(侧重修改)自然是改起来头痛(…

【JAVA设计模式】策略模式

1.什么是策略模式&#xff1f; 策略模式是对算法的包装&#xff0c;是把使用算法的责任和算法本身分割开来&#xff0c;委派给不同的对象管理&#xff0c;相同的事情-----选择不用同方式 &#xff08;不同实现&#xff09;举例子&#xff0c;最终可以实现解决多重if判断问题。 …

java微信关注事件_java策略模式在接收微信事件推送上的具体应用

java策略模式&#xff0c;在我的认知中是根据不同选择执行不同的实现。通过if或者switch-case也能实现这种逻辑&#xff0c;但是代码冗余&#xff0c;可扩展性不强。 百度百科上的解释为策略模式作为一种软件设计模式&#xff0c;指对象有某个行为&#xff0c;但是在不同的场景…

Java设计模式之3种策略模式实现

一、什么是策略模式 所谓策略模式&#xff0c;就是定义了一组策略&#xff0c;分别封装在不同类中&#xff0c;每种策略都可以根据当前场景相互替换&#xff0c;从而使策略的变化可以独立于操作者。比如我们要去某个地方&#xff0c;会根据距离的不同来选择不同的出行方式&…

java8 策略模式_JAVA设计模式之策略模式

策略模式&#xff1a;在策略模式(Strategy Pattern)中&#xff0c;一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中&#xff0c;我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对…

git如何提交代码

代码提交 代码提交一般有五个步骤&#xff1a; 1.查看目前代码的修改状态 2.查看代码修改内容 3.暂存需要提交的文件 4.提交已暂存的文件 5.同步到服务器 1. 查看目前代码的修改状态 提交代码之前&#xff0c;首先应该检查目前所做的修改&#xff0c;运行git status命令 a)…

修改git提交时间

问题描述&#xff1a; 修改git提交记录的时间 git脚本&#xff1a; 此方法使用github上的开源工具完成。感谢无私的奉献者&#xff01; 操作步骤&#xff1a; github上下载文件 解压文件夹&#xff0c;把git-redate文件置于git安装目录的\mingw64\libexec\git-core文件夹…

git提交时常用命令

git提交时常用命令 一、git命令 -- 本地仓库与远程仓库建立链接 git init &#xff08;初始化本地仓库&#xff09; git remote add origin 远程仓库网址 git remote -v (查看远程仓库) git remote rm origin (删除远程分支)-- checkout多种用法 git checkout 文件名 &#…

git提交分支

1. git提交分支相关 在本地新建分支&#xff0c;保证和远程分支一样 git checkout -b 分支名如果分支已存在&#xff0c;只需要切换的话 git checkout 分支名提交前先把代码拉下来更新一下&#xff0c;确保不会覆盖别人的代码 git pull origin 远程分支(如果有)解决冲突 git …

Git 的提交

1 提交对象 理解Git分支&#xff0c;就需要了解提交及在提交过程中产生的提交对象&#xff08;commit object&#xff09;。在进行提交时Git会存储一个提交对象&#xff0c;其中包含一个指针&#xff0c;它指向此次提交时暂存内容的快照。除此之外&#xff0c;提交对象中还包含…

git代码提交

代码提交 代码提交一般有五个步骤&#xff1a; 1.查看目前代码的修改状态 2.查看代码修改内容 3.暂存需要提交的文件 4.提交已暂存的文件 5.同步到服务器 1. 查看目前代码的修改状态 提交代码之前&#xff0c;首先应该检查目前所做的修改&#xff0c;运行git status命令 a…

【git提交】流程

1- git提交&#xff08;分支合并&#xff09;操作流程&#xff1a; git 提交代码到自己的分支&#xff0c;并合并到主分支的完整流程 分支 sml &#xff0c; 主分支&#xff0c; master 分支msl上操作 git status git add . git commit -m ‘xxxxxx’ git pull origin master…

git提交规范,规范自己的提交标准

为了规范我的git提交内容&#xff0c;提交的时候commit -m “备注的信息”&#xff0c;但是每个人的备注信息千奇百怪&#xff0c;为了统一&#xff0c;我们进行了git的规范。 首先要全局安装commitizen npm i -g commitizen4.2.4然后安装插件 npm i cz-customizable6.3.0…

git提交常用命令

git 安装 1.在终端&#xff0c;检查git是否安装 git --version 2.没有安装的话&#xff0c;去git官网&#xff0c;下载git 3.安装后&#xff0c;在终端&#xff0c;检查git是否安装 4设置用户名和邮件地址&#xff08;最好和github的用户名/邮箱保持一致 git config -…

Git 提交规范

1. 背景 Git 是目前世界上最先进的分布式版本控制系统&#xff0c;在我们平时的项目开发中已经广泛使用。而当我们使用Git提交代码时&#xff0c;都需要写Commit Message提交说明才能够正常提交。 git commit -m "提交"然而&#xff0c;我们平时在编写提交说明时&a…

git 提交命令(附加git常用命令)

一、提交到git仓库 1、进入git终端、输入git init指令、会在当前目录生成一个.git的文件夹 git init2、然后通过git status 查看&#xff0c;该指令作用是 列出修改过的(绿色标识)、新创建的(红色标识)、已经暂存但未提交的文件(白色标识) git status3、然后通过git add ind…

git提交

Git提交代码步骤 1.1 第1步&#xff1a;同步远程仓库代码&#xff1a;git pull 提交代码第1步&#xff1a;git pull 同步远程仓库代码到本地 git add / git commit代码之前首先git pull&#xff0c;需先从服务器上面拉取代码&#xff0c;以防覆盖别人代码&#xff1b;如果有…

你真的了解单点登录(SSO)吗? 单点登录实现方式

在程序开发中&#xff0c;特别是网站类开发&#xff0c;会接触到单点登录(SSO)&#xff0c;什么是单点登录&#xff1f;单点登录(SSO)有什么用&#xff1f;下面就来详细介绍一下。 1 单点登录 1.1 什么是单点登录 单点登录的英文名叫做&#xff1a;Single Sign On&#xff0…

单点测试与多点测试

最近在使用PAT与Codeup写程序玩&#xff0c;发现这两者的OJ方式不同。 PAT是单点测试&#xff0c;即输入一组测试用例即可输出结果&#xff0c; 而Codeup是多点测试&#xff0c;即输入所有测试用例后才会输出所有输出结果。 在这里写两个最简单的ab求和小程序来说明二者的区别…