《编写可读代码的艺术》是与Clean Code相似的书,提供改善“丑陋”代码的技巧。本书193页,共16章内容。
本书分为四个部分:
- 表面层次的改进-命名/注释以及审美
- 简化循环和逻辑
- 重新组织代码
- 精选话题
关键思想:代码应当易于理解
可读性基本定律:代码的写法应当使别人理解他所需的时间最小化。
1 表面层次的改进
包括好的名字,好的注释,还有好的格式
1.1 把信息装进名字里
1.1.1 选择专业的词
反例: 函数名 GetPage(url)
正例: FetchPage()、DownloadPage()
反例: class BinaryTree的方法Size() VS
正例:Hight() NumberNodes(),MemoryBytes()
反例: class Thread的方法Stop()
正例: Kill() Pause(),Resume()
1.1.2 避免泛泛的名字
关键思想:清新和准确比装可爱好
迭代器变量,应该有明确指向
避免tmp和retcode泛泛的名字
retcode建议:retcode没有包含更新信息。描述该变量的值的名字代替它。
tmp建议:tmp这个名字只应用于短期存在且临时性为其主要存在因素的变量。
如果需要使用tmp,也需要指明tmp_file,或者tmp_data.
迭代器变量,应该有明确指向
1.1.3 用具体的名字代替抽象的名字
反例: 函数ServerCanStart:检测服务是否可以监听某个端口
正例: CanListenOnPort()
1.1.4 给名字附带更多的信息
1、一个变量名就是一个小小的注释
反例:string id ; //example:"0x128a232b"
正例:string hex_id;
2、变量名带单位
3、附带额外信息
1.1.5 决定名字的长度
名字隐含约束就是不能太长。
- 1、在小的作用域可以使用短名字。
- 2、输入长名字不再是问题。【内置补全功能】
- 3、首字母缩写词和缩写
经验原则:团队新成员可以理解这个名字含义。 - 4、丢掉没用的词
反例 :ConvertToString()
正例: ToString()
1.1.6 利用名字的格式表达含义
利用下划线,大小写或者连字符可以装载更多含义。 在google的C++代码规范中:
- kMaxOpenFiles表示常量,MAX_OPEN_FILES表示宏
- state是局部变量,state_是成员变量
1.1.7 小结
- 使用专业的单词
- 避免空泛的名字
- 使用具体的名字描述细致的事物
- 给名字带上重要的细节
- 给作用域更大的名字采用更长的名字
- 有目的使用大小写和下划线
1.2 不起误解的名字
关键思想:要多问自己几遍,“这个名字会被别人误解成其他含义吗?“要仔细审视这个名字
反例: Filter("years <= 2010") 挑出还是减掉?
反例: Clip(text,length)是截断到,还是去掉?
正例:使用min和max表示极限
正例:使用first和last表示包含的范围
正例:使用begin和end表示包含/排除的范围
布尔型命名
正例:加上is,should,has,can,use变得含义更明确
正例:避免使用反义名词:bool disable_ssl=false;
1.3 审美
布局的三个原则:
- 使用一致的布局,让读者很快习惯这种风格。
- 让相似代码看上去相似。
- 相关的代码分组,形成代码块。
- 1、使用【从审美角度】让人愉悦的代码更容易
- 2、安排换行是节奏保持一致和紧凑
-
3、用方法规整不一致的东西
-
4、在需要时,使用列对齐
- 5、选择有意义的顺序,并保持一致
- 6、把申明按照块组织起来
- 7、把代码分成段落
- 8、一致性比”正确“风格更重要
1.4 注释
关键思想:注释的目的就是尽量帮读者和作者了解一样多
1、什么不需要注释
- 不要为那些可以从代码快速推断的事实写注释
- 不要为了注释而注释
- 不要为不好的名字加注释,应该把名字改好
2、记录你的思想
- 加入“导演评论”
-
为代码的瑕疵做注释(//TODO:)
- 给常量加注释
常量本身不需要解释,记录决定这个常量取值的想法,便于后续优化。
3、站在读者的角度
- 意料之中的提问
- 公布可能的陷阱
- 全局性的注释
高级别的注释信息,包括文件,类,模块如何组织工作的。 - 用注释来总结代码块,防止迷失细节
1.5 写出言简意骇的注释
关键思想:注释应该有很高的信息/空间率
-
1、让注释保持紧凑
- 2、避免使用不明确的代词(it,this)
- 3、润色粗糙的句子
-
4、精确描述函数的行为
- 5、用输入/输出的例子来说明情况
-
6、申明代码的意图
【4,5,6用单元测试代替注释更妥】 -
7、具名函数参数的注释
使用嵌入式的注释,例如Function(/arg=/...)来解释函数的参数。 - 8、采用信息量高的词,使得注释简洁
2 简化循环和逻辑
2.1 把控制流变得易懂
关键思想:把条件,循环以及其它对控制流的改变做的越“自然”越好。运用一种方式使得读者不用停下来重读你的代码
-
1、条件语句中参数顺序
if(length<=10) 还是 if(10>=length)?
while(bytes_expected < bytes_recieved) 还是 while(bytes_expected > bytes_recieved) ?
指导原则【和语言的语法一直】
比较的左侧 | 比较的右侧 |
---|---|
被询问的表达式,它的值倾向于不断变化 | 用来作比较的表达式,它的值倾向于常量 |
if(object == null) 还是 if(null == object)?
2、if/else语句块的顺序
if条件选择标准:
先处理正逻辑而不是负逻辑
先处理简单的
先处理有趣或者可疑的3、三目运算符
关键思想:相对于追求代码最小行,一个更好的方式就是最小化人们理解他所需的时间
建议:默认情况下都if/else, 三目表达式只在最简单的情况下使用。-
4、避免do/while循环
使用while/for,而不是for/while, 把条件【危险】放到首先看到地方。
5、从函数中提前返回
使用卫语句,提前返回。【单出口】6、臭名昭著的goto
7、最小化嵌套
思维栈-可能导致理解困难。
通过提前返回减少嵌套
减少循环内的嵌套
2.2 拆分超长的表达式
1、解释变量
引入变量来解释子表达式。
2、最小化嵌套
用一个变量来代替一段代码。
3、使用德摩根定理
4、滥用短路原理
5、使用德摩根定理
6、复杂表达式,提取函数,提前返回
2.3 变量和可读性
1、减少变量
没有价值的变量
减少中间结果
减少控制变量
2、变小变量的作用域
让你的变量对尽量少的代码行可见
C++ if的作用域
3、只写一次的变量更好
操作一个变量的地方越多,越难确定他的值。
3 重新组织代码
3.1 抽取不相干的子问题
抽取不相干的子问题,建议:积极地发现并抽取不相干的子逻辑。
我们指:
- 看到某个函数或者代码块,问自己:这个代码的高层次目标是什么?
- 对于每一行代码,问:他是直接为了目标而工作吗?这段代码高层次的目标是什么?
- 如果足够的行数在解决不相干的子问题,抽取代码到独立的函数中。
- 抽取纯工具代码
- 其他多用途的代码
- 创建大量通用代码
- 项目专有代码
- 简化已有接口
- 按需重塑接口
-
避免过犹不及
小结:把一般代码和专有代码分离。
3.2 一次只做一件事
- 1、任务可以很小
- 2、从对象中抽取值
3.3 把想法变成代码
爱因斯坦:如果你不能把一件事情解释给你祖母听的话说明你还没有真正理解他
- 1、像对着同事一样,用自然语言描述代码要做什么。
- 2、注意描述中所用的关键词和短语。
- 3、写出与描述匹配的代码
3.4 少写代码
关键思想:最好读的代码就是没有代码。
- 1别费神不需要的功能【过度设计】
- 2质疑和拆分需求 【故事拆分】
- 3保持小代码库
利用工具减少重复代码,并删除无用代码
- 4、熟悉你周边的库
中肯建议:每隔一段时间,花15分钟来阅读标准库的所有函数/模块/类型的名字。
冒险、兴奋-绝地武士追求的并不是这些。--尤达大师
4 精选话题
4.1 测试与可读性
- 1、使阅读易于阅读和理解
测试应该具备可读性,以便其他程序员可以舒服的改变和增加用例。
对使用者隐去不重要的细节,以便重要的细节更突出。 - 2、创建最小的测试申明
测试都可以提炼成:对于这样的输入/情形,期望这样的行为/输出.【BDD的描述更好】 - 3、实现定制的“微语言”(DSL)
- 4、让错误消息具备可读性
- 5、 手工打造错误消息
- 6、 测试函数命名
规则:Test_<Function>_<Situation> - 7、TDD
易测试产生更好的代码。
- 8、走的太远
主要有:
为了可测试,牺牲可读性
着迷100%可测试性
测试成为开发的阻碍
4.2 设计并改进
通过一个例子来说明:演进式改进的过程。
小结
本书2018读的第一本书,大概花了10个多小时。本书在代码可读性的命名,布局,注释,循环控制,抽取函数,表达式,可测试性方面都有很不错的建议,值得内部分享和学习。