聊一聊代码重构——代码中究竟存在哪些坏代码

代码重构相关内容

聊一聊代码重构——我们为什么要代码重构

聊一聊代码重构——代码中究竟存在哪些坏代码

聊一聊代码重构——关于变量的代码实践

聊一聊代码重构——关于循环逻辑的代码实践

聊一聊代码重构——关于条件表达式的代码实践

聊一聊代码重构——程序方法上的代码实践

聊一聊代码重构——程序方法和类上的代码实践

聊一聊代码重构——存在继承关系类上的代码实践

聊一聊代码重构——封装集合和替换算法的代码实践


如果计划重构代码,那么首先要做的是发现坏的代码。坏的代码会导致可读性、可维护性都变的很差。但是坏的代码不仅仅是某一种问题产生的,很多时候是多种问题混合的结果。如果我们尝试重构这些内容,就需要区分不同的错误原因。只有找到不同的错误才能选择不同的处理方式。

过长的方法

长方法绝对是最先被发现的坏代码。长方法通常通常包含大量的代码行数和复杂的业务逻辑。这种情况导致这直接导致2个问题。

  1. 我们无法用一个简单的方法名去描述它,甚至使用大量注释也很难描述。
  2. 上一个原因直接导致我们必须深入代码去理解其内容,而大多数长方法并不具备清晰的逻辑。存在大量的变量或者逻辑控制语句,最终让阅读者迷失在代码中。

针对方法长度,其实阿里规约中也有提到单个方法的总行数不超过 80 行,尝试限制过长的方法,也是对开发者的约束,强迫开发者尝试对自己实现的逻辑进行归纳整理。将长方法分解为若干小方法,我们更容易看清业务的脉络。

大类

大类很多时候会和过长方法一起出现,都是多种职责的代码集中出现的原因。大类多数会出现下面的问题。

  1. 职责过多,一个类承担了大量业务,使得该类的职责过于庞大。并且不同的职责都在一起,导致尝试理解某项业务时很可能被其他逻辑所干扰。
  2. 耦合严重,伴随职责增加,这个类会和其他类存在大量业务交叉。甚至可能出现循环交叉。
  3. 使用这些类的方法会很困难,大类中通常会有一些类似接口的存在,在不熟悉其逻辑情况下很难确定需要使用的接口。
  4. 代码冗余,如果一个大类职责单一,那么很可能出现类似方法被重复的创建可能。

莫名其妙的名字

很多时候有些开发同学喜欢为同时出现的数据通过字母或者数字来区分他们(如:user1、user2)。或者在一些条件控制语句的变量标识提供一个特殊的名字如specialType。对于后续开发者看起来,看起来像是做了区分但是并不知道哪些地方不同,对于控制语句看起来标识了他是特殊的,但是他的特殊性只能通过查看后续代码才能了解。

另外有些时候很多开发同学习惯将返回值和参数拼接成一个方法的名称。对于简单逻辑来说这无疑是个很方便的操作。但是当方法中存在一些复杂的查询逻辑时,上面的命名方式无助于理解里面的内容。这导致其他开发人员看到方法名时莫名其妙,只有认真阅读完内部逻辑才能清楚方法的作用。

过长的参数列表

过长的参数会导致方法的复用度大幅度下降。即使尝试复用,我们也需要为一些我们不需要的参数设置值来保证参数的数量是正确的。导致调用此方法的过程变得让人非常迷惑。我们不得不在调用方法的时候填写长长的一串参数。这导致使用这些方法时很容易出现错误,尤其是方法的数据类型一致的时候,错误概率大大提高。参数列表过长,函数的调用方需要花费更多的时间理解每个参数的含义,降低了代码的可读性。

全局变量

全局变量绝对是BUG重灾的地方。在一些复杂业务中使用全局变量很难实现预定的目标。

在业务中我们尝试通过修改全局变量来影响所有线程,但是无法确认这些线程目前正在进行的业务逻辑。而更在多节点情况下,全局变量更是只能影响单节点的内容。

当然全局变量可以作为一个启动时加载的配置项,后续代码中不会尝试修改其内容。这种情况下其可以被正常使用。

到处都存在的调整

一些公用的值应该被设置到一个固定的常量或者枚举中,这样在后续进行修改的时候我们可以只修改一处。但是实际情况有些老代码在最开始只是一个很简单的业务,随着时间推移越来越多的业务从这个简单业务上发展出来。最开始用了一些固定字符串或者值来维护的类型区分最后会散落到各处的代码中。

而另外一种极端情况就是一些规则本来应该被复用,但是后续接手的开发同学并不希望对原有代码进行调整,他会复制一份旧代码在复制出来的代码中进行调整。直到某一天需求人员想要修改这个处理规则。这个时候一个是需要调整很多地方的逻辑,一次修改这么多内容你需要测试的内容就变得非常多。另外一个更要命的问题,你没办法确定修改了所有的代码。如果代码经历了很多开发,中间有些开发可能会根据个人代码风格进行调整,你无法通过简单的搜索就定位到所有内容。

到处存在的互相调用

随着业务越来越复杂,多个功能发生业务交叉是很正常的事情。但是如果一个完整的业务中多个功能之间的数据沟通出现在代码的各个地方。而慢慢的要想搞清楚功能之间数据交换的逻辑和规律就变的非常复杂了。更麻烦的是很多时候你尝试从一个接口穿透的另外一个接口时发现他内部又调用了其他业务。追踪到最后自己都搞不清楚链路到哪里了。

实际开发中更多的数据处理应该是内部的数据交互。我们应该尽可能的将功能之间的数据交流减少并且集中在可以被统一管理的地方。这样在处理跨功能的问题上,会很有帮助。

数据泥潭

在一个方法中使用大量名称类似类型相同的变量,或者使用对象不同但是属性名称相同的数据。实际开发中这种场景也是很容易出现。比如前端传递参数的DTO对象和后端数据库对应DO是一样的,然后需要进行参数的对比。比如我们需要一些数值对比然后觉得设置哪个结果。当然现实中这部分代码最终都是单独的一个函数或者用BeanUtil的方法进行设置。实际开发中在同一个方法或者相近的几行代码中存在类似的名字还是很常见的。如果很少的变量问题还不大,但是大量的类似字段被平铺到一起,会导致后续修改出问题的概率大大增加。

复杂判断

很多时候看到那种在if语句中设置了4个甚至更多判断项的时候,总是在想真的要怎么写么?实际上很多时候都是最开始只有1-2个判断。后面在有新需求的时候,直接在后面加一个判断项,随着业务增加判断项也越来越多。见过夸张的情况是if的判断项超过了20个。复杂的判断项会使其他开发在阅读这块逻辑的时候花费更多的时间,尤其是一些关联性强的判断项,很多时候还需要阅读则取思考其关联性。如果判断项的内容是不同的判断逻辑,且有先后顺序能否进行一些判断嵌套?如果判断项内容是一些关联性很强的业务的不同类型,知否能抽出为一个单独的函数。这些操作都能让判断变得更容易理解。

循环嵌套语句

循环嵌套的问题在于出现多层嵌套的时候,在最深的那些层级使用上层的循环值的时候。我们需要花一些时间思考这些参数在当前循环的值或者含义。而且稍有走神就有可能将当前循环参数识别到错误的循环中。一般来说这种循环嵌套应该不大于3层。在之前超过这个数值的循环会被抽取到一个单独的方法中,现在可以使用lambda表达式让循环变得更加容易被理解。

冗赘的元素

很多时候我们设计参数或者属性的时候可能想到未来的变化,会多设计一些冗余的内容。但实际上这些变化并没有出现,这些冗余内容就留在系统中。

另外一种场景就是随着一些功能的缩减或者剥离。导致某些逻辑中的参数本来是有用的,但是现在已经不再使用。这些内容留在那里并不会对系统带来影响,但是对于后来接手的开发者可能会产生理解上的困难。如果没有足够的注释他可能不理解为什么会出现一个未曾使用的放在那里。如果后续需要进行重构或者扩展,他不得不将这些内容持续的携带到新的逻辑中。这些已经不使用的内容,应该尽早移除。后续想查看这部分内容应该从代码修改历史中去查看。

过深的调用关系

有时候你看到一个很简单的方法,点进去后发现跳转到了另外一个对象,然后其内部又进入了其他的对象里面。这个过程会很深,这个时候这个功能出现了和多个业务进行强耦合的情况。这个调用链中任何一个接口发生调整都会影响到这个功能的结果。一般这个时候需要对整个功能进行梳理:

  1. 是不是需要这么多流程
  2. 涉及的方法是否可以整合
  3. 如果前两项都无法进行优化,那需要思考真的需要这么复杂的业务么?

一项功能关联的业务越多,出问题的概率也越大,这个功能可靠性就变得很低。所以这个时候就要思考业务设计的问题了。

类似的类

如果要开发一个已经实现的功能,但是和之前有些许差别的新功能。对于有些开发者来说就是复制、粘贴、批量修改名称。如此一个功能完成了,然后两个类似的类出现了。
这是可能发生么,对于质量控制不严的产品里面,这样场景会频繁发生。有的是复制某些方法、有的是复制某些类。
另外在开发一些功能在多种路径的不同处理方式时,我们可能把这些路径放在自己单独的实现类里面,这个时候也会出现很多相同的内容。
上面这些场景我们可以使用父类将这部分相同的内容在抽象类中进行实现,不同功能的子类只需要实现自己的逻辑。

注释

注释不仅仅是写和不写的问题,更麻烦的是如何写好的问题。
对于没有注释的代码我们可以直接发现,但是很多时候错误的注释我们只有在阅读了其逻辑后才能发现。
我们通过单元测试来保障代码的正确性,但是对于注释我们没有这类工具。很多时候在修改代码实现逻辑的时候会忽略掉对注释的修改,这直接导致注释的过时。当然还有一种更离谱但是却可能出现的情况,就是这段代码就是从其他地方复制过来的,这直接导致用其他的业务来描述这段代码。
很多时候我们关注的是注释的有无、注释的对错。但是注释的质量也是一个非常重要的内容。而且这部分是一个很难被规范的内容。很多规范书中会提到注释不能太少,注释也不能过多。我们总是拿这些设置了xxx值进行了xxx判断这种简单的场景来介绍过度注释。但是有些时候某些判断是一个很关键的判断,我们需要给后续开发者提醒这是个非常严重的改动,而另外有些方法有很长的内容,而其只是网络上的某个工具,不需要描述过多。
另外一种影响注释质量的地方就是开发者对逻辑理解的输出能力。这里并不是说代码能力,而是文字水平。针对一些业务的入口方法,他要描述好传入数据可能带来的变化影响的范围,但是实际呢很多开发只是将这些数据作用机械的串在一起。你可能需要多读几遍才能串联起相关意思。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值