常见面试题之设计模式--策略模式
1. 概述
先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。
作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有很多,可以选择Idea
进行开发,也可以使用eclipse
进行开发,也可以使用其他的一些开发工具。
定义:
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
2. 结构
策略模式的主要角色如下:
- 抽象策略(
Strategy
)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 - 具体策略(
Concrete Strategy
)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。 - 环境(
Context
)类:持有一个策略类的引用,最终给客户端调用。
3. 案例实现
【例】促销活动
一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员将促销活动展示给客户。类图如下:
聚合关系可以用带空心菱形的实线来表示。
代码如下:
定义百货公司所有促销活动的共同接口。
public interface Strategy {void show();
}
定义具体策略角色(Concrete Strategy
):每个节日具体的促销活动。
//为春节准备的促销活动A
public class StrategyA implements Strategy {public void show() {System.out.println("买一送一");}
}//为中秋准备的促销活动B
public class StrategyB implements Strategy {public void show() {System.out.println("满200元减50元");}
}//为圣诞准备的促销活动C
public class StrategyC implements Strategy {public void show() {System.out.println("满1000元加一元换购任意200元以下商品");}
}
定义环境角色(Context
):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员。
public class SalesMan { //持有抽象策略角色的引用 private Strategy strategy; public SalesMan(Strategy strategy) { this.strategy = strategy; } //向客户展示促销活动 public void salesManShow(){ strategy.show(); }
}
4. 综合案例
4.1 概述
下图是gitee
的登录的入口,其中有多种方式可以进行登录。
-
用户名密码登录
-
短信验证码登录
-
微信登录
-
QQ
登录 -
…
4.2 目前已实现的代码
(1)登录接口
说明 | |
---|---|
请求方式 | POST |
路径 | /api/user/login |
参数 | LoginReq |
返回值 | LoginResp |
请求参数:LoginReq
@Data
public class LoginReq {private String name;private String password;private String phone;private String validateCode;//手机验证码private String wxCode;//用于微信登录/*** account : 用户名密码登录* sms : 手机验证码登录* we_chat : 微信登录*/private String type;
}
响应参数:LoginResp
@Data
public class LoginResp{private Integer userId;private String userName;private String roleCode;private String token; //jwt令牌private boolean success;}
控制层LoginController
@RestController
@RequestMapping("/api/user")
public class LoginController {@Autowiredprivate UserService userService;@PostMapping("/login")public LoginResp login(@RequestBody LoginReq loginReq){return userService.login(loginReq);}
}
业务层UserService
@Service
public class UserService {public LoginResp login(LoginReq loginReq){if(loginReq.getType().equals("account")){System.out.println("用户名密码登录");//执行用户密码登录逻辑return new LoginResp();}else if(loginReq.getType().equals("sms")){System.out.println("手机号验证码登录");//执行手机号验证码登录逻辑return new LoginResp();}else if (loginReq.getType().equals("we_chat")){System.out.println("微信登录");//执行用户微信登录逻辑return new LoginResp();}LoginResp loginResp = new LoginResp();loginResp.setSuccess(false);System.out.println("登录失败");return loginResp;}
}
注意:我们重点讲的是设计模式,并不是登录的逻辑,所以以上代码并没有真正的实现登录功能。
(2)问题分析
- 业务层代码大量使用到了
if...else
,在后期阅读代码的时候会非常不友好,大量使用if...else
性能也不高。 - 如果业务发生变更,比如现在新增了
QQ
登录方式,这个时候需要修改业务层代码,违反了开闭原则。
解决:
使用工厂方法设计模式+策略模式解决。
4.3 代码改造(工厂+策略)
(1)整体思路
改造之后,不在service
中写业务逻辑,让service
调用工厂,然后通过service
传递不同的参数来获取不同的登录策略(登录方式)。
(2)具体实现
抽象策略类:UserGranter
/*** 抽象策略类*/
public interface UserGranter{/*** 获取数据* @param loginReq 传入的参数* @return map值*/LoginResp login(LoginReq loginReq);
}
具体的策略:AccountGranter
、SmsGranter
、WeChatGranter
/*** 策略:账号登录**/
@Component
public class AccountGranter implements UserGranter{@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("登录方式为账号登录" + loginReq);// TODO// 执行业务操作 return new LoginResp();}
}
/*** 策略:短信登录*/
@Component
public class SmsGranter implements UserGranter{@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("登录方式为短信登录" + loginReq);// TODO// 执行业务操作return new LoginResp();}
}
/*** 策略:微信登录*/
@Component
public class WeChatGranter implements UserGranter{@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("登录方式为微信登录" + loginReq);// TODO// 执行业务操作return new LoginResp();}
}
工程类:UserLoginFactory
/*** 操作策略的上下文环境类 工具类* 将策略整合起来 方便管理*/
@Component
public class UserLoginFactory implements ApplicationContextAware {private static Map<String, UserGranter> granterPool = new ConcurrentHashMap<>();@Autowiredprivate LoginTypeConfig loginTypeConfig;/*** 从配置文件中读取策略信息存储到map中* {* account:accountGranter,* sms:smsGranter,* we_chat:weChatGranter* }** @param applicationContext* @throws BeansException*/@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {loginTypeConfig.getTypes().forEach((k, y) -> {granterPool.put(k, (UserGranter) applicationContext.getBean(y));});}/*** 对外提供获取具体策略** @param grantType 用户的登录方式,需要跟配置文件中匹配* @return 具体策略*/public UserGranter getGranter(String grantType) {UserGranter tokenGranter = granterPool.get(grantType);return tokenGranter;}}
在application.yml
文件中新增自定义配置
login:types:account: accountGrantersms: smsGranterwe_chat: weChatGranter
新增读取数据配置类
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "login")
public class LoginTypeConfig {private Map<String,String> types;}
改造service
代码
@Service
public class UserService {@Autowiredprivate UserLoginFactory factory;public LoginResp login(LoginReq loginReq){UserGranter granter = factory.getGranter(loginReq.getType());if(granter == null){LoginResp loginResp = new LoginResp();loginResp.setSuccess(false);return loginResp;}LoginResp loginResp = granter.login(loginReq);return loginResp;}
}
大家可以看到我们使用了设计模式之后,业务层的代码就清爽多了,如果后期有新的需求改动,比如加入了QQ
登录,我们只需要添加对应的策略就可以,无需再改动业务层代码。
4.4 举一反三
其实像这样的需求,在日常开发中非常常见,场景有很多,以下的情景都可以使用工厂模式+策略模式解决。比如:
- 订单的支付策略
- 支付宝支付
- 微信支付
- 银行卡支付
- 现金支付
- 解析不同类型
excel
xls
格式xlsx
格式
- 打折促销
- 满300元9折
- 满500元8折
- 满1000元7折
- 物流运费阶梯计算
- 5
kg
以下 - 5
kg
-10kg
- 10
kg
-20kg
- 20
kg
以上
- 5
一句话总结:只要代码中有冗长的if-else
或switch
分支判断都可以采用策略模式优化。
相关文章:

常见面试题之设计模式--策略模式
1. 概述 先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有很多,可以选择Idea进行开发&a…...

redis多key问题
多key问题指的是在Redis中存在大量的key,如果这些key过多,超过了Redis可以容纳的内存大小,那么数据会被保存在交换空间(swap区),这会导致性能下降。 Redis是一种基于内存的缓存数据库,它的性能…...

kafka第三课-可视化工具、生产环境问题总结以及性能优化
一、可视化工具 https://pan.baidu.com/s/1qYifoa4 密码:el4o 下载解压之后,编辑该文件,修改zookeeper地址,也就是kafka注册的zookeeper的地址,如果是zookeeper集群,以逗号分开 vi conf/application.conf 启…...

2_Apollo4BlueLite中断控制器NVIC
1.概述 Apollo4BlueLite 的中断控制器是采用 ARM Cortex-M4 内核,并集成了 NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)作为其中断控制器。 NVIC 是 ARM Cortex-M 系列处理器中常用的中断控制器,…...

WAIC2023:图像内容安全黑科技助力可信AI发展
目录 0 写在前面1 AI图像篡改检测2 生成式图像鉴别2.1 主干特征提取通道2.2 注意力模块2.3 纹理增强模块 3 OCR对抗攻击4 助力可信AI向善发展总结 0 写在前面 2023世界人工智能大会(WAIC)已圆满结束,恰逢全球大模型和生成式人工智能蓬勃兴起之时,今年参…...

微信小程序quickstartFunctions中云函数的应用
1、在quickstartFunctions文件中新建文件夹和文件 2、index.js 文件书写 const cloud require(wx-server-sdk);cloud.init({env: cloud.DYNAMIC_CURRENT_ENV }); const db cloud.database();// 链表查询试卷和对应的题库 exports.main async (event, context) > {retu…...

Go学习 2、程序结构
2、程序结构 2.1 命名 一个名字必须以一个字母或下划线开头,后面可以跟任意数量的字母、数字或下划线。 大写字母和小写字母是不同的。 GO语言有25个关键字,关键字不能用于自定义名字。 还有大约30多个预定义名字,对应内建的常量、类型和函…...

SpringBoot整合JavaMail
SpringBoot整合JavaMail 简单使用-发送简单邮件 介绍协议 导入坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>添加配置 spring:mail:host: smtp.qq.co…...

Spring6——入门
文章目录 入门环境要求构建模块程序开发引入依赖创建java类创建配置文件创建测试类运行测试程序 程序分析启用Log4j2日志框架Log4j2日志概述引入Log4j2依赖加入日志配置文件测试使用日志 入门 环境要求 JDK:Java17(Spring6要求JDK最低版本是Java17&…...

【计算机视觉 | 目标检测 | 图像分割】arxiv 计算机视觉关于目标检测和图像分割的学术速递(7 月 17 日论文合集)
文章目录 一、检测相关(5篇)1.1 TALL: Thumbnail Layout for Deepfake Video Detection1.2 Cloud Detection in Multispectral Satellite Images Using Support Vector Machines With Quantum Kernels1.3 Multimodal Motion Conditioned Diffusion Model for Skeleton-based Vi…...

为什么需要GP(Global Platform)认证?
TEE之GP(Global Platform)认证汇总 一、为什么需要认证? 二、为什么是GP? 参考: GlobalPlatform Certification - GlobalPlatform...

eclipse 格式化代码 快捷键
在Eclipse中,可以使用以下快捷键来格式化代码: Windows/Linux快捷键:Ctrl Shift FMac快捷键:Command Shift F 按下相应的快捷键后,Eclipse将自动根据您的代码格式化偏好设置对代码进行格式化。请确保已经选择和配…...

深入探索Socks5代理与网络安全
简介 Socks5代理是一种网络协议,用于在客户端和服务器之间进行数据传输,它可以在网络层和传输层实现代理功能。与其他代理协议相比,Socks5代理更加灵活和安全,为爬虫任务和网络安全提供了重要支持。 Socks5代理的工作原理 Socks5…...

【NLP】如何使用Hugging-Face-Pipelines?
一、说明 随着最近开发的库,执行深度学习分析变得更加容易。其中一个库是拥抱脸。Hugging Face 是一个平台,可为 NLP 任务(如文本分类、情感分析等)提供预先训练的语言模型。 本博客将引导您了解如何使用拥抱面部管道执行 NLP 任务…...

网络安全(黑客)自学笔记
1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一是市场需求量高; 二则是发展相对成熟入门…...

spring数据校验
数据校验 概述 在开发中,会存在参数校验的情况,如:注册时,校验用户名不能为空、用户名长度不超过20个字符,手机号格式合法等。如果使用普通方式,会将校验代码和处理逻辑耦合在一起,在需要新增一…...

因材施教,有道发布“子曰”教育大模型,落地虚拟人口语教练等六大应用
因材施教的教育宗旨下,大模型浪潮中,网易有道凭借其对教育场景的深入理解和对商业化的理性思考,为行业树立了垂直大模型的典范。 7月26日,教育科技公司网易有道举办了“powered by 子曰”教育大模型应用成果发布会。会上重磅推出了…...

golang waitgroup
案例 WaitGroup 可以解决一个 goroutine 等待多个 goroutine 同时结束的场景,这个比较常见的场景就是例如 后端 worker 启动了多个消费者干活,还有爬虫并发爬取数据,多线程下载等等。 我们这里模拟一个 worker 的例子 package mainimport (…...

单列模式多学两遍
单例模式 单例模式(Singleton Pattern,也称为单件模式),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。 定义单例类 ● 私有化它的构造函数,…...

Spring Cloud【SkyWalking网络钩子Webhooks、SkyWalking钉钉告警、SkyWalking邮件告警】(十六)
目录 分布式请求链路追踪_SkyWalking网络钩子Webhooks 分布式请求链路追踪_SkyWalking钉钉告警 分布式请求链路追踪_SkyWalking邮件告警 分布式请求链路追踪_SkyWalking网络钩子Webhooks Wbhooks网络钩子 Webhok可以简单理解为是一种Web层面的回调机制。告警就是一个事件&a…...

【力扣每日一题】2023.7.25 将数组和减半的最少操作次数
目录 题目: 示例: 分析: 代码运行结果: 题目: 示例: 分析: 题目给我们一个数组,我们每次可以将任意一个元素减半,问我们操作几次之后才可以将整个数组的和减半&…...

Docker-Compose 轻松搭建 Grafana+InfluxDb 实用 Jmeter 监控面板
目录 前言: 1、背景 2、GranfanaInfluxDB 配置 2.1 服务搭建 2.2 配置 Grafana 数据源 2.3 配置 Grafana 面板 3、Jmeter 配置 3.1 配置 InfluxDB 监听器 3.2 实际效果 前言: Grafana 和 InfluxDB 是两个非常流行的监控工具,它们可…...

异构线程池的c++实现方案
概要 通常线程池是同质的,每个线程都可以执行任意的task(每个线程中的task顺序执行),如下图所示: 但本文所介绍的线程和task之间有绑定关系,如A task只能跑在A thread上(因此称为异构线程池&am…...

Python实现抽象工厂模式
抽象工厂模式是一种创建型设计模式,用于创建一系列相关或依赖对象的家族,而无需指定具体类。在Python中,可以通过类和接口的组合来实现抽象工厂模式。 下面是一个简单的Python实现抽象工厂模式的示例: # 抽象产品接口 class Abs…...

@vue/cli安装
vue/cli安装 1、全局安装vue/cli包2、查看是否成功 1、全局安装vue/cli包 yarn global add vue/cli2、查看是否成功 vue -V...

用友全版本任意文件上传漏洞复现
声明 本文仅用于技术交流,请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此文章的修改和解释权。如欲转载或传播此文章,…...

程序员面试系列,MySQL常见面试题?
原文链接 一、索引相关的面试题 (1)索引失效的情况有哪些 在MySQL查询时,以下情况可能会导致索引失效,无法使用索引进行高效的查询: 数据类型不匹配:如果查询条件中的数据类型与索引列的数据类型不匹配&…...

前端Web实战:从零打造一个类Visio的流程图拓扑图绘图工具
前言 大家好,本系列从Web前端实战的角度,给大家分享介绍如何从零打造一个自己专属的绘图工具,实现流程图、拓扑图、脑图等类Visio的绘图工具。 你将收获 免费好用、专属自己的绘图工具前端项目实战学习如何从0搭建一个前端项目等基础框架项…...

2023牛客暑期多校第二场部分题解
索引 ABCDEFGHIK A 队友开的题,说是其实就是问能不能用若干个数异或出来某个数。 应该就是线性基板子,然后他写了一下就过了。 B 一开始看没什么人过不是很敢开,结果到后面一看题——这不是最大权闭合子图板子吗??…...

20230724将真我Realme手机GT NEO3连接到WIN10的电脑的步骤
20230724将真我Realme手机GT NEO3连接到WIN10的电脑的步骤 2023/7/24 23:23 缘起:因为找使用IMX766的手机,找到Realme手机GT NEO3了。 同样使用IMX766的还有:Redmi Note12Pro 5G IMX766 旗舰影像 OIS光学防抖 OLED柔性直屏 8GB256GB时光蓝 现…...