最近InfoQ上连载了郑烨
写的《代码之丑》系列文章
,好评不断,其实早在InfoQ开始连载前,我就在他的博客
上看到过了,当时就觉得这个系列写得很实在,应该让大家都知道什么样的代码是有问题的。
说起遗留代码,大家脑子里就会反映出代码质量差、难以理解等不好的印象,其实这些代码也是大家写出来的,它们也有还是新代码的时候,也许这段“遗留代码”就是两个月前你自己写的。
最近负责做新员工转正前的代码抽查工作,按照公司的简版代码规范对代码质量做评估,看了不少代码,大多数都能符合规范,但我却不能说这是好代码,因为当中充斥着不少坏味道。下面就列举了5个问题,与大家分享一下:
1、if的陷阱
这应该是个老生常谈的问题了,当if后只有一句语句时,可以不加大括号,其危害相信大家都知道。像FindBugs
这样的工具也会对此类代码做出提示(这次貌似FindBugs失灵了,估计是规则的问题);此外,清晰的代码缩进,也可以避免由于没有大括号而造成问题。
if (flag)
doSomethingA();
doSomethingC();
希望有人要加doSomethingB()的时候能先想清楚怎么加。
2、OO的封装性
public class ClassXXX {
private Abc abc;
private Def def;
Hij1Impl hij1;
private Klm klm;
...
}
这是一个Spring的Bean,刚看到这段代码时,当中那个hij1是不是出于什么特殊的原因而没有声明为private(要么大家都没,要么就都有,唯独它没有,就有点问题了),但询问了开发的同学后,他也说不出什么原因。
OO的三大基本特征之一就是封装,其他两点是继承与多态,如无必要,还是不要打破封装性,把对象的内部特征暴露在外。
3、依赖于接口而非实现
同样是上面的代码,这位同学还是很好地遵循了代码命名规范,于是一眼就看到了那个Impl,说明这是一个实现类,而非接口。我们提倡“面向接口编程”,这行代码的上下几行都是声明的接口,而唯独这行注入实现,我再一次询问了写这行代码的同学,这次有理由了:
这个Hij接口有两个实现类,分别是Hij1Impl和Hij2Impl,分别对应两种不同的功能,这个类里需要注入前者。
我听完的第一反应就是他没有把Spring Bean的注入搞清楚,这里还是应该声明为Hij接口,然后:
4、消失的异常堆栈
当遇到异常时,有两种处理方法:通过try-catch直接处理,或者向外抛出异常。总之最终要以一种合理地方式解决异常,同时还必须要打印错误日志,虽然这不会影响程序的运行,但对问题排查大有帮助。
在一个SpringMVC的Controller中有这样一段代码:
@RequestMapping
public Stirng processXxx() {
try {
doSomething();
} catch(Exception e) {
return "redirect:" + systemExceptionView;
}
...
}
乍一看,发生异常后显示一个系统异常页面,处理异常了,里面的内容可能是“啊呀,出错了”,或者“系统发生异常,请稍后再试”等等),但这些内容对排查问题没有任何帮助。究竟是什么原因导致系统异常的?
应该用Logger以合理
的日志级别记录下错误堆栈,切记是日志工具(Commons Logging、Apache Log4J、Slf4J等),而不是向System.err输出,或者用e.printStackTrace();而且应该如下形式,不是“消息”+e:
try {
doSomething();
} catch (Exception e) {
logger.warn("错误消息描述", e);
}
5、过多的嵌套与庞大的类
try {
if (conditionA) {
doSomething1();
} else if (conditionB) {
if (xxx==null) {
doSomething2();
} else {
doSomething3();
if (conditionC) {
for (String str : strList) {
List<Item> itemList = getItems(str);
if (conditionD) {
for (Item item : itemList) {
doSomething4();
}
}
}
}
}
}
} catch (Exception e) {
cleanUp();
}
是不是看晕了,这里只是一个演示,真实的代码更恐怖,外面还有两层if,这两层if又是在一个else里,这还只是一个方法,就有300多行,整个类有1300多行,里面有140多个if,就是一个不折不扣的大泥潭,应该对它进行重构。我相信有人看到过更大的方法,更大的类,但作为一个月前写的新代码,这个坑未免太大了一点。
当时代码是做了CodeReview的,也指出了这个类过于庞大,需要重构,可是鉴于页面复杂,项目开发后期重构对项目存在风险,建议项目后另外重构。这个类成功地实现了功能,可是这代码却很难读懂,如果在编写时不能重构它,那等到它变成“遗留代码”时,重构的成本就会翻上几倍,真希望自己能在它编写的时候就看到这段代码。
魔鬼往往隐藏于细节之中,代码中的坏味道也藏于细节之中,迟早它会跳出来给你当头一棒。《细节决定成败》中有这么一句话:“一个企业家要有明确的经营理念和对细节无限的爱”。对于开发人员,这句话也同样适用,尤其是“对细节无限的爱”。
(注:此处均非正式代码,仅做演示)
分享到:
相关推荐
标题“代码坏味道整理”指的是在编程过程中,代码可能会出现的一些不良习惯或低效的编程实践,这些被称为“代码坏味道”。这些坏味道通常会使代码难以理解、维护和扩展,降低了软件的质量。为了提高代码可读性和可...
Duplicated Code是代码坏味道中最常见的一种。它指的是在不同的地点出现相同的程序结构。如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好。最单纯的 Duplicated Code ...
以上列出的是《重构 改善既有代码的设计》书中提到的一些常见的代码“坏味道”。通过识别并解决这些问题,开发者可以显著提升代码的质量。值得注意的是,重构并非一蹴而就的过程,而是一个持续改进的过程。随着项目...
以下是一些常见的代码坏味道及其对应的重构方法: 1. **重复代码 (DRY - Don't Repeat Yourself)**:通过提炼方法、提取类等方式消除代码重复。 2. **过长方法**:可以使用提取方法来拆分长方法,提高代码可读性。...
在第三章中,作者详细列举了多种"代码的坏味道",也就是代码中常见的问题和反模式,旨在帮助开发者识别这些问题并进行有效的重构。 "源码"标签表明我们将关注代码的实际结构和质量,而"工具"标签则暗示可能涉及到...
### 代码TOP10的坏味道 #### 1. 返回值处理 在Java开发中,一个常见的问题是**返回值处理不当**。这个问题通常出现在函数调用后,如果调用方忽略了处理被调用函数的返回值,特别是当这些返回值对于后续业务逻辑有...
常见的代码坏味道包括: 1. **长方法**:如果一个方法执行多个职责,应考虑拆分为多个小方法,每个方法只做一件事。 2. **重复代码**:重复的代码不仅浪费存储空间,也增加了维护成本。通过创建函数或类来实现代码...
重构-改善既有代码的设计之代码的坏味道举例说明.md
代码坏味道,也被称为“code smell”,是软件开发中用来描述代码质量低下、结构不佳或者不易理解的现象。这些坏味道可能并不直接影响程序的运行,但它们往往预示着潜在的问题,比如复杂性增加、可读性下降或维护成本...
然而,在实际工作中,我们常常会遇到一些"代码坏味道",即代码中常见的不良模式,它们降低了代码质量,增加了维护难度。以下是我近期在代码审查中注意到的五种出现次数较多的代码坏味道,并针对每一种提供了解决建议...
您的项目概览,并且可以对代码打分(百分制)根据各自的坏味道数量建立文件索引(对不同文件按照改动频率、复杂度、重复度和坏味道4个维度进行综合评定代码质量等级)坏味道检测索引可以查看具体的类文件中的代码质量...
CheckStyle 是一个静态代码分析工具,可以检测 Java 代码中的错误、坏味道和不良实践。我们可以将 CheckStyle 的 jar 文件和配置文件复制到 custom_hooks 目录下,然后在 pre-receive 钩子脚本中调用 CheckStyle ...
重构的实践中,识别代码中的“坏味道”至关重要。所谓“坏味道”,是指那些指向代码质量问题的不良迹象。例如,“神秘命名”、“重复代码”、“过长函数”和“发散式变化”等,这些都是代码库中经常需要关注的重构...
包括识别坏味道的代码、应用重构技术以及何时进行重构的原则。 4. **错误处理**:讨论了如何有效地处理程序中的错误,提倡预防性编程,避免错误发生,以及如何优雅地捕获和处理异常。 5. **测试驱动开发(TDD)**:...
1. **识别代码坏味道**:书中列出了一些常见的代码坏味道,如重复代码(Duplicated Code)、过长方法(Long Method)、过大的类(Large Class)等。识别这些症状是重构的第一步。 2. **使用单元测试**:重构过程中...
重构是软件开发过程中的一个重要环节,它关注于改善已有代码的设计,以提高代码的可读性和维护性,同时保持原有功能不变。...通过识别和修复代码的坏味道,程序员可以不断优化代码结构,使软件更具生命力。
3. **重构**:书中详细讨论了重构的过程,包括识别坏味道的代码、应用各种重构技术以及如何在不影响系统功能的前提下改进代码结构。 4. **测试驱动开发(TDD)**:TDD是一种先写测试再写实现的开发方式,书中解释了...