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

news2024/12/22 20:55:45

系列文章目录

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、小结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/604381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《嵌入式存储器架构、电路与应用》----学习记录(三)

第4章 嵌入式内存 4.1 Flash的发展背景 Flash是非易失存储器,具有存储密度高、容错能力强和读写速度相对较慢等特点,传统Flash广泛应用于外部大数据存储。为了满足微控制器芯片(MCU)高速运算的需求,嵌入式Flash(eFlash)往往作为MCU的内部数…

【花雕学AI】ChatGPT的四大语言处理神器:文本生成、问答、创意生成和内容优化的技巧和实例

引言:ChatGPT是一个人工智能聊天机器人,它可以理解和交流多种语言,例如中文、英文、日文、西班牙语、法语、德语等。它是由OpenAI开发的,基于GPT-3.5和GPT-4这两个大型语言模型。它不仅可以与用户进行对话,还可以根据用…

Centos7切换到Alibaba Cloud Linux3

通过控制台自动导入迁移源目前仅支持迁移源的类型为物理机/虚拟机/云服务器和阿里云ECS云服务器,若需要迁移其他类型的迁移源,则可以选择手动导入迁移源。 第一步,登录SMC客户端。 登录网址:阿里云登录 - 欢迎登录阿里云&#x…

MYSQL 8 Too many connections error 还在继续,这对DBA 是不公平的

开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共…

c++11 标准模板(STL)(std::bitset)(二)

定义于头文件 <bitset> template< std::size_t N > class bitset; 类模板 bitset 表示一个 N 位的固定大小序列。可以用标准逻辑运算符操作位集&#xff0c;并将它与字符串和整数相互转换。 bitset 满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssig…

qgis二次开发环境搭建(qgis-3.28.6+ubuntu22.04+qt5.15)

背景 一个Ros2项目中用到了qgis&#xff0c;特此整理一下用到的qgis二次开发代码。 linux搭建Debug环境(省事简单apt一把梭) 下载 ubuntu22.04qgis-3.28.6Qt version 5.15.3 编译 参考qgis编译文档 ubuntu22.04 使用上图jammy的安装命令上图中的apt-get安装命令安装的…

PyQt5桌面应用开发(19):事件过滤器

本文目录 PyQt5桌面应用系列再来点事件事件过滤器例子这是什么恶毒巫术?需求分析代码额外的细节知 总结 PyQt5桌面应用系列 PyQt5桌面应用开发&#xff08;1&#xff09;&#xff1a;需求分析 PyQt5桌面应用开发&#xff08;2&#xff09;&#xff1a;事件循环 PyQt5桌面应用开…

从零到无搭建Vue项目及代码风格规范

注&#xff1a;已经有vue项目的可以跳过项目初始化 Vue项目搭建 环境搭建 安装nvm 方便后续切换不通的node版本 nvm官网 傻瓜安装就行 或者搜下自己&#xff08;非本文重点&#xff09;nvm 安装好后 安装一个Node版本 本文使用的 有了环境开始创建Vue项目 打开命令行 cmd n…

Redis底层学习(五)—存储类型-Set篇

文章目录 特点具体服务器操作命令底层结构应用场景 特点 适⽤场景&#xff1a;存储有去重需求的数据&#xff0c;⽐如&#xff1a;针对⼀篇⽂章⽤户进⾏点赞操作。 它的特点是内部元素⽆序且不重复。它的内部实现相当于⼀个特殊的字典&#xff0c;字典中所有的 value 的值都为…

这么好看的头像,岂不拿下!

❝ 如此好看的头像&#xff0c;怎么能不喜欢&#xff1f;&#xff1f;&#xff1f; ❞ 代码放在了最后 后续还会出一个工具&#xff0c;以便于随时打开下载。 看上述的头像是不是还是很不错的。看着网站还是✨✨每天都会有更新的✨✨。 所以&#xff0c;我动手了&#xff0c;下…

5.2 案例引入

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 1.什么是大数据时代的到来&#xff1f; 大数据时代指的是在现代社会中&#xff0c;产生和积累的数据规模庞大、速度快、种类多样的时代。随着计算机技术…

《商用密码应用与安全性评估》第四章密码应用安全性评估实施要点4.4密码应用安全性评估测评过程指南

目录 概述 1.基本原则 2.风险测评控制 3.测评过程 密码应用评估方案 1.主要内容 1&#xff09;密码应用解决方案评估要点 2&#xff09;实施方案评估要点 3&#xff09;应急处置方案评估要点 2.主要任务 3.密码应用方案评估的输出文档 测评准备活动 1.测评准…

不愧是阿里,扣的真细。

铜三铁四已经过去了&#xff0c;今天的行情虽然没有以前好&#xff0c;但是相比去年来说也算是好了一些了。有一些人已经在这个招聘季拿到了不错的Offer了。 今天给大家分享一份面经&#xff0c;今天这位朋友的背景是Java五年本&#xff0c;2023年前被毕业后投入了面试大军怀抱…

Baseline Profile 安装时优化在西瓜视频的实践

‍ 动手点关注 干货不迷路 背景 在Android上&#xff0c;Java/Kotlin代码会编译为DEX字节码&#xff0c;在运行期由虚拟机解释执行。但是&#xff0c;字节码解释执行的速度比较慢。所以&#xff0c;通常虚拟机会在解释模式基础上做一些必要的优化。 在Android 5&#xff0c;Goo…

chatgpt赋能python:Python列表:完整介绍与使用指南

Python列表&#xff1a;完整介绍与使用指南 Python是一种非常受欢迎的编程语言&#xff0c;而Python列表是Python编程中最基本的数据结构之一。列表在Python中的使用频率极高&#xff0c;因为列表可以存储许多不同类型的数据&#xff0c;并且可以很方便地进行操作和修改。在本…

Unity Addressables学习笔记(2)---创建远程服务器对象

1.先创建对象 我的做法是&#xff1a; 先拖动一张图片到Resources/img下就是我选中的这张文件夹 2.把图片拖动到Hierarchy里变成一个对象&#xff0c;再把对象拖动到Resources/prefabs里&#xff0c;图片里的单词敲错了哈哈哈哈。 这样这个图片就变成了预制体&#xff0c;然…

AMD在数据中心领域举步维艰,竞争越来越难

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 数据中心业务举步维艰 2023年第一季度&#xff0c;AMD的数据中心业务收入为13亿美元&#xff0c;几乎没有同比增长&#xff0c;反而环比下降了22%。与此同时&#xff0c;它的主要竞争对手英伟达(NVDA)却获得了越来越多的订…

行为型设计模式04-状态模式

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 状态模式 1、状态模式介绍 状态模式&#xff08;State&#xff09;是一种行为型设计模式&#xff0c;当一个对象的内在状态改变时…

python---条件语句(1)

顺序语句 按照写的顺序执行 条件语句 条件语句的一些注意事项: 1.情况1 2.情况2 bbb已经不属于条件语句中的内容了 3.情况3 通常使用4个空格或一个制表符tab来表示! if语句的嵌套 当有多级条件嵌套时,当前的语句属于哪个代码块,完全取决于缩进的级别.

SOFA Weekly|SOFAArk 社区会议预告、Layotto 社区会议回顾与预告、社区本周贡献

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展 欢迎留言互动&#xff5e; SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&am…