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

《编写可读代码的艺术》深入探讨了代码可读性的基本原则,包括选择专业且具象的名字,避免误解,注重审美,精炼注释以及简化循环和逻辑。书中强调了代码的组织结构,提倡一次只做一件事,少写代码,通过测试增强可读性,并通过实例解析如何改进代码设计。通过这本书,开发者可以学习如何提升代码的可读性和可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《编写可读代码的艺术》

标签: 代码能力 读书笔记


第零部分 基本原则


第1章 代码应当易于理解

可读性基本定理: 代码的写法应当使别人理解它所需的时间最小化

  • 并不是代码行数越小越好
  • 可以适当添加注释

第一部分 表面层次的改进


第2章 将信息装到名字里

  • 选择专业的词,例如“get”可以换为“download”或者“fetch”,树中的高度使用“height”等避免使用“size”,选择词语更有表现力,清晰和精确
  • 避免使用像“tmp”和“retval”这样泛泛的名字
    • ”tmp”只应用于短期存在且临时性为其主要存在因素的变量。
    • 要使用这些名字,必须有好的理由。
  • 用具体的名字替代抽象的名字(例:DISALLOW_EVIL_CONSTRUCTORS)
  • 为名字附带更多信息
    • 带单位的值,start_ms/size_mb/max_kbps
    • 常见其他附加属性,字符编码/是否加密/是否安全
  • 决定名字的长度
    • 小作用域中可以使用较短的名字,其他情况使用长名字,对于当代IDE,名字补全很智能
    • 省略词和缩写词需要考虑对于新员工是否理解
    • 去掉没有用的词语,如coverToString -> toString
  • 利用名字的格式来表达含义,不同实体(类/函数/全局变量/局部变量/常量)可以使用不同的实体

第3章 不会误解的名字

这个名字会被别人解读为其他的含义么,filter是指的挑出还是去掉,length是指的字节数还是字符数

  • 推荐使用min和max来表示包含极限
  • 推荐使用first和last来表示包含范围
  • 推荐使用begin和end来表示包含/排除范围
  • 给布尔命名需要明确,如read_passwd->need_passwd/is_authenticated,disable_ssl->use_ssl
  • 与使用者的期望相匹配,如get表示轻量级访问,size默认复杂度o(1)

第4章 审美

三条原则:
- 使用一致的布局,让读者很快就习惯这种风格
- 让相似的代码看上去相似
- 把相关的代码行分组,形成代码块
使用从审美角度讲让人愉悦的代码更容易

  • 重新安排换行来保持一致和紧凑
  • 将长代码行替换为方法调用来去掉不规则
  • 在需要时使用列对齐,使相似的代码看上去相似
  • 使用有意义的顺序,始终如一地使用它,如对象属性处理顺序/构造函数参数顺序等
  • 把声明按块组织起来,按照分组和层次结构思考
  • 把代码分成“段落”,相似想法物理上接近,提供了可见的“脚印”,利于段落间导航

一致的风格比“正确”的风格更重要

第5章 该写什么样的注释

关键思想: 注释的目的是尽量帮助读者了解得和作者一样多

  • 什么不需要注释
    • 不要为了那些可以从代码本身就能快速推断的事实写注释
    • 不要为了注释而注释
    • 不要给不好的名字加注释,应该改变名字
  • 记录你的思想
    • 加入导演评论
    • 为代码中的暇庇写注释,TODO/FIXME/HACK(不得不采用的粗糙解决方案)/XXX(危险,这里有重要问题)
    • 给常量加注释,解释为什么是这个值
  • 站在读者的角度
    • 想象你的代码在其他人看来是什么样子的
    • 意料之中的提问
    • 公布可能的陷阱
    • 全局观注释,类之间的交互/数据的流动/入口点
    • 总结性注释,段落和多层循环可以通过总结注释表明其做的事情
    • 克服作者心理阻滞

第6章 写出言简意赅的注释

  • 注释应该由很高的信息/空间率,让注释保持紧凑,采用信息含量高的词
  • 避免使用不明确的代词,润色粗糙的句子
  • 准确描述函数的行为,如行数->“\n”的个数?
  • 用输入输出例子来说明特殊的情况
  • 声明代码的高层次意图而非明显的细节
  • “具名函数参数”的注释,如connect(/*timeout=*/5)

第二部分 简化循环和逻辑


第7章 把控制流变得易读

把条件/循环以及其他对控制流的改变做得越“自然”越好,运用一种方式使读者不用停下来重读你的代码

  • 条件语句中参数的顺序
    • 左侧:被问询的表达式,其值更倾向于不断变化
    • 右侧:用来做比较的表达式,它的值更倾向于常量
  • if/else语句块的顺序
    • 首先处理正逻辑而不是负逻辑的情况
    • 先处理:简单的情况/有趣或者可疑的情况/具体分析
  • ?:条件表达式,最小化理解时间而不是最小化行数,只在简单情况下使用
  • 避免do/while循环
  • 从函数中提前返回时,注意清理代码结构化:析构函数/try…finally
  • 臭名昭著的goto,在linux中大量使用,用于出口exit作为清理代码
  • 最小化嵌套
    • 嵌套太多,读者不容易搞清楚当前的分支情况
    • 对代码进行修改时,从全新的角度审视它,把它作为一个整体来看待
    • 通过提前返回来减少嵌套
    • 减少循环内的嵌套 - continue

你能理解执行的流程么(复杂编程结果)

  • 线程/信号量/中断处理程序
  • 异常,多个函数调用中向上冒泡一样执行
  • 函数指针/匿名函数,编译时无法知道到底会执行什么代码
  • 虚方法,可能调用未知子类的代码

第8章 拆分超长的表达式

常用方法

  • 用作中间解释的变量
  • 总结变量
  • 使用德摩根定理
  • 滥用短路逻辑
  • 必要时可以使用宏来简化长表达式

第9章 变量与可读性

  • 变量问题
    • 变量越多,读者越难全部跟踪它们的动向
    • 变量的作用域越大,需要跟踪它的动向就越久
    • 变量改动得越频繁,越难以跟踪它的当前值
  • 减少变量
    • 没有价值的临时变量
    • 减少中间结果
    • 减少控制流变量(flag)
  • 缩小变量的作用域
    • 让你的变量对尽量少的代码行可见
    • C++中if语句的作用域
    • js中创建私有变量(闭包)/全局变量
    • Python/JS中没有嵌套的作用域,本地变量会溢出到整个函数
    • 把定义移动到使用的时候
    • 只写一次的变量更好,易于确定当前值,const/final修饰常量更容易理解

第三部分 重新组织代码


第10章 抽取不相干的子问题

工程学:把大问题拆分成小问题再把这些问题的解决方案放回一起

  • 提取步骤
    1. 对于函数或者代码块,确认这段代码的高层次目标是什么?
    2. 对于每行代码,它是直接为目标而工作的么?这段代码的高层次目标是什么?
    3. 如果足够的行数在解决不相关的子问题,抽取代码到独立的函数中
  • 常见的类型
    • 纯工具代码
    • 其他多用途代码,如调试输出美化代码
  • 简化已有接口
  • 按需重塑接口
  • 过犹不及

第11章 一次只做一件事

步骤

  1. 列出代码所做的所有任务
  2. 尽量将这些任务拆分到不同的函数中,或者至少是代码中不同的段落中

第12章 把想法变成代码

How:

  1. 像对待一个同事一样使用自然语言描述代码要做什么
  2. 注意描述中所用的关键词和短语
  3. 写出与描述所匹配的代码

Example:

t = max(t1, t2, t3)
if t1 < t: t1.next()
if t2 < t: t2.next()
if t3 < t: t3.next()

第13章 少写代码

  • 最好的代码就是没有代码,代码越多,越不易于维护和测试
  • 别费神实现那个功能,你不会需要它
    • 实践中,往往高估功能的重要性,低估功能的实现代价
    • 质疑和拆分你的需求
  • 保持小代码库
  • 熟悉你周边的库
    • 每隔一段时间,花15分钟来阅读标准库中的所有函数/模块/类型的名字

第四部分 精选话题


第14章 测试与可读性

测试应当具有可读性,以便其他程序员可以舒服地改变或者增加测试

  • 使测试更好读
    • 对使用者隐去不重要的细节,以便有更重要的细节会更突出
    • 创建最小的测试声明
      • 对于这样的输入情形,期望有这样的行为/输出
      • 用自然语言描述,然后实现定制的“微语言”
      • 例如:CheckScoreSortFilterBeforeAfter(“-5,1,4,3,-99”,”4,3,1”)
    • 让错误消息具有可读性,更好版本的assert/手工打造错误消息
    • 选择好的测试输入
      • 应当选择一组最简单的输入,它能完整地使用被测代码
      • 简化输入值,又简单又能完成工作的测试值更好
    • 一个功能的多个测试,多种场景,各种临界情况
    • 为测试函数命名
      • 考虑被测试的类/函数/场景/Bug
      • 测试函数并不会被工作代码调用,长度无关紧要
  • 对测试较好的开发方式
    • 利于测试的理想代码
      • 明确定义的接口
      • 没有过多状态和其他的设置
      • 没有很多需要审查的隐藏数据
    • 测试驱动开发
    • 优秀代码
      • 类中只有很少或者没有内部状态
      • 类/函数只做一件事
      • 每个类对别的类依赖较少,低耦合
      • 函数的接口简单,定义明确
    • 走得太远
      • 牺牲真实代码的可读性
      • 着迷于100%的代码覆盖率
      • 让测试成为产品开发的阻碍

第15章 设计并改进“分钟/小时计数器”

  • 方案1 传送带方法
    • 每次添加事件时移动hour/minute事件组合且计数
    • bug:事件精度为秒,实际记录的事件段是少于1分钟/1秒时,平均少0.5秒
  • 方案2 时间桶设计
    • 使用时间桶来统计相应时刻内的事件,如1分钟用60个桶表示,可通过桶数目来扩大精度
    • 具体实现(略)

同时发于 https://www.zybuluo.com/spiritnotes/note/278743

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值