我现在正在做一个比较大的项目,于是我就在网上找到了一个目标比较接近的开源项目,大致测了下,大概有70%的功能是我所需要的,还有30%的功能和我要的不一样。所以我现在有2个选择:1. 自己全部重写。不过这个太费时,于是我选择 2. 修改这个项目,把自己想要的功能改出来。
不过一个老问题又出现了:读懂这个项目比自己写更吃力。我想很多人都应该有这个感受,不然也不会有很多的轮子。所以,我干脆就仔细研究这个问题:为什么别人的代码总是那么难读。
1. 什么叫读懂代码
当然,读懂代码的语法是基本,但更重要的是读懂代码的意义。
读代码的时候,我们更多思考这个问题:这行代码在做什么,为什么要这么做。然而实际上,我们仍然会碰到很多不明觉厉的代码。先看如下示例:
int fn(int a, int b){ int sum = 0; for(int i = a; i < b; i++){ sum += fn(i - 1, i + 1); } return sum; }
从语法上,代码是非常简单的几行。但是我们无法得出它的意义。因为:
代码是在描述具体的执行步骤,而不是描述功能本身。
这就是为什么大部分代码难读的主要原因。作者将它的意图描述成具体的步骤,而读者又需要根据步骤理解意图,这是一个多么吃力的过程。所以,如果想要提高代码可读性,你应该将你的意图写入代码中:即使用注释来描述代码的意图。当然,作者没有那么多时间留注释,所以,更多时候需要尝试自己尝试理解代码。2. 成员的嵌套
先看如下代码:
void fn(){ int a = f1(); int b = f2(a); int c = f3(b); int d = f4(a, b); }
如果你想理解 fn(),那么就必须先理解 f1() f2() f3() f4() 。这就是残酷的现实。
代码中总是互相引用和嵌套。
所以,你需要在最后关头才能真正读懂它,并且不允许任何一部出现差错。然而实际上,读懂这样的代码是不可能的。
比如下面的代码:
void fn(){ int value; if(this.header == null) { value = this.header; } else { value = this.main; } }
这个代码让我抓狂:虽然我知道这个函数的目标是获取 header 的值,如果获取不到则使用 main 的值。但同时还有这些问题:
header的值是什么?main的值是什么?什么时候可以获取到header的值,而什么时候又获取不到?
如此读懂一行代码带来3个问题,在尝试解决这3个问题时又带来更多问题。这是导致代码最终很难读懂的重要原因。
3. 代码的歧义
有时候,一些语法现象也会造成理解代码的错误,如:
function fn(){ } function gn(){ fn(); // 这里是很长很长的代码。 function fn(){ } }
是的,你需要读到最后才知道, fn(); 调用的是下面那个函数,而不是之前的。
4. 运行时判断
真的有很多代码,必须在运行时才能知道它的实际操作。虚函数和动态类型就是一个典型例子。
这也是为什么弱类型的脚本语言的代码都普遍比较难理解的原因,有太多东西需要在运行时才知道。
而读者只能一个个猜测它的意图。
5. 代码的丢失
对的,很多时候,特别是读别人的代码,你根本找不到一个效果和功能它的源码在什么地方。
也许,代码隐藏在一个小角落,或者根本就不存在,或者是自动生成出来的。
这是为什么C++项目难读的一个重要原因:因为有太多的自动生成的代码(通过#define) 。
[待续]