单一职责原则(设计模式)
目录
问题:
定义:
解决:
方式 1:使用策略模式
示例:用户管理
方式 2:使用装饰者模式
示例:用户操作
方式 3:使用责任链模式
示例:用户操作链
总结
推荐
问题:
今天刷面经的时候,发现不理解单一职责原则,特此记录。
定义:
单一职责原则:一个模块或类只完成一个功能。
那可以想一想,引起类变化的原因有哪些呢?
总结下来,只有2种
- 添加
- 修改
如果我们可以,将“添加”和“修改”分开,那么这个类也就完成了单一职责原则
建议先了解一下 委托者模式,因为下面几个设计模式核心都是委托者模式。
解决:
在 Java 开发中,如果你想 将“引起一个类的变更的原因”进行拆分,通常需要遵循单一职责原则,让修改(Modify)和添加(Add)两个操作独立,不相互影响。
可以用 策略模式、装饰者模式、责任链模式、组合模式 等方式来实现。下面是几种常见的方式:
方式 1:使用策略模式
如果修改和添加是两种不同的操作,可以用策略模式拆分,让它们各自处理自己的逻辑。
示例:用户管理
// 定义通用策略接口
public interface UserOperationStrategy {void execute(User user);
}// 修改策略
public class ModifyUserStrategy implements UserOperationStrategy {@Overridepublic void execute(User user) {System.out.println("修改用户信息: " + user.getName());}
}// 添加策略
public class AddUserStrategy implements UserOperationStrategy {@Overridepublic void execute(User user) {System.out.println("添加新用户: " + user.getName());}
}// 上下文类,动态选择操作
public class UserService {private UserOperationStrategy strategy;public void setStrategy(UserOperationStrategy strategy) {this.strategy = strategy;}public void executeOperation(User user) {strategy.execute(user);}
}// 使用策略
public class Main {public static void main(String[] args) {User user = new User("Tom");UserService userService = new UserService();// 进行添加操作userService.setStrategy(new AddUserStrategy());userService.executeOperation(user);// 进行修改操作userService.setStrategy(new ModifyUserStrategy());userService.executeOperation(user);}
}
优点:
- 让“修改”和“添加”逻辑完全解耦,互不影响。
- 方便扩展,比如再加一个“删除用户”策略。
方式 2:使用装饰者模式
如果修改和添加可以叠加,可以使用装饰者模式,在原功能基础上动态增加额外行为,而不改变原类的代码。
示例:用户操作
// 定义基础接口
public interface UserOperation {void execute();
}// 基础实现(原始功能)
public class BaseUserOperation implements UserOperation {@Overridepublic void execute() {System.out.println("基础用户操作");}
}// 装饰器基类
public abstract class UserOperationDecorator implements UserOperation {protected UserOperation decoratedOperation;public UserOperationDecorator(UserOperation decoratedOperation) {this.decoratedOperation = decoratedOperation;}@Overridepublic void execute() {decoratedOperation.execute();}
}// 添加用户功能
public class AddUserDecorator extends UserOperationDecorator {public AddUserDecorator(UserOperation decoratedOperation) {super(decoratedOperation);}@Overridepublic void execute() {super.execute();System.out.println("添加用户");}
}// 修改用户功能
public class ModifyUserDecorator extends UserOperationDecorator {public ModifyUserDecorator(UserOperation decoratedOperation) {super(decoratedOperation);}@Overridepublic void execute() {super.execute();System.out.println("修改用户");}
}// 使用装饰器
public class Main {public static void main(String[] args) {UserOperation operation = new BaseUserOperation();// 先添加,再修改UserOperation addThenModify = new ModifyUserDecorator(new AddUserDecorator(operation));addThenModify.execute();}
}
输出
基础用户操作
添加用户
修改用户
优点
- 动态组合行为,比如 先添加再修改 或者 只修改不添加。
- 方便扩展,不用修改原类。
方式 3:使用责任链模式
如果修改和添加是流程中的不同步骤,可以用责任链模式,让不同操作按顺序执行,方便扩展。
示例:用户操作链
// 责任链接口
public interface UserHandler {void handle(User user);
}// 责任链基类
public abstract class AbstractUserHandler implements UserHandler {protected UserHandler nextHandler;public void setNextHandler(UserHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic void handle(User user) {if (nextHandler != null) {nextHandler.handle(user);}}
}// 添加用户处理器
public class AddUserHandler extends AbstractUserHandler {@Overridepublic void handle(User user) {System.out.println("添加用户: " + user.getName());super.handle(user);}
}// 修改用户处理器
public class ModifyUserHandler extends AbstractUserHandler {@Overridepublic void handle(User user) {System.out.println("修改用户信息: " + user.getName());super.handle(user);}
}// 流程控制
public class Main {public static void main(String[] args) {User user = new User("Tom");// 责任链AddUserHandler addHandler = new AddUserHandler();ModifyUserHandler modifyHandler = new ModifyUserHandler();addHandler.setNextHandler(modifyHandler);// 先添加,再修改addHandler.handle(user);}
}
输出
添加用户: Tom
修改用户信息: Tom
总结
方案 | 适用场景 | 主要特点 |
---|---|---|
策略模式 | 修改和添加是两种独立操作 | 通过不同策略切换操作,逻辑清晰,便于扩展 |
装饰者模式 | 需要叠加功能,比如先添加再修改 | 允许动态组合多个操作,避免修改原类 |
责任链模式 | 操作有固定顺序,比如先添加再修改 | 让多个处理器按顺序执行,扩展性强 |
推荐
- 如果“修改”和“添加”是两种完全独立的操作,用 策略模式。
- 如果“修改”和“添加”可能会动态组合,用 装饰者模式。
- 如果“修改”和“添加”是必须按固定顺序执行,用 责任链模式。
相关文章:
单一职责原则(设计模式)
目录 问题: 定义: 解决: 方式 1:使用策略模式 示例:用户管理 方式 2:使用装饰者模式 示例:用户操作 方式 3:使用责任链模式 示例:用户操作链 总结 推荐 问题&a…...
生理信号概念
rPPG 信号(远程光电容积脉搏波信号) 原理: 基于光电容积脉搏波描记法,利用普通摄像头,在一定距离外捕捉人体皮肤表面因心脏泵血导致的血液容积变化引起的细微颜色变化,通过图像处理和信号分析算法提取心率…...

安卓内存泄露之DMA-BUF异常增长:Android Studio镜像引起DMA内存泄露
安卓内存泄露之DMA-BUF异常增长:Android Studio镜像引起DMA内存泄露 - Wesley’s Blog 今天用着安卓 14 的板子的时候突然系统卡死。 查看日志发现launcher都被干掉了 03-04 06:13:35.544 7872 8479 I ActivityManager: vis BFGS 18740: com.android.launcher3 (pid 8407) se…...
android13打基础: 控件checkbox
测试checkbox的activity // todo: 高级控件checkbox public class Ch4_CheckBoxActivity extends AppCompatActivityimplements CompoundButton.OnCheckedChangeListener {Overrideprotected void onCreate(Nullable Bundle savedInstanceState) {super.onCreate(savedInstance…...
AI应用测试:遇到类ChatGPT的流式接口要如何压测?
先说结论: 使用最普遍的JMeter 就能支持类 OpenAI 的流式接口(如 ChatGPT 的流式聊天接口)的测试 总体设置 JMeter 支持测试 OpenAI 的流式接口,但需要额外配置(如启用 KeepAlive 和调整超时)。如果需要实时处理流式响应,使用 Regular Expression Extractor 或自定义脚…...
React面试葵花宝典之二
36.Fiber的更新机制 React Fiber 更新机制详解 React Fiber 是 React 16 引入的核心架构重构,旨在解决可中断渲染和优先级调度问题,提升复杂应用的流畅性。其核心思想是将渲染过程拆分为可控制的工作单元,实现更细粒度的任务管理。以下是其…...

在日常生活、工作中deepseek能帮我们解决哪些问题
在日常生活、工作中deepseek能帮我们解决哪些问题 DeepSeek极大降低了普通人使用AI的门槛,让AI快速渗透到人们的工作和生活中,无论是专业场景提效、教育学术赋能、商业创新甚至日常生活,都变得更加轻松。 当然这篇文章也参考了deepseek的回…...
【Java】IO流
Java IO流是Java中处理输入输出的核心机制,通过不同的流类型实现了对数据的高效读写。 一、IO流的分类 1. 按数据方向 输入流(Input Stream):从数据源(如文件、网络等)读取数据。输出流(Outp…...

HTML第三节
一.初识CSS 1.CSS定义 A.内部样式表 B.外部样式表 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title&g…...

Visual Studio 2022安装问题解决,提示无法安装Microsoft.VisualStudio.Community.Msi
表现现象为:安装完后提示无法安装Microsoft.VisualStudio.Community.Msi,无法正常开发C项目 查看日志,大概显示: xxx ReturnCode1316 xxxxx 消息详细信息: 指定的帐户已存在。 试了网上所有的办法都没用,反复尝试&…...
【代码分享】基于IRM和RRT*的无人机路径规划方法详解与Matlab实现
基于IRM和RRT*的无人机路径规划方法详解与Matlab实现 1. IRM与RRT*的概述及优势 IRM(Influence Region Map)通过建模障碍物的影响区域,量化环境中的安全风险,为RRT算法提供启发式引导。RRT(Rapidly-exploring Random…...

MybatisPlus从入门到精通
一、MyBatis-Plus核心特性 无侵入性 在MyBatis基础上增强,无需修改原有代码即可使用。自动化CRUD 内置通用Mapper和Service,减少80%单表操作代码。Lambda表达式 支持Lambda形式的条件构造,避免字段名硬编码错误。主键策略 支持雪花算法&…...
el-table input textarea 文本域 自适应高度,切换分页滚动失效处理办法
场景: el-table 表格 需要 input类型是 textarea 高度是自适应,第一页数据都是单行数据 不会产生滚动条,但是第二页数据是多行数据 会产生滚动条, bug: 第一页切换到第二页 第二页滚动条无法展示 解决办法:直接修改样…...

基于Windows11的DockerDesktop安装和布署方法简介
基于Windows11的DockerDesktop安装和布署方法简介 一、下载安装Docker docker 下载地址 https://www.docker.com/ Download Docker Desktop 选择Download for Winodws AMD64下载Docker Desktop Installer.exe 双点击 Docker Desktop Installer.exe 进行安装 测试Docker安装是…...

ffmpeg源码编译支持cuda
1.安装cuda CUDA Toolkit 11.3 Downloads | NVIDIA Developer 在选择组件的时候,将CUDA中的Nsight VSE和Visual Studio Integration取消勾选 不然会安装失败 2.编译ffmpeg 把cuda编译宏定义开启,再编译avcodec 3.编译livavutil报错struct "Cuda…...

动漫短剧开发公司,短剧小程序搭建快速上线
在当今快节奏的生活里,人们的娱乐方式愈发多元,而动漫短剧作为新兴娱乐形式,正以独特魅力迅速崛起,成为娱乐市场的耀眼新星。近年来,动漫短剧市场呈爆发式增长,吸引众多创作者与观众目光。 从市场规模来看…...
《2025软件测试工程师面试》接口测试篇
基础概念 什么是接口测试? 接口测试是测试系统组件间接口的一种测试,主要用于检测外部系统和内部系统之间以及各个子系统之间的交互点。测试的重点是检查数据的交换、传递和控制管理的过程,以及系统间的相互逻辑依赖关系等。 接口测试的优势是什么? 接口测试具有规范性与扩…...
嵌入式学习第二十三天--网络及TCP
进程通信的方式: 同一主机 传统 system V 不同主机 网络 --- 解决不同主机间 的进程间通信 网络 (通信) //1.物理层面 --- 联通(通路) //卫星 2G 3G 4G 5G 星链 (千帆) //2.逻辑层面 --- 通路(软件) MAC os LINUX …...

Elasticsearch:解锁深度匹配,运用Elasticsearch DSL构建闪电般的高效模糊搜索体验
目录 Elasticsearch查询分类 叶子查询 全文检索查询 match查询 multi_match查询 精确查询 term查询 range查询 复杂查询 bool查询简单应用 bool查询实现排序和分页 bool查询实现高亮 场景分析 问题思考 解决方案 search_after方案(推荐) point in time方案 方案…...

SQLAlchemy系列教程:基本数据类型及自定义类型
在SQLAlchemy、Python SQL工具包和ORM中定义模型时,理解基本数据类型至关重要。本教程提供了在SQLAlchemy模型中有效使用内置基本类型的指南。 SQLAlchemy中的基本类型 SQLAlchemy支持一组与SQL数据库类型一致的基本数据类型。SQLAlchemy中的每种类型都为各种SQL类…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...