读书笔记-《编写可读代码的艺术》一

一、代码应当易于理解

1、代码的写法应当使别人理解它所需的时间最小化

2、一般来说,解决问题所用代码越少越好,但代码理解力更重要

少的代码:
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());
可理解的代码:
bucket = FindBucket(key);
if (bucket != null) assert(!bucket->IsOccupied());

3、理解代码所需时间与其他目标没有冲突(如:代码更有效率,容易测试)

4、要经常想一想其他人是不是会觉得你的代码容易理解

 

二、把信息装进名字里

1、选择专业的更有表现力的词 (清晰和精确比装可爱好)

1、如果一个方法是从互联网中得到一个页面,使用 FetchPage() 或者 DownloadPage() 比 getPage(url) 更加专业
2class BinaryTree
        int size();
这个例子的size方法是不明确的,可以是树的高度,节点数,或者占内存空间,所以更专业的可以是Height(), NumNodes()或者MemoryBytes

2、避免空泛的名字,像tmp和retval,除非有使用它们的特殊理由

像i,j,iter和it等名字常用做索引和循环迭代器,尽管这些名字很空泛,但是大家都知道它们的意思。
但有时会有比i,j,k更有意义的迭代器名称
for (int i = 0; i < clubs.size(); i++)
    for (int j = 0; j < clubs[i].members.size(); j++)
        for (int k = 0; k < users.size(); k++)
            if (clubs[i].member[k] == user[j])
                cout << "user[" << j << "] is in club[" << i << "]" << endl;

上面例子中,可以使用(club_i, members_i、user_i) 或者(ci,mi,ui)更好一点

3、使用具体的名字来更细致地描述事物

例如:有一个检测服务是否可以监听某个给定的TCP/IP端口的方法 ServerCanStart()
然而这个方法有点抽象,CanListenOnPort()就更具体一点
Google C++代码库中有一个宏 DISALLOW_EVIL_CONSTRUCTORS
这个方法较为抽象,后来换成了更具体的名字
DISALLOW_COPY_AND_ASSIGN(className)

4、给变量名带上更重要的细节

 

 

 

5、为作用域大的名字采用更长的名字

5.1、名字不应该太长

没人喜欢这种名字
newNavigationControllerWrappingViewControllerForDataSourceOfClass
名字越长越难记,在屏幕占的地方也越大,可能产生多余的换行

5.2、在小的作用域里可以使用短的名字

if (debug) {
    map<string, int> m;
    LookUpNamesNumbers(&m);
    Print(m);
}
尽管m这个名字没有很多信息,但不是问题,因为读者已经有了需要理解这段代码的所有信息
假设m是一个全局变量中的类成员,看到下面的代码就没那么好读了
LookUpNamesNumbers(&m);
Print(m);

5.3、理解使缩写变得可能

程序员有时会采用首字母缩略词或缩写来命名,以便保持较短的名字
例如:把BackEndManger 命名为 BEManager但这样的名字让人费解
但不是不能,例如:使用eval代替evaluation,用doc代替document,用str代替string相当普遍。
所以主要是团队的成员能否立刻理解这个名字的含义

5.4、丢掉没用的词

例如 Convert To String() 改为 ToString()
       DoServerLoop() 改为 ServerLoop()

6、有目的地使用大小写、下划线等

static const in kMaxOpenFiles = 100;
class LogReader {
  public:
    void OpenFile(String local_file);
  private:
    int offset_;
    DISALLOW_COPY_AND_ASSING(LogReader);
}
上面C++例子中,
常量的格式是kConstantName而不是CONSTANT_NAME,为了与#define宏区分开
使用CamelCase来表示类名,类成员变量使用lower_separated,这样能立刻区分是成员变量还是其他变量

 

三、不会误解的名字

1、使用min_和max_来表示(包含)极限

命令极限最清楚的方式是在要限制的东西前加上max_或者min_
例如,你的购物车应用最多不能超过10件物品
CART_TOO_BIG_LIMIT = 10if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT
    Error("Too many items in cart.")
该变量名存在二义性,它没有明确到底是”少于“还是”少于/且包括“, 改为 MAX_ITEMS_IN_CART,新代码将变得简单又清楚
MAX_ITEMS_IN_CART = 10
if shopping_cart.num_items() > MAX_ITEMS_IN_CART
    Error("Too many items in cart.")

2、使用first和last表示包含的范围

 

print integer_range(start=2, stop=4)
尽管start是个合理的参数名,但stop有多种解读。对于这样包含开头和结尾的范围,一个好的选择是first/last
set.PrintKeys(first="Bart", last="Maggie")

 

3、使用begin和end表示包含/排除的范围

 

对于命名包含头/排除尾的范围典型的使用begin/end
printDateRange(begin="OCT 16 12:00am", end="OCT 17 12:00am")

 

4、给布尔值命名

 

通常来说,加上is,has,can或should这样的词,可以把布尔值变得更明确
bool read_password = true;
上面代码有两种截然不同的解释: 1、我们已经读取了密码,2、需要读取密码。
使用 need_password获取 user_is_authenticated这样的名字来代替
避免使用反义名字,例如,不要用 bool disable_sll = false; 可以使用 bool use_ssl = true;

5、小心用于对特定词的期望

 

四、审美

使用一致的布局,让读者很快习惯这种风格

让相似的代码看上去相似

把相关的代码行分组,行成代码块

 

五、注释

什么地方不需要注释:

  • 能从代码本身中迅速地推断的事实
  • 用来粉饰烂代码(例如蹩脚的函数名)的“拐杖式注释”——应该把代码改好

应该记录下来想法:

  • 对于为什么代码写成这样而不是那样的内在理由(“指导性批注”)

  • 代码中的缺陷,使用像TODO:或者XXX:这样的标记

  • 常量背后的故事,为什么是这个值

站在读者的立场上思考:

  • 预料到代码中哪些部分会让读者说:“啊?”并且给它们加上注释

  • 为普通读者意料之外的行为加上注释

  • 在文件/类的级别上使用“全局观”注释来解释所有的部分是如何一起工作的

  • 用注释来总结代码块,使读者不致迷失在细节中

 

posted on 2021-03-03 14:55  一直工作的小白鼠  阅读(84)  评论(0编辑  收藏  举报

导航