读书笔记-《编写可读代码的艺术》一
一、代码应当易于理解
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) 更加专业 2、class 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 = 10; if 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:这样的标记
-
常量背后的故事,为什么是这个值
站在读者的立场上思考:
-
预料到代码中哪些部分会让读者说:“啊?”并且给它们加上注释
-
为普通读者意料之外的行为加上注释
-
在文件/类的级别上使用“全局观”注释来解释所有的部分是如何一起工作的
-
用注释来总结代码块,使读者不致迷失在细节中