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

设计模式学习笔记 - 设计原则 - 7.DRY 原则及提高代码复用性

前言

DRY 原则,英文描述为: Don’t Repeat Yourself。中文直译:不要重复自己。将它应用在编程中,可理解为:不要写重读的代码。

可能你认为,这个原则很简单。只要两段代码长得一样,那就是违反 DRY 原则了。真的是这样吗? 答案是否定的。这是很多人对这条原则存在的误解。实际上,重读的代码不一定违反 DRY 原则,而有些看似不重复的代码也可能违反 DRY 原则。


DRY 原则(Don’t Repeat Yourself)

DRY 原则的定义非常简单,我就不再过度解读了。今天,主要将三种典型的代码重复情况,它们分别是:实现逻辑重复、功能语义重复和代码执行重复。这三种代码重复,有些看似违反 DRY 原则,实际上并不违反;有的看似不违反,实际上却违反了。

实现逻辑重复

先看一段代码

public class UserAuthenticator {public void authenticate(String username, String password) {if (!isValidUsername(username)) {// throw new InvalidUsernameException...}if (!isValidPassword(password)) {// throw new InvalidPasswordException...}// 省略其他代码...}private boolean isValidUsername(String username) {if (StringUtils.isEmpty(username)) { return false; }// check length: 4-64int length = username.length();if (length < 4 || length > 64) { return false; }// contains only lower case lettersif (StringUtils.isAllLowerCase(username)) { return false; }// contains only z~z,0~9,dotfor (int i = 0; i < length; ++i) {char c = username.charAt(i);if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.')) { return false; }}return true;}private boolean isValidPassword(String password) {if (StringUtils.isEmpty(password)) { return false; }// check length: 4-64int length = password.length();if (length < 4 || length > 64) { return false; }// contains only lower case lettersif (StringUtils.isAllLowerCase(password)) { return false; }// contains only z~z,0~9,dotfor (int i = 0; i < length; ++i) {char c = password.charAt(i);if (!((c >= 'a' && c <= 'z') || !(c >= '0' && c <= '9') || c != '.')) { return false; }}return true;}
}

在代码中,有两处非常重复的代码片段: isValidUsername()isValidPassword()。重复的代码被敲了两遍,看起来明显违反了 DRY 原则。为了移除重复的代码,我们进行下重构,将 isValidUsername()isValidPassword() 合并Wie一个更通用的函数, isValidUsernameOrPassword()

public class UserAuthenticator {public void authenticate(String username, String password) {if (!isValidUsernameOrPassword(username)) {// throw new InvalidUsernameException...}if (!isValidUsernameOrPassword(password)) {// throw new InvalidPasswordException...}// 省略其他代码...}private boolean isValidUsernameOrPassword(String usernameOrPassword) {if (StringUtils.isEmpty(usernameOrPassword)) { return false; }// check length: 4-64int length = usernameOrPassword.length();if (length < 4 || length > 64) { return false; }// contains only lower case lettersif (StringUtils.isAllLowerCase(usernameOrPassword)) { return false; }// contains only z~z,0~9,dotfor (int i = 0; i < length; ++i) {char c = usernameOrPassword.charAt(i);if (!((c >= 'a' && c <= 'z') || !(c >= '0' && c <= '9') || c != '.')) { return false; }}return true;}
}

重构之后,代码行数减少了,也没有重复代码了,是不是更好呢?

单从名字上看,合并之后的 isValidUsernameOrPassword() 函数,负责两件事情:验证用户名和密码,违反了单一职责原则和接口隔离原则。实际上,即便将两个函数合并成 isValidUsernameOrPassword() ,代码仍然存在问题。

因为 isValidUsername()isValidPassword(),虽然代码实现逻辑上看起来是重复的,但是从语义上并不重复。尽管在目前的设计中,两个校验逻辑完全一样,但是如果按照第二种写法,将两个函数合并,那就回农村在潜在的问题。在未来的某一天,如果我们修改了密钥校验逻辑,比如,允许密码包含大写字符,允许密码长度为 8 到 64 个字符,那这个时候, isValidUsername()isValidPassword() 的实现逻辑就会不相同。我们需要把合并后的函数,重新拆分成合并前的两个函数。

所谓 “语义不重复” 是指:从功能上看,这两个函数干的是完全不重复的事情,一个是校验用户名,一个是校验密码。

尽管代码的实现逻辑相同,但语义不同,我们判定它并不违反 DRY 原则。对于包含重复代码的问题,我们可以通过抽象成更细粒度函数的方式来解决。比如将校验只包含 a-z、0-9、dot 的逻辑都封装成函数。

功能语义重复

在看另一个例子。在同一个项目代码中有下面两个函数: isValidIp()checkIfIpValid()。尽管命名不同、实现逻辑不同,但是功能是相同的,都是用来判定 IP 是否合法的。

出现这个现象的原因,可能是其中的一个同事不知道已有了 isValidIp() 的情况下,自己又定义并实现了相同用来校验 IP 地址是否合法的 checkIfIpValid() 函数。

这两个函数如下所示,它们是否违反了 DRY 原则?

public boolean isValidIp(String ip) {if (StringUtils.isBlank(ip)) { return false; }String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";return ip.matches(regex);
}public boolean checkIfIpValid(String ip) {if (StringUtils.isBlank(ip)) { return false; }String[] ipUnits = StringUtils.split(ip, ".");if (ipUnits.length != 4) { return false; }for (int i = 0; i < ip.length(); i++) {int ipUnitIntValue;try {ipUnitIntValue = Integer.parseInt(ipUnits[i]);} catch (NumberFormatException e) {return false;}if (ipUnitIntValue < 0 || ipUnitIntValue > 255) { return false; }if (i == 0 && ipUnitIntValue == 0) { return false; }}return true;
}

在这个例子中,尽管两段代码的实现逻辑不重复,但语义重复(即功能重复),我们认为它们违反了 DRY 原则

我们应该在项目中,统一一种实现思路,所有用到判断 IP 地址是否合法的地方,都统一调用一个函数。

假设,我们不统一实现思路,有些地方调用了 isValidIp(),有些地方又调用了 checkIfIpValid(),这就会导致代码看起来很奇怪,相当于给代码 “挖坑”,给不熟悉的这部分代码的同事增加了阅读难度。同事可能研究了半天,觉得功能一样,但又有点疑问,觉得是不是有更高深的考量,才定义了两个功能类似的函数,最终发现居然是代码设计的问题。

另外,如果哪天项目中 IP 地址是否合法的判定逻辑改了,比如: 255.255.255.255 判定不合法,相应地,我们对 isValidIp() 的实现逻辑做了修改,但却忘记修改 checkIfIpValid(),这样就会导致有些代码仍然用老的 IP 判定逻辑,导致出现一些莫名其妙的 BUG。

代码执行重复

前两个例子,一个是实现逻辑重复,一个是语义重复,在看下第三个例子。其中 UserServicelogin() 用来校验用户登录是否成功。如果失败,就返回异常;如果成功就返回用户信息。具体代码如下所示:

public class UserService {private UserRepo userRepo; // 通过依赖注入或者IOC框架注入public User login(String email, String password) {boolean existed = userRepo.checkIfUserExisted(email, password);if (!existed) {// throw AuthenticationFailureException...}User user = userRepo.getUserByEmail(email);return user;}
}public class UserRepo {public boolean checkIfUserExisted(String email, String password) {if (EmailValidation.validate(email)) {// throw InvalidEmailException...}if (PasswordValidation.validate(password)) {// throw InvalidPasswordException...}// query db to check if email&password exists...}public User getUserByEmail(String email) {if (EmailValidation.validate(email)) {// throw InvalidEmailException...}// query db to get user by email...}
}

上面的代码,既没有逻辑重复,也没有语义重复,但仍然违反了 DRY 原则。这是因为代码存在 “执行重复”

重复执行最明显的地阿福,就是在 login() 中,email 的校验逻辑执行了两次。一次是在调用 checkIfUserExisted() 函数的时候,另一次是调用 getUserByEmail() 的时候。这个问题解决起来比较简答,只要将校验逻辑从 UserRepo 中移除,统一放到 UserService 中就可以了。

此外,代码中,还有移除比较隐藏的执行重复: login() 函数并不需要调用 checkIfUserExisted(),只需要调用一次 getUserByEmail() ,从数据库中获取用户的 email、password 等信息,然后跟输入的 email、password 信息做比对,判断是否登录成功。

这样的优化是很有必要的。因为 checkIfUserExisted()getUserByEmail() 都需要查询数据库,而数据库类的 I/O 操作是比较耗时的。我们在写代码的时候,应该尽量减少 I/O 操作。

按照刚刚的思路,我们重构下代码。

public class UserService {private UserRepo userRepo; // 通过依赖注入或者IOC框架注入public User login(String email, String password) {if (EmailValidation.validate(email)) {// throw InvalidEmailException...}if (PasswordValidation.validate(password)) {// throw InvalidPasswordException...}User user = userRepo.getUserByEmail(email);if (user == null || !password.equals(user.getPassword())) {// throw AuthenticationFailureException...}return user;}
}public class UserRepo {public boolean checkIfUserExisted(String email, String password) {// query db to check if email&password exists...}public User getUserByEmail(String email) {// query db to get user by email...}
}

代码复用性(Code Reusability)

什么是代码的复用性?

首先区分三个概念: 代码复用性(Code Reusability)、代码复用(Code Reuse)、DRY 原则。

  • 代码复用表示一种行为:我们在开发新功能的时候,尽量复用已存在的代码。
  • 代码复用性表示一段代码可被复用的特性或能力:我们在编写代码的时候,尽量让代码可复用。
  • DRY 原则是一条原则:不要写重复的代码。

首先,“不重复” 不代表 “可复用”。在一个项目中,可能不存在任何重复的代码,但也不表示里面有可复用的代码,不重复和可复用完全是两个概念。所以,从这个角度来说,DRY 原则和可复用性将的是两回事。

其次,“复用” 和 “可复用性” 关注角度不同。代码 “可复用性” 是从代码开发者的角度来讲的,“复用” 是从代码使用者角度来讲的。比如 A 同事编写了一个 UrlUrils 类,代码的 “可复用性” 很好。同事 B 在开发新功能是,直接 “复用” A 同事编写的 UrlUrils 类。

虽然复用性、复用、DRY 原则这三者从理解上有区别,但是它们的目的是一样的,都是为了较少代码量,提高代码的可读性、可维护性。此外,复用已经过测试的老代码,bug 会比从零开发的要少。

“复用” 这个概念不仅可以指导细粒度的模块、类、函数的设计开发,实际上,一些框架、类库、组件等的生产也都是为了达到复用的目的。比如,Spring 框架、UI 组件等等。

怎么提高代码复用性?

一共有 7 条规则:

  • 减少代码耦合。对于高耦合的代码,当希望复用其中的一个功能,想把这个功能的代码抽取出来成为一个独立的模块、类或者函数时,往往会牵一发而动全身。所以,高耦合度的代码会影响到代码的可复用性。
  • 满足单一职责原则。 前面讲过,如果职责不够单一,模块、类设计得大而全,那就增加了代码的耦合度(依赖它的,它依赖的代码就会比较多)。也会影响到代码的可复用性。相反,粒度越细的代码,代码的通用性会越好,容易被复用。
  • 模块化。这里的 “模块”,不单单只一组类构成的模块,还可以理解为单个类、函数。我们要善于将功能独立的代码,封装成模块。独立的模块就像积木,更加容易复用,直接拿来搭建更加复杂的系统。
  • 业务与非业务逻辑分离。越是和业务无关的代码余额容易复用,越是针对特定业务的代码越难复用。所以,为了复用跟业务无关的代码,我们将业务和非业务逻辑代码分离,抽取成一些通用的框架、类库、组件等。
  • 通用代码下层。从分层角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。一般情况下,在代码分层之后,为了避免交叉调用导致调用关系混乱,我们只允许上层代码调用下层代码及同层代码,杜绝下层代码调用上层代码。所以,通用的代码我们尽量下沉到更下层。
  • 继承、多态、抽象、封装。在讲面向对象特性的时候,我们讲过,利用继承可以将公共代码抽取到父类,子类复用父类的属性和方法。利用多态,可以动态替换一段代码的部分逻辑,让这段代码可复用。此外,抽象和封装,从更加广义的层面、而非狭义的面向对象特性层面来理解的话,越抽象、越不依赖具体实现,越容易复用。代码封装成模块,隐藏可变细节、暴露不变的接口,就越容易复用。
  • 应用模板等设计模式。一些设计模式,也能提高代码复用性。比如,模板模式利用了多态技术来实现,可以灵活地替换其中的部分代码,整个流程模板代码可复用。

除了上面讲到的 7 点,还有一些跟编程语言相关的特性,也可以提高代码的复用性,比如泛型编程等。另外,除了上面讲到的知识,复用意识也很重要。在写代码的时候,要取多思考,这部分代码是否可以抽取出来,作为一个独立模块、类或者函数供多出使用。在设计每个模块、类、函数的时候,要像设计一个外部 API 一样,去思考它的复用性。

辩证思考和灵活应用

编写可复用的代码并不简单。如果在编写代码时,已经有复用的需求场景,那根据复用的需求去开发可复用的代码,可能还不算难。但是,如果当下没有复用的需求,只是希望现在编写的代码具有可复用的特点,能在未来某个同事开发某个新功能时复用得上。在这种没有具体复用需求的情况下,就需要去预测未来代码会如何复用,这就比较有挑战了。

实际上,除非有明确的复用需求,否则,为了暂时用不到的复用需求,花费太多时间、精力,投入太多成的开发成本,并不是一个值得推荐的做法。也违反我们之前讲到的 YAGNI 原则。

实际上,我们在第一次写代码的时候如果当下没有复用的需求,而未来的需求也不是特别明确,并且开发复用代码的成本比较高,那我们就不需要考虑它的复用性。在之后,开发新能够的时候,发现可以复用的之前的代码,那我们就重构它,让其变得更加复用。

总结

1.DRY 原则

讲解了三种重复的情况:实现逻辑重复、语义重复、执行逻辑重复。

  • 实现逻辑重复,但功能语义不重复,并不违反 DRY 原则。
  • 实现逻辑不重复,但是功能语义重复,则违反了 DRY 原则。
  • 此外,代码执行重复也是违反 DRY 原则。

2.代码复用性

提高代码可复用性的 7 点方法:

  • 减少代码耦合
  • 满足单一职责原则
  • 模块化
  • 业务与非业务逻辑分离
  • 通用代码下沉
  • 继承、多态、抽象、封装
  • 应用模板等设计模式

除了上面降到的方法外,复用意识也非常重要。在设计每个模块、类、函数时,要像设计一个外部 API 一样思考它的复用性。

在第一编写代码时,如果当下没有复用需求,而未来的复用需求也不是特别明确,并且开发可复用代码的成本比较高,那我们就不需要考虑代码的复用性。在之后开发新功能时,发现可以复用之前的代码,那我们就重构这段代码,让其变得更加可复用。

相对于代码可复用,DRY 原则适用性更强一些。我们可以不写可复用的代码,但一定不能写重复的代码。

相关文章:

设计模式学习笔记 - 设计原则 - 7.DRY 原则及提高代码复用性

前言 DRY 原则&#xff0c;英文描述为&#xff1a; Don’t Repeat Yourself。中文直译&#xff1a;不要重复自己。将它应用在编程中&#xff0c;可理解为&#xff1a;不要写重读的代码。 可能你认为&#xff0c;这个原则很简单。只要两段代码长得一样&#xff0c;那就是违反 …...

方法的调用

自定函数(方法) 函数(方法): 给定一个具有独立功能的代码片段进行"命名",并通过该该类名调用"方法" main主函数 在当前类中,可以直接调用方法(因为方法使用了static关键字) package study;import java.time.LocalDate; import java.time.format.Date…...

VGW在 Windows 平台上局域网就绪的旁路由器程序

在查阅本篇文章之前可以查看下&#xff0c;本人前两年写的关于VGW软件路由器的文章 Linux 平台上面单网卡 TUN/TAP实现局域网其它设备上网_linux 物理网卡与tun同网段-CSDN博客 VGW软件路由器是一个工作IEEE以太网&#xff08;L2&#xff09;链路层的路由器程序&#xff0c;它…...

能源大数据采集,为您提供专业数据采集服务

随着经济的不断发展&#xff0c;能源产业也逐渐成为国民经济的支柱产业之一。而对于能源行业来说&#xff0c;数据采集是一项至关重要的工作。以往&#xff0c;能源企业采集数据主要依靠人工收集、整理&#xff0c;但是这种方式不仅效率低下&#xff0c;而且容易出现数据不准确…...

01_Maven

文章目录 Maven安装MavenMaven的工作流程配置MavenMaven的使用module和project的关系如何用Maven导包 如何用Maven进行项目构建指令介绍clean指令compile指令package指令install指令 Maven的依赖管理如何导包scope作用域依赖传递依赖冲突 使用Maven开发项目Junit如何使用Junit …...

C语言题目练习

目录 前言 1、转置矩阵 1.1 题目 描述 输入描述&#xff1a; 输出描述&#xff1a; 1.2解题 分析&#xff1a; 程序&#xff1a; 2、KiKi判断上三角矩阵 2.1 题目 描述 输入描述&#xff1a; 输出描述&#xff1a; 2.2 解题 分析&#xff1a; 程序&#xff1a; 3、…...

物联网安全|TrustAsia助力PSWG应对全球物联网产品安全合规挑战

万物互联时代&#xff0c;随着物联网连接数快速增长&#xff0c;物联网设备的潜在网络安全隐患也日益增长&#xff0c;可能导致设备故障、数据被盗、篡改、隐私泄露等问题的发生&#xff0c;甚至成为网络攻击的跳板&#xff0c;对互联网基础设施构成严重威胁。 我们看到&#…...

基于单片机的医院输液系统设计

目 录 摘 要 Ⅰ Abstract Ⅱ 引 言 1 1系统方案设计与论证 3 1.1系统硬件结构总体设计方案 3 1.2点滴速度测量电路方案的选择与论证 3 1.3液面检测电路方案的选择与论证 4 1.4通过电机控制滴速电路的方案与论证 4 1.5显示器接口电路方案选择与论证 5 1.6键盘接口电路方案选择与…...

安卓简单登录

注意 有的朋友不知道登录咋写&#xff0c;这里我就简单给出相应代码&#xff0c;用的本地存储&#xff0c;没用网络请求&#xff0c;有需要可以替换成想要的&#xff0c;废话不多上代码 登录 import androidx.appcompat.app.AppCompatActivity;import android.content.Context…...

【计算机网络】DNS/ICMP协议/NAT技术

文章目录 一、DNS(Domain Name System)1.DNS背景2.域名3.浏览器中输入url后,发生的事情 二、ICMP协议1.什么是ICMP协议2.ICM功能3.ICMP的报文格式4.ping命令5.traceroute命令 三、NAT技术1.NAT技术背景2.NAT IP转换过程3.NAPT4.NAT技术的缺陷5.NAT和代理服务器 四、TCP/IP五层模…...

2403C++,C++20协程通道

原文 通道是一个可用来连接协程,实现不同协程间通信的并发安全队列. Test fun test know channel() runBlocking<Unit> {val channel Channel<Int>()//生产者val producer GlobalScope.launch {var i 0while (true) {delay(1000)channel.send(i)println("…...

C语言从入门到实战——预处理详解

预处理详解 前言一、预定义符号1.1 __FILE__1.2__LINE__1.3 __DATE__1.4__TIME__1.5__STDC__ 二、 #define定义常量三、 #define定义宏四、 带有副作用的宏参数五、 宏替换的规则六、宏函数的对比七、 #和##7.1 #运算符7.2 ##运算符 八、 命名约定九、 #undef十、命令行定义十一…...

【LabVIEW FPGA】CIC滤波器

一、CIC滤波器应用概述 在通信数字信号上下变频时&#xff0c;经常会用到对数字信号的升采样和降采样&#xff0c;即通过CIC数字速率器实现变采样率。 二、滤波器IP 首先设置滤波器基本参数&#xff08;filter specification&#xff09; 滤波器类型&#xff08;Filter Type…...

砝码称重 蓝桥杯

在C中&#xff0c;fabs()和abs()都用于计算数字的绝对值&#xff0c;但它们之间有一些区别。 fabs(double x)&#xff1a;计算浮点数x的绝对值&#xff0c;返回一个double类型的结果。 abs(int x)&#xff1a;计算整数x的绝对值&#xff0c;返回一个int类型的结果。 数组的默…...

AmzTrends x TiDB Serverless:通过云原生改造实现全局成本降低 80%

本文介绍了厦门笛卡尔数据&#xff08;AmzTrends&#xff09;在面临数据存储挑战时&#xff0c;选择将其数据分析服务迁移到 TiDB Serverless 的思路和实践。通过全托管的数据库服务&#xff0c;AmzTrends 实现了全局成本降低 80% 的效果&#xff0c;同时也充分展示了 TiDB Ser…...

[最佳实践] Windows上构建一个和Linux类似的Terminal

感谢大佬批评指正&#xff0c;现已更新 preview Target&#xff1a;致力打造最赏心悦目Window下的终端&#xff0c;同时能够很接近Linux的使用习惯 key word&#xff1a;windows终端美化 windows terminal windows powershell 类似Linux下的Window终端 Window也能用ll windows…...

租赁系统|手机租赁软件|租赁系统功能开发

当如今的生活用品越来越多、交流更加便捷时&#xff0c;人们的消费需求也变得越来越丰富。不可避免地&#xff0c;我们会遇到这样一种情况&#xff1a;需要新的手机&#xff0c;但资金有限。此时&#xff0c;手机租赁小程序呼之欲出。这种创意不仅使我们能够充分利用各类闲置物…...

【设计模式 04】建造者模式

如果要构建的对象很复杂&#xff0c;那么可以将整个构建过程拆分成多个步骤&#xff0c;并为每一个步骤定义一个抽象的接口。并添加一个指导者用来控制构建产品的顺序和步骤。 Java实现&#xff1a; // 产品类 class Product {private String part1;private String part2;pub…...

Python使用错误总结

【1】cannot import name ‘ParameterSource’ from ‘click.core’ 其根本原因在于是black模块&#xff0c;其模块版本可能过时&#xff0c;升级black模块版本即可&#xff1a; pip install black --upgrade【2】partially initialized module ‘charset_normalizer’ has n…...

【Java EE初阶三十】JVM的简单学习

1. JVM 内存区域划分 一个运行起来的 Java 进程&#xff0c;就是一个 JVM 虚拟机&#xff0c;需要从操作系统申请一大块内存&#xff0c;就会把这个内存&#xff0c;划分成不同的区域&#xff0c;每个区域都有不同的作用. JVM 申请了一大块内存之后,也会划分成不同的内…...

thinkphp5水平分割表partition,以及查询操作

前言 先交代下背景,在一个项目中,有一个数据表有水平分表的需求。当时想找到一种方法&#xff0c;把对数据库的操作&#xff0c;写到一个模型里&#xff0c;通过去换模型属性中的table来达到代码不变操作的数据表变化的效果。 我们都知道&#xff0c;模型要想关联数据表的话&a…...

docker部署aria2-pro

前言 我平时有一些下载视频和一些资源文件的需求&#xff0c;有时候需要离线下载&#xff0c;也要速度比较快的方式 之前我是用家里的玩客云绝育之后不再写盘当下载机用的&#xff0c;但是限制很多 我发现了aria2 这个下载器非常适合我&#xff0c;而有个大佬又在原来的基础…...

vue中Mixins

使用 Mixins 的主要优点包括&#xff1a; 代码复用: 可以将常用的逻辑封装在 Mixin 中&#xff0c;然后在多个组件中重复使用。逻辑分离: 将不同功能的代码分开管理&#xff0c;使代码更加清晰和易于维护。灵活性: Mixins 允许你在组件中引入多个 Mixin&#xff0c;并且可以根…...

linux常用指令(定期更新)

linux常用指令 1.页相关页大小 2.系统参数3.启动参数4.网络参数查询网卡所属numa节点信息网络测速相关iperf测试sar监控网卡流量查看网卡txqueuelen和mtu抓包tcpdump 网络数据收发状态snmp协议栈netstat -i所有网口TX-OK、RX-OKnetstat-s查看各个协议的收发数据ethtool -S单个网…...

【项目】图书管理系统

目录 前言&#xff1a; 项目要求&#xff1a; 知识储备&#xff1a; 代码实现&#xff1a; Main&#xff1a; Books包&#xff1a; Book&#xff1a; BookList&#xff1a; Operate包&#xff1a; Operate: addOperate: deleteOperate: exitOperate: findOperate:…...

华为OD机试 - 疫情扩散时间计算 - 矩阵(Java 2024 C卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&am…...

[数据集][图像分类]棉花叶子病害分类数据集2293张4类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;2293 分类类别数&#xff1a;4 类别名称:["diseased_cotton_leaf"…...

《辐射4》是一款什么样的游戏 怎样在mac电脑上玩到《辐射4》辐射4攻略 辐射4开局加点 怎么在Mac电脑玩Steam游戏

辐射4&#xff08;Fallout 4&#xff09;是由Bethesda开发的一款动作角色扮演类游戏&#xff0c;为《辐射》系列游戏作品的第四代&#xff0c;于2015年11月10日发行。游戏叙述了主角一家在核爆当天&#xff08;2077年10月23日&#xff09;&#xff0c;被Vault-Tec&#xff08;避…...

视频推拉流EasyDSS平台直播通道重连无法转推的原因排查与解决

视频推拉流EasyDSS视频直播点播平台&#xff0c;集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务。 用户使用EasyDSS平台对直播通道进行转推&#xff0c;发现只要关闭…...

Javaweb之SpringBootWeb案例之自动配置案例的自定义starter测试的详细解析

3.2.4.3 自定义starter测试 阿里云OSS的starter我们刚才已经定义好了&#xff0c;接下来我们就来做一个测试。 今天的课程资料当中&#xff0c;提供了一个自定义starter的测试工程。我们直接打开文件夹&#xff0c;里面有一个测试工程。测试工程就是springboot-autoconfigurat…...