一、如何精确命名
错误1: 命名过于宽泛
命名过于宽泛,不能精准描述,这是很多代码在命名上存在的严重问题,也是代码难以理解的根源所在。
data、info、flag、process、handle、build、maintain、manage、modify
上面的词语是否经常出现在代码中?这都是一些十分宽泛的词语。首先,命名要能够描述出这段代码在做的事情。一个好的名字应该描述意图,而非细节。 说直白一些,就是我们的方法名应该和其内部逻辑强关联。
错误2: 用技术术语命名
我们当前有一个集合叫做bookList,这个名字看起来没什么,但它的名字和List类型“绑定”了,假如说这个集合是一个不可重复的集合,那就出现了这么一个现象,”一个名字是list的集合,它的类型却是set“。这样变很混乱。我们不如直接叫”books“,这就和”类型“不关联了。
总结
好的命名,是体现业务含义的命名。
二、乱用英文
一般来说,常见的命名规则是:
- 类名是一个名词,表示一个对象。
- 方法名则是一个动词,或者是动宾短语,表示一个动作。
案例1:一个函数名是 retranslation,其表达的意图是重新翻译,但作为函数名,它应该是一个动词,所以,正确的命名应该是 retranslate。
案例2:某个方法叫completedTranslate,它要做的是将一些章节的信息标记为“翻译完成”。它的问题在于completedTranslate并不是一个正常的英语函数名。从这个名字你能看出,作者想表达的是“完成翻译”,因为是已经翻译完了,所以他用了完成时的completed。但这个语法是错误的,若改成动宾结构,应是completeTranslation。
总结
编写符合英语语法规则的代码
三、重复代码
常见重复:
- 复制粘贴的代码
- 结构重复
- if和else的两个代码块中的结构高度相似
if-else案例
if (user.isEditor()) {
service.editChapter(chapterId, title, content, true);
} else {
service.editChapter(chapterId, title, content, false);
}
上述代码,if代码块和else代码块的语句高度相似,唯一区别就是boolean类型的区别,这就是可以进行结构优化的地方,优化如下:
boolean approved = user.isEditor();
service.editChapter(chapterId, title, content, approved);
添加一个方法,用于获取boolean值,直接省去if-else语句了。
总结
千万不要复制粘贴,如果需要复制粘贴,首先应该做的是提取一个新的函数出来,把公共的部分统一处理。
四、长函数/长方法
多长的函数算长?这并没有一个确切的标准,不同的人、团队的标准都不一样。
在该专栏的作者看来,像 Python、Ruby 这样表达能力比较强的动态语言,大多数情况下,一行代码可以解决很多问题,所以,我对自己的要求大约是 5 行左右,并且能够用一行代码解决的问题,就尽量会用一行代码解决。对于Java 这样表达能力稍弱的静态类型语言,可以放宽10-20行。
长函数产生的原因:
- 以性能为由,在早期,人们关注函数的调用会涉及“入栈出栈”,不如直接执行来得性能高。这种想法经过各种演变流传到今天。不过,这个观点在今天是站不住的。
- 平铺直叙,说白了,就是像流水一样,把一整段逻辑都写在一个函数中,而不去进行函数/方法的封装。
- 一次加一点,一开始代码不长,随着代码迭代慢慢变长。
五、大类
类之所以成为了大类,一种表现形式就是我们上节课讲到的长函数,一个类只要有几个长函数,那它就肯定是一眼望不到边了。大类还有一种表现形式,类里面有特别多的字段和函数,也许,每个函数都不大,但架不住数量众多。
产生原因:
六、长参数列表
每个程序员只要想到,一个函数拥有几十甚至上百个参数,内心就难以平静下来。函数的参数是为了传递、共享信息。
减少参数数量的方法:
- 聚沙成塔:将参数列表封装成对象。
- 动静分离:函数的参数有不同的变化频率,对于静态的,不常变化的参数可以不用传值,直接在方法内部赋值即可。
- 告别标记:我们有时候会用“flag”,即一个boolean变量来作为参数,以告诉方法它想调用其中的哪个逻辑。这么做并不好,我们凭空造出了一个flag参数,多此一举。更有效的方法是直接将该方法的多个逻辑拆出来,封装成多个方法。
七、滥用控制语句
嵌套的代码也好,else 语句也罢,二者真正的问题在于,它们会使代码变得复杂,超出人
脑所能理解的范畴。我们可以通过提取单个元素操作,降低循环语句的复杂度,而用卫语
句来简化条件表达式的编写,降低选择语句的复杂度。一个衡量代码复杂度的标准是圈复
杂度,我们可以通过工具检查一段代码的圈复杂度。
八、不一致问题
对于一个团队来说,一致是非常重要的,是降低集体认知成本的重要方式。常见的不一致问题如下:
- 命名中的不一致:如枚举类中,各个常量的命名不统一。
- 方案中的不一致:团队内的不同成员分别引入了不同的依赖(程序库),然后去使用其中的功能相同的方法。比如有的人引入了谷歌的Guava,有的人引入了Apache 的 Commons Lang,它们能做类似的事情,所以,程序员也会根据自己的熟悉程度选择其中之一来用,造成代码中出现不一致。
- 代码中的不一致:体现在一个方法中的代码混乱,层次不统一。解决方案是将一个方法拆成多个方法,把业务代码和与业务关系不大的代码拆开。
九、落后代码风格
每一种有生命力的语言都会在自己的生命周期中不断地对语言本身进行改进,无论是引入新的语言特性,还是引入新的程序库,都会对代码的编写产生或多或少的影响。如Java8引入的Optional和Stream,通过Optional来避免空指针判断问题,通过Stream来代替循环。
总之,不断学习“新”的代码风格,不断改善自己的代码。