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

article/2025/10/4 17:39:38

一、什么是策略模式

  所谓策略模式,就是定义了一组策略,分别封装在不同类中,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距离的不同来选择不同的出行方式,比如:共享单车、打车、坐飞机等等,这些出行方式即不同的策略。

二、为什么使用策略模式

  我们在平时为了快速完成业务需求,会在代码实现时大量使用if…else,但当代码中大量充实着这种结构时会使代码的可读性、维护性降低并更容易出错。策略模式是解决这种问题的好办法,它符合设计模式中的开闭原则。

三、策略模式的组成

3.1 结构

image.png

3.2 角色

  • Context(环境类)
    策略上下文,维护一个Strategy对象的引用,屏蔽上层模块对策略、算法的直接访问
  • Strategy(抽象策略类)
    这是一个抽象角色,定义了所有的具体策略类所需的接口
  • ConcreteStrategy(具体策略类)
    实现了在抽象策略类中定义的方法,封装了相关的算法或行为。根据多态的特性,在运行时,具体策略类将覆盖在环境类中定义的抽象策略累对象

四、如何实现策略模式

源代码:https://github.com/mofan3/strategy-mode.git

4.1 基于注解实现

  • 枚举定义
/*** 消息类型枚举*/
@Getter
@AllArgsConstructor
public enum MessageType {PUSH("通知栏"),SMS("短信"),EMAIL( "邮件"),WECHAT( "微信"),DING_DING_ROBOT( "钉钉机器人"),DING_DING_WORK_NOTICE("钉钉工作通知");/*** 描述*/private String description;
}
  • 注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface HandlerAnnotation {  MessageType type();
}
  • 策略接口定义
public interface BizHandler {void execute(MessageInfo messageInfo);
}
  • 策略实现
@HandlerAnnotation(type = MessageType.SMS)
@Slf4j
public class SmsHandler implements BizHandler {@Overridepublic void execute(MessageInfo messageInfo) {SmsMessageInfo smsMessageInfo = (SmsMessageInfo) messageInfo;// 发送短信log.info("发送短信消息:{}",smsMessageInfo.getContent());}}
@HandlerAnnotation(type = MessageType.WECHAT)
@Slf4j
public class WeChatHandler implements BizHandler {@Overridepublic void execute(MessageInfo messageInfo) {WeChatMessageInfo weChatMessageInfo = (WeChatMessageInfo) messageInfo;// 发送微信log.info("发送微信消息:{}",weChatMessageInfo.getContent());}}
  • 策略组装
@Component
public class BizHandlerContext  implements BeanPostProcessor {private final Map<String, BizHandler> bizHandlerMap = new HashMap<>();@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {HandlerAnnotation annotation = AnnotationUtils.findAnnotation(bean.getClass(), HandlerAnnotation.class);if (ClassUtils.isAssignableValue(BizHandler.class,bean) && annotation != null) {bizHandlerMap.put(annotation.type().name(), (BizHandler)bean);}return bean;}public BizHandler getBizHanler(String type) {BizHandler bizHandler = this.bizHandlerMap.get(type);if (bizHandler == null) {throw new RuntimeException("not.found.BizHandler");}return bizHandler;}}

BizHandlerContext类实现BeanPostProcessor接口,在postProcessBeforeInitialization(…)方法中拿到所有含有@HandlerAnnotation注解的bean,以bean的注解中的type值作为键和bean作为值构建handlerMap,从而在外界以type作为请求时可以准确的获取到handlerMap中相应的bean。

  • 测试
@SpringBootTest
class StrategyModeApplicationTests {@AutowiredBizHandlerContext bizHandlerContext;@Testvoid contextLoads() {AbstractBizHandler bizHanler = bizHandlerContext.getBizHanler(MessageType.SMS.name());SmsMessageInfo smsContentModel = new SmsMessageInfo();smsContentModel.setContent("【XXX】验证码:313122,您正在进行登录操作,若非本人操作,请勿泄露");bizHanler.execute(smsContentModel);}}

4.2 基于@Autowired的实现

  • 策略接口定义
public interface BizHandler {void execute(MessageInfo messageInfo);String getType();
}
  • 策略实现
@Service
@Slf4j
public class SmsHandler implements BizHandler {@Overridepublic void execute(MessageInfo messageInfo) {SmsMessageInfo smsMessageInfo = (SmsMessageInfo) messageInfo;// 发送短信log.info("发送短信消息:{}",smsMessageInfo.getContent());}@Overridepublic String getType() {return MessageType.SMS.name();}}
@Service
@Slf4j
public class WeChatHandler implements BizHandler {@Overridepublic void execute(MessageInfo messageInfo) {WeChatMessageInfo weChatMessageInfo = (WeChatMessageInfo) messageInfo;// 发送微信log.info("发送微信消息:{}",weChatMessageInfo.getContent());}@Overridepublic String getType() {return MessageType.WECHAT.name();}}
  • 策略组装
@Component
public class BizHandlerContext{private final Map<String, BizHandler> bizHandlerMap = new HashMap<>();@Autowired(required = false)public void putBizHandler(List<BizHandler> bizHandlerList) {for (BizHandler bizHandler : bizHandlerList) {String type = bizHandler.getType();if (StringUtils.isEmpty(type)) {throw new IllegalArgumentException("BizHandler name must not be empty");}bizHandlerMap.put(type, bizHandler);}}public BizHandler getBizHanler(String type) {BizHandler bizHandler = this.bizHandlerMap.get(type);if (bizHandler == null) {throw new RuntimeException("not.found.BizHandler");}return bizHandler;}
}

BizHandlerContext使用了Spring的@Autowired注解按照类型自动装配的功能,循环遍历BizHandler集合,获取type值作为键和bean作为值加入到handlerMap中。

  • 测试
@SpringBootTest
class StrategyModeApplicationTests {@AutowiredBizHandlerContext bizHandlerContext;@Testvoid contextLoads() {AbstractBizHandler bizHanler = bizHandlerContext.getBizHanler(MessageType.SMS.name());SmsMessageInfo smsContentModel = new SmsMessageInfo();smsContentModel.setContent("【XXX】验证码:313122,您正在进行登录操作,若非本人操作,请勿泄露");bizHanler.execute(smsContentModel);}}

4.3 基于InitializingBean的实现

  • 抽象处理器定义
public abstract class AbstractBizHandler implements InitializingBean {@Autowiredprivate BizHandlerContext bizHandlerContext;@Overridepublic void afterPropertiesSet() throws Exception {bizHandlerContext.putBizHandler(getType(),this);}public abstract void execute(MessageInfo messageInfo);public abstract String getType();
}

InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法。也可用@PostConstruct注解实现,但接口实现方式在调用效率上高一些,@PostConstruct是通过反射机制调用的

  • 策略实现
@Service
@Slf4j
public class SmsHandler extends AbstractBizHandler {@Overridepublic void execute(MessageInfo messageInfo) {SmsMessageInfo smsMessageInfo = (SmsMessageInfo) messageInfo;// 发送短信log.info("发送短信消息:{}",smsMessageInfo.getContent());}@Overridepublic String getType() {return MessageType.SMS.name();}}
@Service
@Slf4j
public class WeChatHandler extends AbstractBizHandler {@Overridepublic void execute(MessageInfo messageInfo) {WeChatMessageInfo weChatMessageInfo = (WeChatMessageInfo) messageInfo;// 发送微信log.info("发送微信消息:{}",weChatMessageInfo.getContent());}@Overridepublic String getType() {return MessageType.WECHAT.name();}}
  • 策略组装
@Component
public class BizHandlerContext{private final Map<String, AbstractBizHandler> bizHandlerMap = new HashMap<>();public void putBizHandler(String type, AbstractBizHandler handler) {bizHandlerMap.put(type, handler);}public AbstractBizHandler getBizHanler(String type) {AbstractBizHandler bizHandler = this.bizHandlerMap.get(type);if (bizHandler == null) {throw new RuntimeException("not.found.BizHandler");}	return bizHandler;}
}
  • 测试
@SpringBootTest
class StrategyModeApplicationTests {@AutowiredBizHandlerContext bizHandlerContext;@Testvoid contextLoads() {AbstractBizHandler bizHanler = bizHandlerContext.getBizHanler(MessageType.SMS.name());SmsMessageInfo smsContentModel = new SmsMessageInfo();smsContentModel.setContent("【XXX】验证码:313122,您正在进行登录操作,若非本人操作,请勿泄露");bizHanler.execute(smsContentModel);}}

五、总结

  通过策略设计模式的使⽤可以把我们方法中的if语句优化掉,以上3种实现方式本质上是利用Map的key-value结构做映射来解决⼤量if else 的叠加嵌套问题,⼤量的if语句使用会让代码难以扩展,也不好维护,同时在后期遇到各种问题也很难维护。在使用这样的设计模式后可以很好的满⾜隔离性与和扩展性,对于不断新增的需求也⾮常⽅便承接。


http://chatgpt.dhexx.cn/article/6VZuEwXQ.shtml

相关文章

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求和小程序来说明二者的区别…

什么是单点登录?如何理解单点登录

什么是单点登录&#xff1f; 什么是单点登录&#xff1f; 因为讲了Cookie&#xff0c;讲了Session&#xff0c;这里我觉得有必要说一下单点登录。我们先来看一下什么是单点登录&#xff1a;单点登录&#xff08;Single Sign On&#xff09;&#xff0c;简称为 SSO&#xff0c;…

单点登录和多点登录

SSO&#xff08;单点登录&#xff09;的概念&#xff1a;在一个多系统共存的环境下&#xff0c;用户在一处登录后&#xff0c;就不用再其他系统中登录&#xff0c;也就是用户的一次登录能得到其他所有系统的信任。 SSO&#xff08;单点登录&#xff09;的应用场景&#xff1a;…

OAuth2.0 实现单点登录

文章目录 OAuth2.0 实现单点登录一、四种授权模式二、搭建验证服务器三、使用 Postman 接口测试四、基于 EnableOAuth2Sso 实现五、基于 EnableResourceServer 实现 &#x1f6a9;&#x1f36c;5.1 资源服务器&#x1f36c;5.2 客户端访问&#x1f36c;5.3 解决远程调用 六、使…

单点登录(SSO)-- 实现单点登录的几种方式

1.为什么需要单点登录 三个角度&#xff1a; 1.1 方便用户的使用&#xff1a;用户登录一次&#xff0c;可以使用不同的服务和页面&#xff0c;省了忘记密码的痛苦1.2 简化开发&#xff1a;SSO让开发人员只要开发一个通用的身份验证框架&#xff0c;就不用为身份验证操心了1.3 …

面试系列:单点登录的知识(一)

大家好&#xff0c;我是车辙&#xff0c;由于目前接手的业务涉及到了单点登录&#xff0c;所以一直在疯狂的去补充这方面的知识。也写下了这篇面试形式的文章&#xff0c;写的不好大家轻点 Diss。 面试开始 在焦急的等待中&#xff0c;一位看上去比较年轻的小伙子走了过来。我…