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

单一职责原则

单一职责原则:

就一个类而言,应该只有一个引起它变化的原因,如果一个类承担的职责过多就等于把这些职责耦合在一起,至少会造成以下两方面的问题:

  • 我们要去修改该类中的一个职责可能会影响到该类的其它职责。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。
  • 当客户端仅需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

我们在设计一个类时要学会发现职责,并把那些职责相互分离,其实要去判断是否应该分离出一个类来并不难,前面说过,一个类应该只有一个引起它变化的原因,如果你能想到其它的原因也能去改变这个类,那么这个类就具有多于1个的职责,就应该考虑类的职责分离。

在之前的这篇博客中,https://blog.csdn.net/weixin_44049823/article/details/128907849,我们实现的计算器实际上也用到了单一职责原则,这里我们选出其中最经典的3.0版本和5.0版本来学习单一职责原则。

3.0版本计算器代码如下:

#include<iostream>
using namespace std;
#include<string>//业务逻辑
//异常类用于处理异常情况
class opeException
{
public:void getMessage(){cout << "您的输入有误!" << endl;}
};//运算类用于处理运算
class Operation
{
public:Operation(string& _num1, string& _num2, string& _ope) :num1(_num1), num2(_num2), ope(_ope){}//获取运算结果int getResult(){if (!(isStringNum(num1) && isStringNum(num2) && (ope == "+" || ope == "-" || ope == "*" || ope == "/")))throw opeException();if (ope == "+"){re = stoi(num1) + stoi(num2);}else if (ope == "-"){re = stoi(num1) - stoi(num2);}else if (ope == "*"){re = stoi(num1) * stoi(num2);}else if (ope == "/"){if (stoi(num2) != 0){re = stoi(num1) / stoi(num2);}elsethrow opeException();}return re;}
private:int re;string num1;string num2;string ope;//判断一个字符串是不是数字bool isStringNum(string& s){bool flag = true;for (auto e : s)if (!(isdigit(e))){flag = false;break;}return flag;}
};//界面逻辑
int main()
{try{string _num1 = " ";string _num2 = " ";string _ope = " ";cout << "请输入左操作数:" << endl;cin >> _num1;cout << "请输入右操作数:" << endl;cin >> _num2;cout << "请输入操作符" << endl;cin >> _ope;Operation operation(_num1, _num2, _ope);cout << operation.getResult() << endl;}catch (opeException &ex){ex.getMessage();}return 0;
}

仅仅一个运算类Operation就实现了加减乘除4种功能,很明显在这个类中我至少有4个原因去修改这个类,我修改加法算法的时候可能会影响到其它的运算算法,这个类的耦合太高且严重违反了单一职责原则。

修改后的5.0版本如下:

#include<iostream>
using namespace std;
#include<string>//业务逻辑//异常类用于处理异常情况
class opeException
{
public:void getMessage(){cout << "您的输入有误!" << endl;}
};//运算类
class Operation
{//判断一个字符串是不是数字bool isStringNum(string& s){bool flag = true;for (auto e : s)if (!(isdigit(e))){flag = false;break;}return flag;}protected:bool isError(string& _strNum1, string& _strNum2, string& _ope){if (!(Operation::isStringNum(_strNum1) && Operation::isStringNum(_strNum2) && (_ope == "+" || _ope == "-" || _ope == "*" || _ope == "/"))){return false;}}
public:virtual int getResult() = 0;
};//加法运算类
class addOperation :public Operation
{
private:string strNum1;string strNum2;string ope;int re;
public:addOperation(string& _strNum1, string& _strNum2, string& _ope) :strNum1(_strNum1), strNum2(_strNum2), ope(_ope), re(0) {}virtual int getResult() override{if (!isError(strNum1, strNum2, ope))throw opeException();elsere = stoi(strNum1) + stoi(strNum2);return re;}
};//减法运算类
class subOperation :public Operation
{
private:string strNum1;string strNum2;string ope;int re;
public:subOperation(string& _strNum1, string& _strNum2, string& _ope) :strNum1(_strNum1), strNum2(_strNum2), ope(_ope), re(0) {}virtual int getResult() override{if (!isError(strNum1, strNum2, ope))throw opeException();elsere = stoi(strNum1) - stoi(strNum2);return re;}
};//乘法运算类
class mulOperation :public Operation
{
private:string strNum1;string strNum2;string ope;int re;
public:mulOperation(string& _strNum1, string& _strNum2, string& _ope) :strNum1(_strNum1), strNum2(_strNum2), ope(_ope), re(0) {}virtual int getResult() override{if (!isError(strNum1, strNum2, ope))throw opeException();elsere = stoi(strNum1) * stoi(strNum2);return re;}
};//除法运算类
class divOperation :public Operation
{
private:string strNum1;string strNum2;string ope;int re;
public:divOperation(string& _strNum1, string& _strNum2, string& _ope) :strNum1(_strNum1), strNum2(_strNum2), ope(_ope), re(0) {}virtual int getResult() override{if (!isError(strNum1, strNum2, ope))throw opeException();else if (stoi(strNum2) != 0)re = stoi(strNum1) / stoi(strNum2);elsethrow opeException();return re;}
};//运算工厂类
class OpeFactory
{
public:Operation& choose(string &_strNum1,string &_strNum2,string &_ope){if (_ope == "+"){operation = new addOperation(_strNum1, _strNum2, _ope);}else if (_ope == "-")operation = new subOperation(_strNum1, _strNum2, _ope);else if (_ope == "*")operation = new mulOperation(_strNum1, _strNum2, _ope);else if (_ope == "/"){operation = new divOperation(_strNum1, _strNum2, _ope);}elseoperation = nullptr;return *operation;}private:Operation* operation;
};//界面逻辑
int main()
{try{string _strNum1 = " ";string _strNum2 = " ";string _ope = " ";cout << "请输入左操作数:" << endl;cin >> _strNum1;cout << "请输入右操作数:" << endl;cin >> _strNum2;cout << "请输入操作符:" << endl;cin >> _ope;OpeFactory factory;Operation* re = &factory.choose(_strNum1, _strNum2, _ope);if (re != nullptr)cout << (*re).getResult() << endl;elsecout << "您的输入有误!" << endl;}catch (opeException ex){cout << "您的输入有误" << endl;}return 0;
}

在5.0版本的计算器代码中,我们将运算类分成了4种类,分别是加法类、减法类、乘法类、除法类,还创建了一个工厂类专门用于根据不同情况实例化对象,每个类只有一个职责,我们要修改某个功能只需要去修改对应的类即可,极大降低了代码之间的耦合。

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点:

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
  • 提高类的可读性。复杂性降低,自然其可读性会提高。
  • 提高系统的可维护性。可读性提高,那自然更容易维护了。
  • 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。

相关文章:

单一职责原则

单一职责原则&#xff1a; 就一个类而言&#xff0c;应该只有一个引起它变化的原因&#xff0c;如果一个类承担的职责过多就等于把这些职责耦合在一起&#xff0c;至少会造成以下两方面的问题&#xff1a; 我们要去修改该类中的一个职责可能会影响到该类的其它职责。这种耦合…...

golangの并发编程(GMP模型)

GMP模型 && channel1. 前言2. GMP模型2.1. 基本概念2.2. 调度器策略2.3. go指令的调度流程2.4. go启动周期的M0和G02.5. GMP可视化2.6. GMP的几种调度场景3. channel3.1. channel的基本使用3.2. 同步器1. 前言 Go中的并发是函数相互独立运行的体现&#xff0c;Gorouti…...

MacBook Pro错误zsh: command not found: brew解决方法

问题描述&#xff1a;本地想安装Jenkins&#xff0c;但是brew指令不存在/我的电脑型号是19款的MacBook Pro&#xff08;Intel芯片&#xff09;。解决方法MacBook Pro 重新安装homebrew&#xff0c;用以下命令安装&#xff0c;序列号选择阿里巴巴下载源。/bin/zsh -c "$(cu…...

spring中BeanFactory 和ApplicationContext

在学习spring的高阶内容时&#xff0c;我们有必要先回顾一下spring回顾spring1.什么是springspring是轻量级的&#xff0c;指核心jar包时很小的&#xff1b;非侵入式的一站式框架(数据持久层&#xff0c;web层&#xff0c;核心aop)&#xff0c;为了简化企业级开发。核心是IOC&a…...

HC32L17x的LL驱动库之dma

#include "hc32l1xx_ll_dma.h"/// //函 数: //功 能: //输入参数: //输出参数: //说 明: // uint8_t LL_DMA_DeInit(DMA_TypeDef* DMAx, uint32_t Channel) {__IO uint32_t* dmac NULL;dmac &(DMAx->CONFA0);Channel << 4;dmac …...

SSM项目 替换为 SpringBoot

一、运行SSM项目 保证项目改为SpringBoot后运行正常&#xff0c;先保证SSM下运行正常。 项目目录结构 创建数据库&#xff0c;导入sql文件 查看项目中连接数据jar版本&#xff0c;修改对应版本&#xff0c;修改数据库配置信息 配置启动tomcat 运行项目&#xff0c;测试正常…...

RL笔记:动态规划(2): 策略迭代

目录 0. 前言 (4.3) 策略迭代 Example 4.2: Jack’s Car Rental Exercise 4.4 Exercise 4.5 Exercise 4.6 Exercise 4.7 0. 前言 Sutton-book第4章&#xff08;动态规划&#xff09;学习笔记。本文是关于其中4.2节&#xff08;策略迭代&#xff09;。 (4.3) 策略迭代 基…...

2023软件测试金三银四常见的软件测试面试题-【测试理论篇】

三、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的SE会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; 我…...

蓝桥训练第二周

1 &#xff0c;泛凯撒加密 内存限制&#xff1a;128 MB时间限制&#xff1a;1.000 S 题目描述 众所周知&#xff0c;在网络安全中分为明文和密文&#xff0c;凯撒加密是将一篇明文中所有的英文字母都向后移动三位&#xff08;Z的下一位是A&#xff09;&#xff0c;比如a向后…...

详讲函数知识

目录 1. 函数是什么&#xff1f; 2. C语言中函数的分类&#xff1a; 2.1 库函数&#xff1a; 2.2 自定义函数 函数的基本组成&#xff1a; 3. 函数的参数 3.1 实际参数&#xff08;实参&#xff09;&#xff1a; 3.2 形式参数&#xff08;形参&#xff09;&#xff1a; …...

gin 框架初始教程文档

一 、gin 入门1. 安装gin &#xff1a;下载并安装 gin包&#xff1a;$ go get -u github.com/gin-gonic/gin2. 将 gin 引入到代码中&#xff1a;import "github.com/gin-gonic/gin"3.初始化项目go mod init gin4.完整代码package mainimport "github.com/gin-go…...

Maven的下载和安装【详细】

文章目录一、什么是Maven&#xff1f;二、Maven的安装与配置2.1下载Maven安装包2.2配置Maven环境变量2.3验证三、Idea配置Maven3.1配置 setting.xml文件3.2Idea配置Maven一、什么是Maven&#xff1f; Apache Maven是个项目管理和自动构建工具&#xff0c;基于项目对象模型&…...

[数据结构]:04-循环队列(数组)(C语言实现)

目录 前言 已完成内容 循环队列实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-QueueCommon.cpp 04-QueueFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容&#xff0c;除其中使用到C引用外&#xff0c;全为C语言代码。使用C引用主要是为了…...

buu [GWCTF 2019]BabyRSA 1

题目描述&#xff1a; import hashlib import sympy from Crypto.Util.number import *flag GWHT{******} secret ******assert(len(flag) 38)half len(flag) / 2flag1 flag[:half] flag2 flag[half:]secret_num getPrime(1024) * bytes_to_long(secret)p sympy.nextp…...

codeforces 1669F

题意: alice和bob从数组两边的吃糖果, 数组的值就是糖果重量 要求alice和bob吃的糖果重量必须一样, 输出能吃几个糖果 这题最先想到的是前后缀相加 模拟一个前缀和 和 后缀和 在n/2的位置向前找前缀和 在n/2的位置向后找后缀和 找到第一个前缀和后缀和的下标输出就好 …...

高数考试必备知识点

三角函数与反三角函数的知识点 正弦函数 ysin x&#xff0c; 反正弦函数 yarcsin x • y sin x&#xff0c; x∈R&#xff0c; y∈[–1&#xff0c;1]&#xff0c;周期为2π&#xff0c;函数图像以 x (π/2) kπ 为对称轴 • y arcsin x&#xff0c; x∈[–1&#xff0c;1]…...

[蓝桥杯] 二分与前缀和习题练习

文章目录 一、二分查找习题练习 1、1 数的范围 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 机器人跳跃问题 1、2、1 题目描述 1、2、2 题解关键思路与解答 1、3 四平方和 1、3、1 题目描述 1、3、2 题解关键思路与解答 二、前缀和习题练习 2、1 前缀和 2、1、1 题目描述…...

SpringMvc中HandlerAdapter组件的作用

概述 我们在使用springMVC时&#xff0c;都知道其中不仅包含handlerMapping组件还包含handlerAdapter组件&#xff0c;为什么呢&#xff1f; springMVC请求流程图 HandlerAdapter组件使用了适配器模式 适配器模式的本质是接口转换和代码复用&#xff0c;这里使用适配器模式的…...

FreeRTOS优先级翻转

优先级翻转优先级翻转&#xff1a;高优先级的任务反而慢执行&#xff0c;低优先级的任务反而优先执行优先级翻转在抢占式内核中是非常常见的&#xff0c;但是在实时操作系统中是不允许出现优先级翻转的&#xff0c;因为优先级翻转会破坏任务的预期顺序&#xff0c;可能会导致未…...

服务器部署—部署springboot之Linux服务器安装jdk和tomcat【建议收藏】

我是用的xshell连接的云服务器&#xff0c;今天想在服务器上面部署一个前后端分离【springbootvue】项目&#xff0c;打开我的云服务器才发现&#xff0c;过期了&#xff0c;然后又买了一个&#xff0c;里面环境啥都没有&#xff0c;正好出一期教程&#xff0c;方便大家也方便自…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

Docker环境下安装 Elasticsearch + IK 分词器 + Pinyin插件 + Kibana(适配7.10.1)

做RAG自己打算使用esmilvus自己开发一个&#xff0c;安装时好像网上没有比较新的安装方法&#xff0c;然后找了个旧的方法对应试试&#xff1a; &#x1f680; 本文将手把手教你在 Docker 环境中部署 Elasticsearch 7.10.1 IK分词器 拼音插件 Kibana&#xff0c;适配中文搜索…...

C/Python/Go示例 | Socket Programing与RPC

Socket Programming介绍 Computer networking这个领域围绕着两台电脑或者同一台电脑内的不同进程之间的数据传输和信息交流&#xff0c;会涉及到许多有意思的话题&#xff0c;诸如怎么确保对方能收到信息&#xff0c;怎么应对数据丢失、被污染或者顺序混乱&#xff0c;怎么提高…...

边缘计算设备全解析:边缘盒子在各大行业的落地应用场景

随着工业物联网、AI、5G的发展&#xff0c;数据量呈爆炸式增长。但你有没有想过&#xff0c;我们生成的数据&#xff0c;真的都要发回云端处理吗&#xff1f;其实不一定。特别是在一些对响应时间、网络带宽、数据隐私要求高的行业里&#xff0c;边缘计算开始“火”了起来&#…...