好的代码是不需要注释的,因为它本身就能够自我说明,加上注释反而显得比较多余。不好的代码只看代码本身没法明白代码所表达的东西,必须借助注释才可以。虽然说注释应该作为一种规范,但我们还是应该尽量去提升代码的易读性
、自我解释性
。
说一下我对自我解释性很强的代码的理解和实践吧,不一定完全正确,仅供参考。
计算机中有个“ 元数据
”的概念,即描述数据的数据,比如描述进程的pcb,描述文件的fcb,描述http请求体的头字段等,说起来注释
也是一种元数据。
个人觉得在程序中, 变量名
就是一个元数据,他描述了他所存储的变量的内容,我们要求变量名要见名知意
,这样阅读代码的时候就能清晰的理出哪段逻辑处理的是什么数据,其实这个和注释的功能类似。自我解释性很强的代码就依赖于此。同理,函数名、类名、文件名、模块名、甚至项目名都有类似的作用,我们封装函数、类,拆分文件、模块、划分项目等,除了是为了逻辑复用,也有描述逻辑本身的作用。自我解释性很强的代码正是基于此。
1. 复杂逻辑的中间结果需要用变量存储
有一些逻辑需要从输入经历一系列的处理步骤才能得到最终的数据,这个过程会有很多的中间结果,有时候逻辑特别复杂,而一些中间结果比较有意义,这时候把这些中间结果用见名知意的变量所存储,会使得代码更加易读。举个栗子:
一段描述“如果明天下雨,我就不去动物园了”的逻辑的代码
if (空气湿度大于 xxx && 风力大于xxx && 温度低于 xxx) {
不去动物园();
}
去动物园();
其中“ 空气湿度大于 xxx && 风力大于xxx && 温度低于 xxx ”描述的逻辑就是 下雨,但看逻辑却并不能一眼看出来,这时候最常见的优化思路当然就是注释:
if (空气湿度大于 xxx && 风力大于xxx && 温度低于 xxx) { // 如果明天下雨
不去动物园();
}
去动物园();
这样是可以提升代码的易读性和可维护性,但可能会有如果这段逻辑变更,那么注释也要同步更新等一系列的问题,最好的优化方式就是提升代码本身的自我解释性,比如这里简单的加一个变量或者封装一个函数或工具类。
let 明天下雨 = 空气湿度大于 xxx && 风力大于xxx && 温度低于 xxx;
if (明天下雨) {
不去动物园();
}
去动物园();
or
function 明天是否下雨() {
return 空气湿度大于 xxx && 风力大于xxx && 温度低于 xxx;
}
if (明天是否下雨() === true) {
不去动物园();
}
去动物园();
以上两种方式都是自我解释性很强的,当然别的封装形式也可以。封装和拆分, 复用
是一个目的,同时见名知意的名字也能起到元数据
的作用,提高代码的简洁和易读性。
2. 步骤清晰的逻辑应当在代码中清晰的表现出来
有一些逻辑步骤很清晰,这时候就应该在代码中明确的表现出这种清晰来,比如:
把大象装冰箱一共分几步:
一种写法:
把冰箱门打开xx();
把冰箱门打开yy();
把冰箱门打开zz();
把大象装进去xx();
把大象装进去yy();
把大象装进去zz();
把冰箱门关上xx();
把冰箱门关上yy();
把冰箱门关上zz();
这样是很常见的写法,也能实现功能,但是代码的可维护性却可能很低,我遇到的很多一个函数好几百行代码的,一个文件几千行代码的都是这种问题。
这种逻辑清晰的,这样封装一下会更好:
function 把冰箱门打开(){
把冰箱门打开xx();
把冰箱门打开yy();
把冰箱门打开zz();
}
function 把大象装进去() {
把大象装进去xx();
把大象装进去yy();
把大象装进去zz();
}
function 把冰箱门关上() {
把冰箱门关上xx();
把冰箱门关上yy();
把冰箱门关上zz();
}
把冰箱门打开();
把大象装进去();
把冰箱门关上();
虽然总体代码量是增加的,但是逻辑的清晰度却提升了一个档次。当然这里的函数换成类、模块等都可以。
3.用到的一些设计模式要加上对应的名称
代码在拆分的过程中,我们可能会用到一些设计模式,这时候如果在名字上不体现出来,那么可能看很久才能看懂。但是只要在命名类、函数、变量的时候加以区分,那么可以让阅读代码的人很快get到代码整体的组织方式,比如Factory、Facade、Proxy、Decorator、Command等。
其实很多框架的源码在书写过程中也是这样做的,比如Struts2的RequestFacade、Mybatis的SqlSessionFactory等。这样读者在读到响应的类名的时候就能够理解这个类的功能。
同样,除了设计模式,我觉得各个目录下的代码也应该加上对应的前缀或后缀,比如XxxUtils、BaseXxx、CommonXxx等。
总结
以上,虽然只列了3点,但是主要的思想已经表达清楚了,就是合理的运用一些变量名、函数名、类名、模块名等,通过见名知意的名字来描述代码本身。