`
DigitalSonic
  • 浏览: 210236 次
社区版块
存档分类
最新评论

代码中的坏味道

阅读更多

最近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接口,然后:

  • 通过<property>配置 ,直接显式地注入所依赖的Bean。
  • 使用Spring的Autowire机制 ,并且明确使用byName,而非byType;此处要注意两个Bean的id。

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的,也指出了这个类过于庞大,需要重构,可是鉴于页面复杂,项目开发后期重构对项目存在风险,建议项目后另外重构。这个类成功地实现了功能,可是这代码却很难读懂,如果在编写时不能重构它,那等到它变成“遗留代码”时,重构的成本就会翻上几倍,真希望自己能在它编写的时候就看到这段代码。

 

魔鬼往往隐藏于细节之中,代码中的坏味道也藏于细节之中,迟早它会跳出来给你当头一棒。《细节决定成败》中有这么一句话:“一个企业家要有明确的经营理念和对细节无限的爱”。对于开发人员,这句话也同样适用,尤其是“对细节无限的爱”。

 

(注:此处均非正式代码,仅做演示)

 

分享到:
评论

相关推荐

    编码中的21种代码坏味道

    详细描述了在设计和编码中的21中代码坏味道,让我们知道什么样的代码方式是容易给后续维护带来极大影响的问题。

    《重构 改善既有代码的设计》之代码的坏味道

    附件是在读完《重构 改善既有代码的设计》之后,为代码的坏味道所撰写的总结文档。

    代码坏味道整理

    NULL 博文链接:https://takemind.iteye.com/blog/2312244

    代码的坏味道 重构方式对应表

    代码的坏味道 重构方式对应表 DOC 可打印.

    代码TOP10的坏味道

    java代码的TOP10的坏味道,我们TOP10的坏味道主要如下: TOP 1 —— 返回值处理 A: 被调函数执行结果对业务流程有影响时,调用者却没有处理其返回值。 包括:可能导致空指针访问、缺少回退处理(资源泄漏等)。

    代码坏味道与启发--《代码整洁之道》总结.pdf

    代码坏味道与启发--《代码整洁之道》总结.pdf

    重构-第3章 代码的坏味道-读书笔记

    NULL 博文链接:https://aqxiebin.iteye.com/blog/1579688

    重构_改善既有代码的设计PDF

    重构原则 代码的坏味道 简化函数调用 重构,复用与现实 重构工具

    重构,改善现有代码——代码坏味道

    该文档是以前整理的笔记本,主要是针对代码坏问道的一些描述和一些解决办法。东西都是摘录的,虽然距离现在差多十几年,但是感觉还是可用的,所以分享一下。

    软件重构技术(重构介绍、重构原则、代码的坏味道。。。)

    任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员

    生成Ruby代码质量报告RubyCritic.zip

    您的项目概览,并且可以对代码打分(百分制)根据各自的坏味道数量建立文件索引(对不同文件按照改动频率、复杂度、重复度和坏味道4个维度进行综合评定代码质量等级)坏味道检测索引可以查看具体的类文件中的代码质量...

    5种出现次数较多的代码坏味道

    最近一段时间,我参与了几次代码审查,发现了5种出现次数较多的代码坏味道,总结如下

    代码质量.ppt

    代码坏味道 什么是好代码? 什么代码复杂度? 怎么解决代码复杂性? 重要设计模式

    重构-改善既有代码的设计+中文版.pdf

    重构-改善既有代码的设计+中文版代码重构 代码的新思维 何为优秀代码 代码的坏味道 如何重构 设计重构 设计的基本原理 重构到模式 架构重构

    php在项目中寻找代码的坏味道(综艺命名)

    今天想给综艺频道添加一些内容,但是综艺这个词太难伺候了(主要是我的英文不是太好)。我把整个命名过程摘录如下

    重构-改善既有代码的设计

    第3章 代码的坏味道 75 第4章 构筑测试体系 89 第5章 重构列表 103 第6章 重新组织函数 109 第7章 在对象之间搬移特性 141 第8章 重新组织数据 169 第9章 简化条件表达式 237 第10章 简化函数调用 ...

    Android代码-MultiItem

    RecyclerView是一个大家常用的列表控件,在列表中不免会出现多种类型的布局,这时adapter中多种类型的判断就会充满着switch的坏味道,可怕的是需求变更,增加或修改新的类型时,所有的改动都在adapter中进行,没有一...

    重构-改善既有代码的设计

    于是萌生想做一本重构工具书的想法,本来打算自己重新将重构书的内容再整理归类一下,后来发现原书的目录编排就很适合做工具书,包括坏味道分类,重构手法归类等,都有了一个比较系统的整理。因此,我利用空余时间...

    重构—改善既有代码的设计

    3、代码的坏味道(BadSmellsinCode) 4、建立测试体系(BuildingTests) 5、重构名录(CatalogofRefactoring) 6、重构方法(Refactoring Method) 6、1重新组织你的函数(ComposingMethods) 6、2在...

    重构-改善既有代码的设计(chm清晰版)

    章节三 代码的坏味道 章节四 构筑测试体系 章节五 重构名录 章节六 重新组织你的函数 章节七 在对象之间搬移特性 章节八 重新组织数据 章节九 简化条件表达式 章节十 简化函数调用 章节十一 处理概括关系 章节十二 ...

Global site tag (gtag.js) - Google Analytics