编程常见影响可读性的问题总结 含混不清的命名 过长的函数 过大的类 滥用的继承 魔鬼金字塔 Callback hell 设计模式滥用 过多的配置 滥用注释 函数过多参数 反复修改的临时变量 在函数中修改入参 一个函数多个责任

命名问题是很严重的问题,我见过有些系统用大量拼音缩写命名,有些缩写长达5-6个字母以上,这种除非是长期维护系统的程序员,其他人看了都是一脸懵逼。

命名最好用英文加上驼峰命名法,比如getAccount,userName。对于英文单词的选择,由于我们是非英语国家,即使在英语国家里面,同一个含义可能也对应多个单词,最好团队建立一个常用英文单词库,比如性别都叫:gender,而不是有的用sex,可以建立团队内部Wiki解决。

清晰的命名,应该让其他人一眼看去就知道这个东西大概是干什么的。我很不喜欢的一种命名是类成员都加上m开头,如mUserName,这个毫无必要。接口前面也不必加上I,另外以下划线开头的习惯我更觉得是一种干扰。

过长的函数

一般过长的函数表明这个函数承担了太多责任,可以用Extract Method将部分责任分给其他更小的函数。责任太多的函数将很难阅读,阅读者由于很多计算的干扰而看不清你的主线。一般函数控制在20行以内,10行以内更好。

过大的类

过大的类和过长的函数类似,不过是一个类可能承担了太多责任。可以把部分责任扩展为新的类,用委托的方式实现。

滥用的继承

我曾经碰到有个系统,有多达5-6层的继承,最底层的实现,又一路用super调用到最上层的类。当你查看某一个功能的代码时,你不得不打开N个窗口,在不同的类之间跳来跳去。

除了一些特殊情况,如Template模式,要谨慎使用继承。如果可以使用聚合,就不必去使用继承。如果仅仅是为了共享部分代码而用继承,这个就完全没有必要。

魔鬼金字塔

魔鬼金字塔是指多层的向右缩进,就像金字塔一样。有说法人类的大脑只能暂存3层栈,如果多层嵌套,大脑将很难记住前面的嵌套到哪里了。解决办法是将部分代码Extract method组成新的方法,保持主方法比较干净。

Callback hell

这是另外一种反模式,我曾经看到过3-4层甚至以上的Callback嵌套,当时的程序员即使是很有经验的,也差点崩溃了。Callback在某些情况下是提高了效率,不过不可滥用,一般来说我觉得一层就足够了。

设计模式滥用

设计模式是好东西,在某些环境下,设计模式为了解决某些特定问题是非常好的。设计模式的基础是面向抽象编程,只要你遵循面向抽象编程,就不必非要到处硬套设计模式。一个系统里面有几个地方使用设计模式挺好,如果到处都是设计模式,你就要崩溃了。

过多的配置

有些系统外面用了太多文件对系统进行配置,以至于光看代码,你根本不知道他们怎么组合在一起的(这也是我不太喜欢早期Spring用XML进行装配的原因)。Java有了Annotation后,把代码和配置写在一起,一定程度上解决了这个问题。

滥用注释

注释是好东西吗?可能有些地方是。但最好的注释我认为是对函数、变量、成员、类的良好命名,让人仅仅看名字就能理解代码而不是靠注释。如果代码中有过多注释,还影响代码的阅读。我看到有些开源库,注释多到你找不到代码在哪里!

注释还有个最大的问题是和代码的同步性,有些代码改了,注释却未必同步了。这样注释编程没用的甚至是错误的,这带来更大问题。

函数过多参数

函数参数如果过多(超过3个),在调用的地方就很难弄清楚顺序,为了弄清楚就不得不跑到声明的地方看一看,这时候可以用个类把这些参数聚合起来。

现在有些IDE如intelij idea的最新版本,可以在调用的地方标出参数名称,这个很有帮助,不过还是尽量不要太多为好。

反复修改的临时变量

在函数中声明一个临时变量,最好声明为final的(除了一些循环因子如i),在第一次赋值即不允许改变。一个允许变来变去的临时变量,将给阅读带来很多困扰。

在函数中修改入参

调用一个函数,如果会对入参带来副作用,比如对传入的一个List进行操作,对调用方将比较困扰。如果你确实需要,宁可返回一个新的List。幸好Java中只允许值传递,你对入参赋值不影响调用方,否则更麻烦。

一个函数多个责任

我曾经遇到一个Bug,一个人有多个名字:昵称、别人给你的别名、通信录中的名字,不同的地方显示的名字不同。然后开发人员把这些setter方法,里面又加了代码对对象进行操作,导致最后完全搞不清谁会修改代码。解决办法是把setter都恢复干净,然后另外写了一个读取showName的方法。