《编写可读代码的艺术》读书笔记
- 2023-02-16 17:00:00
- 潘仙芝
- 原创 480
一、代码应当易于理解
代码的写法应当使别人理解它所需的时间最小化
二、表面层次的改进
1. 把信息装入名字里
· 选择专业的词
· 清晰精确比装可爱好
· get 和 fetch download
· 找到更有表现力的词
· 避免泛泛的名字(或者说要知道什么时候使用它)
· 比如tmp、retval
· 用具体的名字代替抽象的名字
· 为名字附带更多信息
· 带单位的值,带单位的参数
· 附带其他重要属性
· 使用前缀或后缀来给名字附带更多信息
· 决定名字的长度
· 隐含的约束,名字不能太长
· 在小的作用域中使用短的名字
· 利用名字的格式来表达含义
· 首字母缩略词和缩写
· 丢掉没用的词
· 总结
· 把信息塞入名字里
· 使用专业的单词
· 使用空泛的名字
· 使用具体的名字来更细致地描述事物
· 给变量名带上重要的细节
· 为作用域大的名字采用更长的名字
· 有目的的使用大小写、下划线等
2.不会误解的名字
· 多问几遍:这个名字会被别人解读成其他的含义吗?
· 命名极限最清楚的方式是在要限制的东西前加上max_或min_
· 推荐使用first和last来标识包含范围
· 使用begin和end来标识包含/排除范围
· 与使用者的期望相匹配
· 如何权衡多个备选名字
3.审美
· 从审美的角度讲让人愉悦的代码更容易
· 重新安排换行来保持一致和紧凑
· 用方法来整理不规则的东西
· 在需要时使用列对齐
· 列提供“可见的栏杆”
· 让相似的代码看起来相似
· 选一个有意义的顺序,始终一致地使用它
· 把声明按块组织起来
· 把代码分成“段落”
· 个人风格与一致性
· 一致的风格比“正确”的风格更重要
· 总结
· 代码用一致的有意义的方式“格式化”
· 多个代码块做相似的事情,尝试让它们有同样的剪影
· 把代码按列对齐可以让代码更易浏览
· 选择一个有意义的顺序并始终用这样的顺序
· 用空行把大块代码分成逻辑上的段落
4.改写怎样的注释
· 注释的目的是尽量帮助读者了解的和作者的一样多
· 什么不需要注释
· 不要为那些从代码本身就能快速推断的事实写注释
· 不要为了注释而注释
· 好代码>坏代码+好注释
· 不要给不好的名字加注释,应该把名字改好
· 记录你的思想
· 加入导演评论
· 为代码中的瑕疵写注释
· 给常量加注释
· 站在读者的角度
· 意料之中的提问
· 公布可能的陷阱
· 全局观注释
· 总结性注释
· 最后的思考,克服作者心理阻滞
· 不管你心理想什么,先写下来
· 读一下这段注释,看看有什么可以改进的
· 不断改进
· 总结
· 什么地方不需要注释
· 能从代码本身迅速推断事实
· 用来粉饰烂代码的拐杖式注释,应把代码改好
· 你应该记下来的想法
· 对于为什么代码写成这样而不是那样的内在理由,指导性批注
· 代码中的缺陷,使用像TODO:或者XXX:这样的标记
· 常量背后的故事,为什么是这个值
· 站在读者的立场上思考
· 预料到代码中哪些部分会让读者说:啊?,并且加上注释
· 为普通读者预料之外的行为加上注释
· 在文件/类的级别上使用全局观注释来解释所有的部分是如何一起工作的
· 用注释总结代码块,使读者不至于迷失在细节中
5.写出言简意赅的注释
· 注释应当有很高的信息/空间率
· 让注释保持紧凑
· 避免使用不明确的代词
· 润色粗糙的句子
· 精确地描述函数的行为
· 用输入/输出例子来说明特别的情况
· 声明代码的意图
· 具名函数的注释
· 采用信息含量高的词
· 总结
· 当像 it 和 this 这样的代词可能指代多个事物时,应避免使用它们
· 尽量精确地描述函数的行为
· 在注释中用精心挑选的输入和输出例子来说明
· 声明代码的高层次意图,而非明显的细节
· 用嵌入的注释来解释难以理解的函数参数
· 用含义丰富的词来使注释简洁
三、简化循环和逻辑
1.把控制流变得易读
· 把条件、循环以及其他对控制流的改变做得越自然越好,运用一种方式使读者不用停下来重读你的代码
· 条件语句中的参数顺序
· if/else语句块的顺序
· ?:条件表达式,三目运算符
· 相对于追求最小化代码行数,一个更好的度量方法是度量方法是最小化人们理解它所需的时间
· 默认情况下使用if/else,?:三目运算符只有在最简单的情况下使用
· 避免do/while循环
· 从函数中提前返回
· 臭名昭著的goto
· 最小化嵌套
· 嵌套是如何累积而成的
· 通过提早返回来减少嵌套
· 减少循环内的嵌套
· 把循环、条件和其他跳转写得简单易读
· 总结
· 写一个比较时,把改变的值写在左边并且把更稳定的值写在右边更好一些
· 重新排列if/else语句的语句块,先处理正确的简单的有趣的情况。有时这些准则会冲突,但是当不冲突时,这是要遵循的经验法则
· 三目运算符、do/while循环、goto经常会导致代码的可读性变差。最好不要用它们,总是有更整洁的代替方式
· 嵌套的代码块应改写成更加线性的代码来避免神嵌套
· 在函数顶部处理简单的情况尤其有用
2.拆分超长的表达式
· 用做解释的变量
· 总结变量
· 使用德摩定理
· 滥用短路逻辑
· 要小心 智能 的小代码段,它们往往在以后会让别人读起来感到困难
· 找更优雅的方式
· 拆分巨大的语句
· 另一个简化表达式的创意方法
· 总结
· 把巨大的表达式拆成小段
· 通过简单的名字描述子表达式来让代码文档化
· 帮助读者识别代码中的主要概念
3.变量和可读性
· 问题
· 变量越多,越难全部跟踪它们的动向
· 变量的作用域越大,就需要跟踪它的动向越久
· 变量改变得越频繁,越难以跟踪它的当前值
· 减少变量
· 没有价值的临时变量
· 减少中间结果
· 减少控制流变量
· 缩小变量的作用域
· 让你的变量对尽量少的代码行可见
· 不同语言不同的管理作用域规则
· C++的if语句作用域
· JavaScript创建 私有 变量
· JavaScript全局作用域
· Python和JavaScript中没有嵌套的作用域
· 把定向向下移
· 只写一次的变量更好
· 操作一个变量的地方越多,越难确定它的当前值
· 总结
· 减少变量
· 减小每个变量的作用域
· 只写一次的变量更好
四、重新组织代码
1.抽取不相关的子问题
· 积极地发现并抽取出不相关的子逻辑
· 纯工具代码
· 其他多用途代码
· 意料之外的好处
· 创建大量通用代码
· 项目专有的功能
· 简化已有接口
· 按需重塑接口
· 过犹不及
· 把一般代码和项目专有的代码分开
2.一次只做一件事
· 应该把代码组织得一次只做一件事情
· 任务可以很小
· 从对象中抽取值
3.把想法变代码
· 清楚地描述逻辑
· 了解函数库是有帮助的
· 把这个方法应用于更大的问题
· 用自然语言描述解决方案
· 递归地使用这种方法
4.少写代码
· 最好读的代码就是没有代码
· 别费神实现那个功能,你不会需要它
· 质疑和拆分你的需求
· 保持小代码库
· 让你的代码库越小越轻量越好
· 删除没用的代码
· 熟悉你周边的库
· 为什么重用库有这么大的好处
· 避免编写新代码
· 从项目中消除不必要的功能,不要过度设计
· 重新考虑需求,解决版本最简单的问题,只要能完成工作就行
· 经常性地通读标准库的整个API,保持对它们的熟悉程度
四、精选话题
1.测试与可读性
· 测试应当具有可读性,以便其他程序员可以舒服地改变或者增加测试
· 程序员会不敢修改真实代码
· 程序员不会再增加新的测试
· 这段测试什么地方不对
· 使整个测试更可读
· 创建最小的测试声明
· 实现定制的 微语言
· 让错误消息具有可读性
· 手工打造错误信息
· 选择好的测试输入
· 简化输入值,简单又能完成工作的测试值更好
· 一个功能的多个测试
· 为测试函数命名
· 对测试较好的开发方式
· 测试驱动开发
· 走得太远
· 牺牲真实代码的可读性,只是为使能测试
· 着迷于100%的测试覆盖率
· 让测试成为产品开发的阻碍
2.设计并改进“分钟/小时计数器”
· 定义类接口
· 改进命名
· 改进注释
· 得到外部视角的观点
· 尝试1:一个幼稚的方案
· 这段代码易于理解吗?
· 一个更易读的版本
· 性能问题
· 它一直不停地在变大
· MinuteCount()和HourCount()太慢了
· 尝试2:传送带设计方案
· 实现两阶段传送带设计
· 这个设计不灵活
· 这个类占用的内存很大
· 尝试3:时间桶设计方案
· 实现时间桶设
· 实现TrailingBucketCounter
· 实现ConveyorQueue