posts - 48, comments - 95, trackbacks - 0, articles - 2
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

我现在正在做一个比较大的项目,于是我就在网上找到了一个目标比较接近的开源项目,大致测了下,大概有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) 。

 

 

[待续]