软件是让机器有了指令能执行一系列的动作,可将重复的机械劳动自动化。软件工程师们大多数会对重复都深恶痛疾,一旦发现有重复的迹象,就会想尽办法用技术手段去解决它。重复代码也意味着重复劳动,每次变更都必须要同时修改好几个地方,很容易遗漏而出镨,因而我们相信没有人喜欢重复的代码。
但是,实际项目中的业务逻辑总是错综复杂,有很多看似重复的场景,却又不完全一样。虽然我们不喜欢重复,实际上受限项目时间与经验技能,又不知不觉地在制造重复。人大多都有惰性,编写代码也是从模仿开发,都会经过拷贝与粘贴的阶段,当完成了软件开发任务,再也没有回过来再看看我们写的代码。久而久之,软件中充斥着大量的重复、相似的代码。他们的持续存在造成了代码可维护性差,代码质量下降。
Robert C.Martin
在他的代码整洁之道一书中写道:
重复可能是软件中一切邪恶的根源,许多原则与实践规则都是为了控制与消除重复而创建。…… 软件开发领域的所有创新都是不断在尝试从源代码中消除重复。
何谓重复代码,简言之,就是指重复或近似的代码。
重复代码都有哪些类型?最为简单明了的是完全一样的代码片段,其它的有如下,可让我们更好识别重复:
消除复杂代码除了需要技巧之外,个人觉得存在重复代码主观上更多是一种工作态度的反映,需要我们有责任心,兴趣与热情。
除主观原因之外,在软件架构与设计上不恰当,也会导致重复代码(更多情况是相似代码):
像Java/C/C++语言,我们一般采用工具 Simian
或 PMD
集成到CI来自动检查代码重复率。根据重复所在范围,我们通常可以分为如下几种:
在同一个类中存在重复代码,通过走读代码就可以识别出来,也最容易解决。解决办法也相当简单,通常是采用 提取方法
来消除。
在同一个类树下的不同子类中重复,也比较能容易识别出来。可以通过 上移方法
和 模板方法
将公共部分上移到共同的父类。如果方法中没有操作成员变量,可以提到辅助类。
在两个完全不相干的类中,如果不是专门地寻找很难发现,借助工具一般能扫描出来。 可以先 提取方法
,然后 移动方法
到新建的类,来消除重复。
在不同的模块中存在重复代码,一般考虑提供公共的模块。提取公共模块可以消除重复,但也带来了依赖,提到公共横块中的代码应该是稳定的。公共模块通常需要从整个软件设计来思考,合理的模块划分能有效地消除大量的重复。
如果出现大量大段重复的代码,如相似的配置项解释代码,当超过几百行时,我们可以考虑采用代码自动生成,虽没有消除重复代码,但消除重复劳动。有些语言提供代码宏机制,合理的使用宏也可以消除大段重复代码。
还有一种重复,是工具扫描不出来的,就是与其它项目的重复。在开源的世界里,提供了非常多并且成熟的轮子。有人说,软件工程师写多少代码不重要,重要的是解决问题的效率,而提升效率之一就是懂得使用已有的轮子。好的软件工程师,要善用前人打下的基础,站在成功的肩膀上。
最大的问题是我们不知有轮子存在,那我们怎么解决呢?开源社区总是存在非常多有热心的人,他们整理各个 awesome
项目,首先是要善用搜索与社区:
上面只是简单列出有哪些项目,还需要我们进一步的选型分析。对于Java软件工程师来说,已有足够多的工具库,常用他们也可以大量降低我们代码量:
消除重复代码的技能,没有什么特别的复杂的东西,无非就是提取函数,提取类,提取公共模块、复用现有的轮子。
消除重复代码,其实就是解放软件工程师自己的时间,只有正确的心态去面对,自己才会有更大的收获,而不是在无意义地重复地劳动。
解决重复并不困难,困难的是发现重复。发现重复并不困难,困难是培养发现重复的习惯。当我们开始习惯性地向内看看同组的代码,向看外看看开源代码,只有看得多才能有更开阔的眼界,消除重复就是轻而易举。