一个简单需求竟让我改了十几处代码,必须控诉到底什么是重复代码!

开发 前端
我们这行都很幽默 ,总说编程就是CV,自黑写程序大部分都是靠复制粘贴。实际上,很多高级或资深程序员写代码也就是CV:把其他项目里的一段代码复制过来,稍加改动,run 一下没问题就能上线。

 [[437430]]

正文

我们这行都很幽默 ,总说编程就是CV,自黑写程序大部分都是靠复制粘贴。实际上,很多高级或资深程序员写代码也就是CV:把其他项目里的一段代码复制过来,稍加改动,run 一下没问题就能上线。

但这种做法就是在给子孙后代挖坑!

通常只要这些CV代码其中有一点逻辑要修改,就意味着所有CV的地方都要修改。因此很多人实际开发时,发现明明很简单的小需求,你却要改很多地方,需要花很长时间和测试解释如何才能测到,搞的PMO和领导,对你的人效都很不满意!只要你少改一处,就意味着留下一处潜在的bug,然后发布生产时发现又有各种小问题,继续回退并通宵 hotfix,最后重新发版。

发现CV的致命点了吗?这种做法是最容易产生重复代码的地方,所以 Do Not CV!

正确姿势:

  • 提取出方法fun
  • 在需要的地方调用fun方法

CV 的重复代码相对容易发现,但有些代码结构类似,这也是重复代码!然而有些人却对这类坏味道视而不见。其实还有很多制造重复代码的不竭恶意动力:

  • 代码结构设计不合理,导致同一实现散落各处

由于项目初期的代码结构设计不合理,导致后续开发时无法快速找到现有实现,或找到了但是不好直接引用

改进:对不合理的地方及时重构

  • 为了稳定性,坚死活不动老逻辑

拷贝一份。由于对于业务不熟 && 对自身代码能力不信任不敢重构

改进:通过微重构进行多次迭代小改进,慢慢优化

  • 互联网敏捷开发,“快”

由于时间紧张或者能力问题无法识别出的坏代码。

改进:提升能力

重复结构

某系统要将作品相关信息发送给翻译引擎:

  • sendBook 把作品信息发出去
  • sendChapter 把章节发出去
  • startTranslation 启动翻译

这几个业务都是以后台执行,所以,方法签名都增加了一个 Task注解,表明是任务调度入口。实际代码执行放到对应service业务方法。

够简洁了吗,但这段代码结构却是有重复的,注意catch语句。之所以要catch,是为防止系统出问题而无人发觉。捕获到异常后,把错误信息通过IM发给相关负责人。注意该逻辑是后来加上的,所以,这段代码作者不厌其烦地在每处都修改了代码。

虽然这三个函数调用的业务代码不同,但结构一致,基本流程可概括为:

  • 调用业务函数
  • 若出错,发通知

当你能够发现结构上的重复,我们就可以把这个结构提取出来。从OOP来说,就是提炼出一个接口:

使用函数式编程简化代码:

若再有一些通用结构调整,如在任务执行前后要加上一些日志信息,这样改动就可放到 executeTask 方法里,而不用四处改!还容易漏!

所以这类问题其实不复杂,关键在于发现结构重复。相比直接CV,结构重复极具迷惑性。很难让人一下反应出来干的三件事,居然也是重复代码。

一般参数是名词,而方法调用是动词。动词不同时,并不代表没有重复代码!懂得这点,就比较容易发现结构相似了。

比如案例中的:发作品信息、发章节、启动翻译,看上去是三件不同事,只是因为动词不同,但除了动词,其它部分都相同!所以,它们属于结构重复。

做真正的选择

对章节内容进行编辑。有一个业务逻辑,章节只有在审核通过之后,才能去做后续的处理,比如,章节的翻译。所以,这里的 editChapter 方法最后那个参数表示是否审核通过。

在这段代码里面,目前的处理逻辑:

  • 如果这个章节是由作者来编辑的,那么这个章节是需要审核的
  • 如果这个章节是由编辑来编辑的,那么审核就直接通过了,因为编辑本身同时也是审核人

if 选择的到底是啥?

感觉 if 选择的一定是两段不同业务处理。

但仔细观察,发现if 和 else 两段代码几乎一样,仅最后一个参数不同而已。

没错,这也是一种重复代码。只是这种重复代码是你自己新写出来的,而非CV所得。

因为写时,只想到 if 判断后要做啥,而未曾想过 if 语句判断的到底是啥,客观上就很容易导致重复。

写代码要有表达性

准确表达意图,是优秀代码基本要求。而这里的 if 判断区分的是参数,而非动作。可微调这段代码:

这里我把 user.isEditor() 判断结果赋值给 approved boolean变量,而 非直接作为参数传给 editChapter,这波操作只为提高代码可读性。

因为 editChapter 最后一个参数表示该章是否审核通过,引入了 approved 变量,即可清楚地看到,一个章节审核是否通过的判断条件是“用户是否是一个编辑”,这样代码更清晰!

若将来审核通过条件变了,变化点都在 approved 变量赋值这一处而已!若你追求更具表达性的做法,可提取一个方法,变化都放到该方法里:

实际项目经常能看到 if 语句未有效选择目标,有的是参数列表特长,有的是在 if 代码块有多个语句。

所以,只要你看到 if 语句出现,而且 if 和 else 的代码块长得又比较像,多半就是出现了这类坏味道。

不想玩“找茬”,给自己日后开发留坑,就尽快重构吧!

Don’t Repeat Yourself(DRY,不要重复自己)

在一个系统中,每一处知识都必须有单一、明确、权威地表述。Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

写代码要想做到 DRY,一个关键点是能够发现重复。发现重复:

  • 在沼泽中挣扎后,被动发现
  • 提升自己的鉴别能力,主动发现重复

这种主动识别的能力,其实背后要有对软件设计更好的理解,尤其是分离关注点。

本文转载自微信公众号「JavaEdge」,可以通过以下二维码关注。转载本文请联系JavaEdge公众号。

 

 

责任编辑:武晓燕 来源: JavaEdge
相关推荐

2021-12-28 10:28:41

代码

2019-04-15 14:03:50

代码软件编码

2017-01-03 15:38:08

Android

2021-07-30 15:31:35

代码重用漏洞攻击

2019-05-23 08:55:41

代码开发工具

2011-09-05 10:30:51

重构代码库业务模型

2019-12-23 11:03:07

抽象MOVJava

2024-12-03 09:23:20

2022-03-07 05:53:41

线程CPU代码

2014-09-18 10:50:26

创业

2018-10-11 20:38:27

大数据生态圈分布式

2021-12-02 06:08:36

物联网IOT物联网技术

2014-07-08 09:21:10

死代码创意歌曲

2015-06-05 09:15:37

移动开发者

2013-05-21 09:32:11

ChromebookChrome OS

2022-10-19 11:17:35

2022-10-31 07:09:15

拷贝代码项目

2012-11-28 13:25:27

程序员

2018-06-23 08:02:31

程序员代码故事

2010-06-28 15:58:45

EclipseJavaIDE
点赞
收藏

51CTO技术栈公众号