当前位置: 首页 > news >正文

深入理解设计原则之单一职责原则(SRP)

系列文章目录

C++高性能优化编程系列
深入理解设计原则系列
深入理解设计模式系列
高级C++并发线程编程

SRP:单一职责原则

  • 系列文章目录
  • 1、单一职责原则的定义和解读
  • 2、单一职责原则案例解读
    • 2.1、违背单一职责原则反面案例
    • 2.2、违背单一职责原则反面案例 - 解决方案
  • 3、类的职责是否越细化越好
  • 4、如何判断类的职责是否单一
  • 5、小结

1、单一职责原则的定义和解读

单一职责原则(Single Responsibility Principle, SRP)的描述:一个类或模块只负责完成一个职责(或功能)。

注意单一职责原则描述的对象有两个:类(Class)和模块(Module)。关于这两个概念我们有两种理解方式。

  • 一种理解方式把模块看作比类更抽象的概念,把类看作一种模块
  • 另一种理解方式把模块看作更粗粒度的代码块,多个类组成一个模块

无论哪种理解方式,单一职责原则在应用这两个描述对象时,原理是相通的。粒度小,功能单一。

2、单一职责原则案例解读

2.1、违背单一职责原则反面案例

  • 反面案例1: 重复的假象
    某个工资管理程序中的Employee类有三个函数caculatePay()、reportHourse()和save()。

在这里插入图片描述

图1 Employee类

这三个类的函数分别对应的是三类非常不同的行为者,违反了SRP设计原则。

caculatePay()函数是由财务部门制定的,他们负责向CFO汇报。
reportHourse()函数是由人力资源部制定并使用的,他们负责向COO汇报。
save()函数是由DBA制定的,他们负责向CTO汇报。

这三个函数被放在同一个源代码文件,即同一个Employee类中,程序员这样做实际就等于使三个类行为耦合在了一起。这有可能会导致CFO团队的命令影响到COO团队所依赖的功能。

例如,caculatePay()函数和reportHourse()函数使用同样的逻辑来计算工作时数。程序员为了避免重复编码,通常会将该算法单独实现一个名为的reportHourse()函数。
在这里插入图片描述

图2 算法共享

接下来,假设CFO团队需要修改正常工作时数的计算方法,而COO带领的HR团队不需要这个修改,因为他们对数据的用法是不同的。
这时候,负责这项修改的程序员会注意到函数调用了函数,但可能不会注意到该函数会同时被调用。

于是,该程序员就这样按照要求进行了修改,同时CFO团队的成员验证了新算法工作正常。这项修改最终被成功部署上线。

但是,COO团队显然完全不知道这些事的发生,HR仍然在使用产生的报表,随后就发现他们的数据出错了!最终这个问题让COO十分愤怒,因为这些错误的数据给给公司造成了几百万的损失。
与此类的事情我们多多少少都经历过。这类问题发生的根源就是因为我们将不同的行为所依赖的代码强凑到一起。对此,SRP强调这类代码一定要分开。

  • 反面案例2: 代码合并
    一个拥有很多函数的源代码文件必然经历很多次代码合并,该文件中的这些函数分别服务不同行为者的情况就更加常见了。

例如,CTO团队的DBA决定要对Emploee数据表结构进行简单修改。与此同时,COO团队的HR需要修改工作时数据报表的格式。

这样一来,就很可能出现两个来自不同团队的程序员分别对Emploee进行修改的情况。不出意外的话,他们各自的修改一定会互相冲突,这就必要进行代码合并。

在这个例子中,这次代码合并不仅可能让CTO和COO要求的功能出错,甚至连CFO原本正常的功能也可能收到影响。

事实上,这样的案例还有很多,我们就不一一例举了。他们的一个共同点是,多人为了一个不同的目的修改了一份源代码,这很容造成问题的产生。

而避免这种问题产生的方法就是将服务不同行为者的代码进行切分。

2.2、违背单一职责原则反面案例 - 解决方案

我们有很多方法可以用来解决上面的问题,每一种方法都需要将相关的函数划分到不同的类。

其中,最简单直接办法是将数据与函数分离,设计三个类共同使用一个不包括函数的、十分简单的EmployeeData类,每个类只包含与之前相关函数的代码,互相不可见,这样就不存在相互依赖的情况了。
在这里插入图片描述

图3 三个类互相不可见

这种解决方案的坏处在于:程序员现在需要在程序里处理三个类。另一种方法是使用Facade(外观)设计模式。
在这里插入图片描述

图4 Facade模式

这样一来,EmployeeFacade类所需要的代码量就很少了,他仅仅包含了初始化和调用三个类的函数。

当然,也有程序员更倾向于把重要的业务逻辑与数据放在一起,那么我们也可以选择将最重要的函数保留在Emploee类中,同时用这个类调其他没那么重要的函数。
在这里插入图片描述

图5 将最重要的函数保留在Emploee类中,同时用这个类调其他没那么重要的函数

读者也许会反对上面这些解决方案,因为看上去这里的每个类中都只有一个函数,事实上并非如此,因为无论是计算工资、生成报表还是保存数据都是一个很复杂的过程,每个类都可能包含了许多私有函数。

总而言之,上面的每一类都分别容纳了一组作用于相同作用域函数,而在作用域之外,它们各自的私有函数是互相不可见的。

3、类的职责是否越细化越好

在面向对象编程中,类的职责不应该过多、过于复杂,而应该越细越好,这是因为:

类的单一职责原则(Single Responsibility Principle):每个类都应该只有一个职责,这样可以保证类的代码简洁明了、易于维护和扩展。

高内聚低耦合原则(High Cohesion Low Coupling):将一个类拆分成多个单一职责的类,可以使得各类之间的耦合度降低,提高代码的灵活性和可复用性。

然而,同时过度细化职责也有其负面影响:

增加代码复杂度和维护成本:当类被拆分成过多微小的类时,会增加代码的数量和复杂度,导致维护成本的增加。

过度抽象将导致代码的不透明性和可读性降低:在面向对象编程中,过度抽象会导致代码难以阅读和理解。

因此,类的职责是否越细化越好,需要根据具体情况进行权衡。在实际编程中,需要保持类的职责尽量单一、明确,但不要过度细化,避免代码的冗余和不必要的复杂性。同时也要注意把握好封装的程度,保证类的内部实现不会对外部造成影响。

4、如何判断类的职责是否单一

要判断一个类的职责是否单一,可以使用以下方法:

  1. 查看类的名称和文档:类的名称和文档应该准确地描述它的职责。如果名称或文档涵盖了多个职责,那么这个类可能不够单一。
  2. 分析类中的方法:观察类中的方法是否都涉及同一个领域或者问题域。如果这些方法处理不同的领域或问题域,那么这个类可能不够单一。
  3. 查看类的属性:观察类中的属性是否都与类的职责相关。如果属性与类的职责无关或者有多个职责,那么这个类可能不够单一。
  4. 观察类的依赖关系:观察类是否依赖其他类或模块,如果这些依赖与类的职责无关或者有多个职责,那么这个类可能不够单一。
  5. 观察代码的复杂度:观察类的代码是否过于复杂,如果代码过于复杂,可能说明这个类的职责不够单一。

综上所述,以上方法可以帮助你判断一个类的职责是否单一。如果你发现一个类的职责过于复杂或者不够单一,那么就需要考虑对这个类进行重构,将其拆分成多个单一职责的类。

5、小结

单一职责原则主要讨论的是函数和类之间的关系 - 但是它在两个讨论层面上会以不同的形式出现。在组件层面上,我们可以将其称为共同闭包原则,在软件架构层面,它则是用于奠定架构边界的变更轴心。

相关文章:

深入理解设计原则之单一职责原则(SRP)

系列文章目录 C高性能优化编程系列 深入理解设计原则系列 深入理解设计模式系列 高级C并发线程编程 SRP:单一职责原则 系列文章目录1、单一职责原则的定义和解读2、单一职责原则案例解读2.1、违背单一职责原则反面案例2.2、违背单一职责原则反面案例 - 解决方案 3…...

钉钉群通过短信转发器接收手机短信消息

1.短信转发器官网下载 下载地址 首发地址:https://github.com/pppscn/SmsForwarder/releases国内镜像:https://gitee.com/pp/SmsForwarder/releases网盘下载:https://wws.lanzoui.com/b025yl86h 访问密码:pppscn 使用文档 首发…...

【C++模版】模版进阶 {非类型模版参数; 模版的特化; 模版的分离编译; 模版总结}

一、非类型模版参数 模板参数分类型形参与非类型形参。 类型形参:出现在模板参数列表中,跟在class或者typename之后的参数类型名称。非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来…...

Azure Active Directory 的功能和优势

Azure Active Directory (Azure AD) 是 Microsoft 基于云的多租户目录和标识管理服务。 Azure AD 有助于支持用户访问资源和应用程序,例如: 位于企业网络上的内部资源和应用。 Microsoft 365、Azure 门户和 SaaS 应用程序等外部资源。 为组织开发的云应…...

mysql查询语句执行过程及运行原理命令

Mysql查询语句执行原理 数据库查询语句如何执行? DML语句首先进行语法分析,对使用sql表示的查询进行语法分析,生成查询语法分析树。语义检查:检查sql中所涉及的对象以及是否在数据库中存在,用户是否具有操作权限等视…...

可视化探索开源项目的 contributor 关系

引语:作为国内外最大的代码托管平台,根据最新的 GitHub 数据,它拥有超 372,000,000 个仓库,其中有 28,000,000 是公开仓。分布式图数据库 NebulaGraph 便是其中之一,同其他开源项目一样,NebulaGrpah 也有自…...

SpringBoot 实现启动项目后立即执行方法的几种方式

在项目开发中某些场景必须要用到启动项目后立即执行方式的功能,如我们需要去初始化数据到redis缓存,或者启动后读取相应的字典配置等,这篇文章主要聊聊实现立即执行的几种方法。 一、CommandLineRunner和ApplicationRunner 这两者的实现方法…...

2021第十二届蓝桥杯Python组国赛【真题+解析+代码】

🎁2021第十二届蓝桥杯python组国赛真题 🚀 真题练习,冲刺国赛 🚀 2021第十二届蓝桥杯python组国赛真题解析代码 博观而约取,厚积而薄发 🏆国赛真题目录 文章目录 🎁2021第十二届蓝桥杯python组国…...

3D引擎渲染管理系统概览

3D引擎渲染管理系统, 目前由: RendererScene, RendererSubScene, RendererSceneGraph, RenderProcess, RenderingCacheProcess/FBOProcess, (Material)PassGraph, (Material)PassNode, Material(Shader)Pipeline, RenderingFlowContainer, RenderableEnti…...

蔚来Java实习面经

目录 1.解释一下MySQL中脏读、不可重复读、幻读2.索引失效的场景有哪些?3.Explain执行计划用过吗4.Type字段有哪一些5.binlog和redolog的区别6.Redis基本数据类型7.有序集合的底层数据结构使用的是?8.跳表插入数据的过程能描述一下吗9.线程池&#xff0c…...

nginx 搭建http-flv(rtmp)流媒体的一次尝试

nginx 搭建http-flv(rtmp)流媒体的一次尝试 项目需要通过调用海康摄像头实现远程监控,但是由于网络限制,只能通过代理来调用,因此只能放弃海康官网提供的视频插件,经过一番搜索,决定采用此种方式:nginx 搭…...

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 目录 Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 六、关键脚本 附加: 声音设置相关 一、简单介绍…...

【A卡,Windows】stable diffusion webui下载安装避坑指南

观前提醒 本文内容都是本人亲身经历的,一个一个安装下载测试所感,当然如果你更想用傻瓜式集成包的,那还是跳过这篇文章吧。 当然我不推荐这篇文章的操作,因为我用了差不多1h才有一副图,有N卡,就用N卡&…...

并发编程-系统学习篇

并发编程的掌握过程并不容易。 我相信为了解决这个问题,你也听别人总结过:并发编程的第 一原则, 那就是不要写并发程序 这个原则在我刚毕业的那几年曾经是行得通的,那个时候多核服务器还是一种奢侈品,系统的并发量也很…...

在浏览器网页上使用JavaScript如何将mp4视频转换成gif动态图片

前言 要将mp4视频转换为gif动态图像&#xff0c;可以使用JavaScript库中的FFmpeg.js。这个库可以使用JavaScript读取和写入文件&#xff0c;也可以使用canvas和WebGL在浏览器中进行视频处理。 步骤如下&#xff1a; 1.在网站中引入FFmpeg.js库 <script src"https:/…...

Nginx网络服务——主配置文件-nginx.conf

Nginx网络服务——主配置文件-nginx.conf 一、全局配置的六个模块简介二、nginx配置文件的详解1.全局配置模块2.I/O 事件配置3.HTTP 配置4.Web 服务的监听配置5.其他设置 三、访问状态统计与控制1.访问状态统计2.基于授权的访问控制3.基于客户端的访问控制 一、全局配置的六个模…...

Java Map集合

8 Map集合 HashMap: 元素按照键是无序,不重复,无索引,值不做要求LinkedHashMap: 元素按照键是有序,不重复,无索引,值不做要求8.1 Map集合概述和特点 Map集合是一种双列集合,每个元素包含两个值Interface Map<K,V>; K:键的类型,V:值的类型Map集合的每个元素的格…...

数据库中的中英文术语大全

一、基础理论 基础理论英文术语中文释义data数据database&#xff08;DB&#xff09;数据库database system&#xff08;dbs&#xff09;数据库系统database management system数据库管理系统database administrator数据库管理员relational model关系模型relational database关…...

调用华为API实现身份证识别

调用华为API实现身份证识别 1、作者介绍2、调用华为API实现身份证识别2.1 算法介绍2.1.1OCR简介2.1.2身份证识别原理2.1.3身份证识别应用场景 2.2 调用华为API流程 3、代码实现3.1安装相关的包3.2代码复现3.3实验结果 1、作者介绍 雷千龙&#xff0c;男&#xff0c;西安工程大…...

一个简单的基于C/S模型的TCP通信实例

1 TCP协议 1.1 概念 TCP是一种面向连接的、可靠的协议&#xff0c;有点像打电话&#xff0c;双方拿起电话互通身份之后就建立了连接&#xff0c;然后说话就行了&#xff0c;这边说的话那边保证听得到&#xff0c;并且是按说话的顺序听到的&#xff0c;说完话挂机断开连接。也…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...