转载

拒绝重复代码

拒绝重复

软件是让机器有了指令能执行一系列的动作,可将重复的机械劳动自动化。软件工程师们大多数会对重复都深恶痛疾,一旦发现有重复的迹象,就会想尽办法用技术手段去解决它。重复代码也意味着重复劳动,每次变更都必须要同时修改好几个地方,很容易遗漏而出镨,因而我们相信没有人喜欢重复的代码。

但是,实际项目中的业务逻辑总是错综复杂,有很多看似重复的场景,却又不完全一样。虽然我们不喜欢重复,实际上受限项目时间与经验技能,又不知不觉地在制造重复。人大多都有惰性,编写代码也是从模仿开发,都会经过拷贝与粘贴的阶段,当完成了软件开发任务,再也没有回过来再看看我们写的代码。久而久之,软件中充斥着大量的重复、相似的代码。他们的持续存在造成了代码可维护性差,代码质量下降。

Robert C.Martin 在他的代码整洁之道一书中写道:

重复可能是软件中一切邪恶的根源,许多原则与实践规则都是为了控制与消除重复而创建。…… 软件开发领域的所有创新都是不断在尝试从源代码中消除重复。

重复类型

何谓重复代码,简言之,就是指重复或近似的代码。

重复代码都有哪些类型?最为简单明了的是完全一样的代码片段,其它的有如下,可让我们更好识别重复:

  • 类型Ⅰ,代码片段中除了空格、注释以及换行以外的内容 完全一致
  • 类型Ⅱ,代码片段中除了空格、注释、换行以及 变量名以外 的内容完全一致
  • 类型Ⅲ,代码片段中除了空格、注释、换行以及变量名以外的语句可能有 增删改,功能不变
  • 类型Ⅳ,两个或更多个代码段执行 相同的运算 ,但通过 不同的语法和变量 来实现。

重复原因

消除复杂代码除了需要技巧之外,个人觉得存在重复代码主观上更多是一种工作态度的反映,需要我们有责任心,兴趣与热情。

  • 懒惰,能够容忍不好的代码
    • 打一份工的心态,做完这份就去做其它的,哪管生死
    • 进度很紧,以完成任务为优先,后续没有再进行优化
  • 技能不足,出现不必要的重复代码
    • 老人留下的祖传代码,又缺少文档说明与测试用例,根本不敢随便动
    • 新人进来,一是对整体代码不熟悉,二是从模仿开发,Ctrl+C/Ctrl+V
  • 缺乏沟通,团队之间协作不够
    • 没有 code review 机制,缺少代码持续看护
    • 团队成员间缺少交流,每个人自扫门前雪

除主观原因之外,在软件架构与设计上不恰当,也会导致重复代码(更多情况是相似代码):

  • 模块的职责分离不清晰:职责的分配不准确,就可能导致代码结构不清晰,也就可能导致代码的重复。比如说,每个模块都需要访问数据库,都会涉及到数据库的映射,事务管理等,如果能把数据访问层分离出单独一层,就可能避免数据操作的重复代码。也就是我们需要在设计上就抽取公共的模块。
  • 模块内抽象的粒度过大:重用的关键是保持合适的粒度,以及对关系的解耦。粒度表现类一级,缺少可复用的辅助类或模板类,可以通过寻找共性,以泛化的方式提取共性特征。粒度表现在方法级,需要编写许多小的方法,找到类中可以重复调用的职责,抽取为单独的方法。

重复层次

像Java/C/C++语言,我们一般采用工具 SimianPMD 集成到CI来自动检查代码重复率。根据重复所在范围,我们通常可以分为如下几种:

在同一个类中重复

在同一个类中存在重复代码,通过走读代码就可以识别出来,也最容易解决。解决办法也相当简单,通常是采用 提取方法 来消除。

在同一个类树下重复

在同一个类树下的不同子类中重复,也比较能容易识别出来。可以通过 上移方法模板方法 将公共部分上移到共同的父类。如果方法中没有操作成员变量,可以提到辅助类。

在不相干的类中重复

在两个完全不相干的类中,如果不是专门地寻找很难发现,借助工具一般能扫描出来。 可以先 提取方法 ,然后 移动方法 到新建的类,来消除重复。

在不同的模块中重复

在不同的模块中存在重复代码,一般考虑提供公共的模块。提取公共模块可以消除重复,但也带来了依赖,提到公共横块中的代码应该是稳定的。公共模块通常需要从整个软件设计来思考,合理的模块划分能有效地消除大量的重复。

大量大段重复代码

如果出现大量大段重复的代码,如相似的配置项解释代码,当超过几百行时,我们可以考虑采用代码自动生成,虽没有消除重复代码,但消除重复劳动。有些语言提供代码宏机制,合理的使用宏也可以消除大段重复代码。

重复轮子

还有一种重复,是工具扫描不出来的,就是与其它项目的重复。在开源的世界里,提供了非常多并且成熟的轮子。有人说,软件工程师写多少代码不重要,重要的是解决问题的效率,而提升效率之一就是懂得使用已有的轮子。好的软件工程师,要善用前人打下的基础,站在成功的肩膀上。

最大的问题是我们不知有轮子存在,那我们怎么解决呢?开源社区总是存在非常多有热心的人,他们整理各个 awesome 项目,首先是要善用搜索与社区:

  • Java: https://github.com/akullpp/awesome-java
  • Java: https://github.com/jobbole/awesome-java-cn
  • Go: https://github.com/avelino/awesome-go

上面只是简单列出有哪些项目,还需要我们进一步的选型分析。对于Java软件工程师来说,已有足够多的工具库,常用他们也可以大量降低我们代码量:

  • Apache Commons: 最为常用的是 Lang , Collections , IO , Math 库
  • Guava: Guava教程

结语

消除重复代码的技能,没有什么特别的复杂的东西,无非就是提取函数,提取类,提取公共模块、复用现有的轮子。

消除重复代码,其实就是解放软件工程师自己的时间,只有正确的心态去面对,自己才会有更大的收获,而不是在无意义地重复地劳动。

解决重复并不困难,困难的是发现重复。发现重复并不困难,困难是培养发现重复的习惯。当我们开始习惯性地向内看看同组的代码,向看外看看开源代码,只有看得多才能有更开阔的眼界,消除重复就是轻而易举。

原文  http://lanlingzi.cn/post/thoughts/2019/0602_dry/
正文到此结束
Loading...