surface

本文为"the art of readable code"一书的笔记

packing informations into names

choose specific words

但是作者却批评这种命名方式。作者说,get太模糊了,我们看不出来它是从缓存中get还是 从数据库中get,还是从互联网上get。如果是从互联网上get,则应该使用fetchPage或者 downloadPage()。我觉得这个说法非常好,我以后给变量或者函数起名字的时候也要注意 这方面的东西了。

avoid generic names like tmp and retval

我却用过tmp这东西,后来想了一下,tmp这种东西的确看不出来任何含义,就算时间紧迫,我 也不会用tmp这种变量了。但是,就像作者所说的一样,对于下面的代码:

void swap(int *a,int *b)
{
    int tmp = *a;
    *a = b;
    *b = tmp;
}

这时候tmp则用得恰到好处,tmp在这里生命周期非常短,而且它的作用刚好是作为临时来用的。

多个计数器变量时,这种东西就经常会让人很难看懂了。看下面的例子:

for(int i = 0; i < N; i++){
    for(int j = 0; j < M; j++){
        for(int k = 0; k < C; k++){
            if(school[i].teacher[k] == user[j]){
                ...
            }
        }
    }
}

school,teacher,user这种变量命名是很好的,但是i,j,k这种东西就很难懂了。我们怎么 知道i,j,k分别对应哪个数组的下标呢?我以前也经常写这种代码,也经常看到其他人写这种代码, 每当我看到这种代码时(就算是我自己的代码),我都觉得非常头疼,现在看了这本书,马上 醒悟过来,以后再也不写这种代码了!转而使用si,ti,ui,这样 school[si].teacher[ti] == user[ui] 就非常清楚了。

prefer concrete names over abstract names

serverCanStart是抽象的名字,而canListenOnPort则是一个具体的名字

attaching extra information to name,by using a suffix or prefix

string id;

而这个id必须是十六进制,这时,直接使用id就很不好,因此你看不出来它必须使用十六进制, 使用hex_id代替就很明确了。

使用书上的例子,看下面的js代码:

var start = (new Date()).getTime();
var elapsed = (new Date()).getTime() - start;
document.writeln("time is:" + elapsed + " seconds");

如果对js比较熟悉的,会知道,getTime()返回的是ms,而不是s,因此这样的命名会很容易 产生bug,不是每个程序员都会记得那么清楚getTime()返回的单位是什么。把这2个变量改成 start_ms和elapsed_ms就很清楚了! 看到这里,我决定,以后遇到这种有单位的变量,都要带个单位的后缀,以写出可读的代码。

deciding how long a name should be

if(i != k){
    int t = a[i];
    a[i] = a[k];
    a[k] = t;
}

我以前很恶心java的类名方法名很长,看起来不简洁,现在看多了也习惯了,而且现在打长的变量 名的确很简单了,因此现在很多编辑器或IDE都有自动补全的功能。比如我现在使用的Emacs就自带 有补全的功能(Alt + /),我居然还不知道,因此我都是用auto-complete的,看来当它不起作用 时,我就可以手动地补全了。

作者说,对于工程项目的代码,最好不要写缩略词,因为新加进来的成员可能看不懂缩略词的意思, 而一些最常见的缩写,比如evaluation写成eval,string写成str,document写成doc,则写 成缩写比较好。其实,我觉得,像linux系统这么大的工程都使用了很多缩写,有的时候缩写还是 非常必要的,可能是我的个人原因,我不喜欢又臭又长的代码,我喜欢简洁的代码,对于unix哲学 中的缩写规则,我很感兴趣,在程序中还是要尽量使用缩写。

using name formating to pack extra information

在google c++的规范中,类名首字母大写,使用驼峰式;宏常量名全部大写,使用下划线分隔; const变量则首字母大写,驼峰式,区分宏常量;类的方法首字母大写,驼峰式;类的变量全小写, 最后要跟一个下划线;其它局部变量则全部小写,后面不跟下划线。 在html/css中,id一般使用下划线分隔,而class使用中线(dash)来分隔。

names that can't be misconstructed

a b c d


first last

a b c d


begin end

bool disable_ssl = false;

而要使用

bool use_ssl = true;

实现是计算一大堆数据的和,但是一般程序员第一眼看过去的时候就会以为仅仅是返回和,并 没有想到里面会有代价很多的计算,很有可能会经常调用这个函数,这样就会使程序变得很慢。 使用computeSum会使人更容易明白。

表的长度,O(n)的速度很慢。看下面的例子:

while (list.size() > max_size) {
    ...
}

连写STL的程序员也有不规范的时候,一般的程序员会以为size()是O(1)的速度,直接返回 链表的长度,这样就会使得程序的速度非常慢了。如果改成countSize()会好很多, 但是幸运的是,作者说了,最新版的STL已经把size()变成了O(1)速度。

aesthetics

public class PerformanceTester {
    public static final TcpConnectionSimulator wifi =
        new TcpConnectionSimulator(
            500,  /* Kbps */
            80,   /* millisecs */
            200,  /* jitter */
            1     /* packet loss % */);
    public static final TcpConnectionSimulator t3_fiber =
        new TcpConnectionSimulator(
            4500,  /* Kbps */
            10,    /* millisecs */
            0,     /* jitter */
            0      /* packet loss % */);
    public static final TcpConnectionSimulator cell =
        new TcpConnectionSimulator(
            100,  /* Kbps */
            400,  /* millisecs */
            250,  /* jitter */
            5     /* packet loss % */);
}

这里,缩进是对齐了,注释也对齐了,但是占用的行数太多,而且注释重复了3遍。 改成这样就好看多了:

public class PerformanceTester {
    // TcpConnectionSimulator(throughput, lantency, jitter, packet_loss)
    //                          [Kbps]     [ms]      [ms]    [percent]

    public static final TcpConnectionSimulator wifi =
        new TcpConnectionSimulator(500, 80, 200, 1);
    public static final TcpConnectionSimulator t3_fiber =
        new TcpConnectionSimulator(4500, 10, 0, 0);
    public static final TcpConnectionSimulator cell =
        new TcpConnectionSimulator(100, 400, 250, 5);
}

漂亮的代码,但是一直做不到,因为要打很多空格。后来发现emacs有通过正则表达式 对齐的功能align-regexp,爽极了!

knowing what to comment

when should not comment?

翻页,所以,没有价值的注释不要写。下面的代码的注释都没有价值。

// The class definition for Account
class Account {
public:
    // Constructor
    Account();

    // Set the profit member to a new value
    void SetProfit(double  profit);

    // Return the profit from this Account
    double GetProfit();
};

因为,注释的含义从代码中已经可以看出来了,注释没有提供额外的信息,其实是和 代码重复了,不仅浪费地方,而且浪费写代码的人的时间和读代码的人的时间。

what should comment be?

// TODO:use a faster algorithm 有一些和TODO一样的很流行的标签:

Marker Typical meaning


TODO things to finish FIXME known-broken code here HACK inelegant solution to a problem XXX danger!major problem here

making comments precise and compact

//Example:strip("abba/a/ba", "ab") returns "a"

// Iterate through the list in reverse order
for (i = SIZE; i >= 0; i--) {
    ...
}

上面的注释写了和没写差不多,改成这样就非常好了:

// display each price, from highest to lowest
for (i = SIZE; i >= 0; i--) {
    ...
}
Connect(/* timeout_ms = */ 10, /* use_encryption = */ false);

要比这样的代码

Connect(10, false);

更容易看懂。