第11章:变量名的力量
第11章:变量名的力量:把问题域、作用域和团队规则变成读者可复述的命名线索。
学习目标
- 能把一个变量名改写为包含问题域角色、作用域和单位线索的名称。
- 能用误导模式和三个作用域样本检查名称是否让读者做出错误推断。
- 能回答:一个短名何时诚实,何时必须展开成带单位和角色的长名?
为什么需要好的变量名
短名字只减少了几次键入,却可能把一次误读推迟到数周后的维护工作。读者看到 total,不知道它是金额、数量还是已经折扣后的结果;看到 flag,也无法直接判断 true 代表“已发送”还是“需要发送”。当变量的使用者换人、代码离开屏幕后,名称就是最早可见的上下文。
本章把命名当作一个小型设计决策:名称应该让读者先猜到对象,再猜到角色,最后知道单位或状态。猜一猜:把一个同类型的数从函数内部搬到模块边界时,原来的短名是否仍然足够?下面的实验会把这次猜测变成可观察的变化。
常见命名误区
先把名称当作阅读接口
一个 ↡把问题域对象、角色或动作写进标识符,使读者不必追溯实现才能猜到含义 不等于越长越好;它的价值是减少读者需要补全的隐含信息。invoiceTotalCents 告诉读者这是发票总额,并且以分计数;total 只告诉读者这里有一个总数。
↡一个名字在代码中可被直接看见和使用的范围,例如循环体、函数或模块 改变的是读者可获得的上下文,而不是变量本身的含义。循环体里的 index 通常有足够的邻近线索;跨函数传递的 lineItemCount 则需要把对象和角色写出来。名称长度应该随着可见上下文减少而增加。
↡把计算结果的阶段或性质写进名称的限定词,例如 estimated、raw、normalized 或 cached 用来区分原始值、估算值、缓存值和归一化值。price 与 estimatedPrice 不能在同一层被当成同一种承诺;限定词提醒调用者还存在一次转换或不确定性。
第11章 变量名的力量
本章的中心判断是:名称把问题域概念、角色、单位和状态带进阅读现场。它不是变量类型的重复标签,而是让维护者在改代码前就能排除一部分错误解释的接口线索。
11.1 选择好变量名的注意事项
选择名字时先问读者要做什么决定,再保留能影响这个决定的词。名称应描述当前含义,不描述过去的实现;如果一个词无法帮助调用、审查或调试,就不要为了显得正式而堆进去。
最重要的命名注意事项
最重要的标准是名副其实:名称描述当前含义,不描述过去的实现。pendingInvoiceLines 比 buffer 多传达了业务角色,读者不用先研究数据结构就能判断它处于待处理阶段。
以问题为导向
以问题为导向意味着优先使用领域对象和动作,而不是 data、temp、thing 这类实现占位。retryAfterSeconds 比 value 更能指导调用者,因为它同时说明了决策和单位。
最适当的名字长度
长度取决于歧义和可见上下文。局部循环可以使用 index,模块级状态可能需要 selectedInvoiceId;验收标准不是字符数,而是读者能否一眼排除同型误用。
变量名字的效果范围
变量名字的效果范围会随着引用者数量扩大而扩大。定义处清楚的名字,在返回值、日志、测试和异步回调里未必仍然清楚;跨边界的数据应保留角色,局部临时值才可以依赖邻近表达式。
变量名字中的计算值限定词
“变量名字中的计算值限定词”要让读者知道结果处于哪个处理阶段,以及它是否仍可能被转换、刷新或重新计算;限定词必须对应真实的状态承诺,不能只作为装饰性前缀。
使用 raw、normalized、cached、estimated 等限定词时,必须说明它们对应的阶段。normalizedEmail 表示已经完成规范化,不能在另一处又被当成用户原始输入。
变量名字中的常用反义词
反义词要成对且稳定:start/end、min/max、first/last、enable/disable 都比临时拼出的 notStart 容易阅读。先决定哪个方向是正向语义,再把它固定到规则里。
11.2 为特定类型的数据命名
不同类型的数据有不同的阅读风险。命名时不要只重复类型名称,而要说明它在当前任务中的身份;布尔、枚举和常量尤其需要把方向、成员或单位写清楚。
为循环索引命名
循环索引要表达遍历对象。对订单使用 orderIndex,对嵌套行项目使用 lineItemIndex;只有循环极短且没有第二个索引时,index 才不会造成误读。
为状态变量命名
状态变量要说明状态属于谁、处于什么阶段。connectionState 比 state 更能限制搜索范围;如果状态有明确集合,还应使用稳定的状态名,而不是让数字承担秘密含义。
为临时变量命名
“为临时变量命名”并不意味着给每个中间值堆叠长名称,而是要在它跨过一行或一个小范围后仍保留对象、阶段和用途线索,让调试者能判断它是否可以安全复用。
临时变量也要表达临时值的角色。normalizedSku、nextPageToken 比 tmp 和 x 更容易在调试器里定位;一次没有独立语义的中间运算则可以直接内联。
为布尔变量命名
↡只能有真或假两种状态、通常用于表达一个可直接提问的条件 应该能接在“是否”后面朗读:isArchived、hasPermission、canRetry。避免 status、flag 或 notReady 这种需要读者翻译方向的名字。
为枚举类型命名
↡用一组有名字的离散成员表达有限状态,避免让裸数字承载含义 的类型名应说明它描述的对象,例如 InvoiceStatus;成员名保持同一抽象层次,如 Draft、Paid、Overdue。
为常量命名
常量名要显式表达固定的业务意义和单位:MaxLoginAttempts 与 Timeout 的信息量不同,CacheTtlSeconds 更不会被误当成毫秒。大小写属于格式规则,单位和用途属于数据合同。
11.3 命名规则的力量
单个好名字只能帮助一个读者;稳定的规则才能让一组文件互相解释。规则应优先解决高频误读:布尔方向、单位后缀、缩写表、类型前缀以及跨语言接口的共享词汇。
为什么要有规则?
规则把审查从“我个人喜欢这个名字”变成可复核的比较。它降低团队成员、代码生成器和不同语言模块之间的翻译成本,也让搜索和重命名更有把握。
何时采用命名规则
当同一类对象反复出现、同型值容易交换,或代码跨模块、跨团队、跨语言传递时,就值得写下规则。局部实验可以轻量,共享接口则要把最容易误解的部分提升为团队约定。
正式程度
正式程度应该和代码寿命、受众与风险匹配。短期脚本不必复制产品的全部前缀,但公开库、持久化字段和协议边界必须优先保证稳定、可搜索和可迁移。
11.4 非正式命名规则
非正式规则也需要一个可复述的边界:什么情况必须遵守、哪些缩写已经约定、例外如何被说明。它可以从一页示例开始,随着真实冲突增加再扩展。
语言无关规则的指导原则
语言无关的部分关注意义:名称描述对象和角色,布尔值读作问题,数值保留单位,缩写来自共享词表,过时名称不能继续误导读者。这些原则换语言后仍然成立。
语言相关规则的指导原则
语言相关规则处理大小写、模块导出、常量写法和类型命名。它们不应覆盖语义判断;TypeScript 的类型保护和 C++ 的强类型包装都要先解决“这个数代表什么”。
混合语言编程的注意事项
混合语言系统要为边界选择共同词汇。一个模块说 userId,另一个模块说 customerNumber,如果它们是同一概念就应减少翻译;如果不是同一概念,就要明确写出转换关系。
命名规则示例
可以从四条小规则开始:金额使用最小货币单位并带后缀;持续时间带 Seconds 或 Milliseconds;布尔值使用 is/has/can/should;缩写只使用团队词表中的形式。每条规则都配一个反例。
11.5 标准前缀
前缀只有在表达稳定的类别或状态时才有价值。is、has、can 让布尔语义明显;raw、cached、default 可以表示处理阶段,但不能重复类型系统已经表达的内容。
用户自定义类型缩写
↡团队为常见用户类型约定的短写形式,只有在词义稳定且可查时才值得使用 应来自共享词表,例如 acct 是否代表 account 要全团队一致。只在一个文件里自创缩写会缩短字符,却扩大搜索和入职成本。
语义前缀
语义前缀强调值的阶段、来源或可变性,而不只是数据类型。previousBalance、newBalance、displayName 比 string1、string2 更能让差异进入阅读;前缀不再区分真实状态时就应删除。
标准前缀的优点
标准前缀形成视觉扫描线:看到 is 就期待条件,看到 cached 就会问失效策略,看到 raw 就知道它可能尚未清洗。它不是正确性的证明,却能把正确的问题提前暴露在调用点。
11.6 创建具备可读性的短名称
短名称的目标是压缩重复上下文,而不是删除语义。先写准确的候选名,再检查哪些词已经由作用域、类型或容器可靠提供;只删掉不会改变决策的部分。
一般的缩写指导原则
缩写应可发音、可搜索、可在代码库中唯一解释。删除元音、保留首字母或截断单词都可能制造碰撞;团队词表要包含全称、缩写、反例和适用边界。
语音缩写
语音缩写只能在团队确实按同一种方式读它时使用。config、repo 可能容易理解,但没有上下文的 pr、svc、mgr 可能对应多个全称;发音相似不等于语义唯一。
有关缩写的评论
缩写不是绝对禁用项:行业标准、协议字段和广泛共享的技术词可以保留。关键是把例外写成规则,并检查读者是否能从搜索、类型或文档中恢复全称。
11.7 应该避免的名称
避免没有承诺的名称:data、info、thing、value、temp、flag 和仅表示类型的 list 都需要追问对象和角色。过时名称同样危险,重构后应清理 old...、new... 等过渡痕迹。
关键点
命名质量来自一条可复核链:问题域决定语义,作用域决定长度,数据类型决定线索,团队规则决定一致性,误导实验检验维护者是否会误判。先看名字能否指导决定,再讨论风格偏好。
三步命名实验:从线索到决定
先预测:在三个作用域和一个误导模式之间切换时,哪一列会最先变得不可靠?每一步都把同一套“名称 → 语义 → 决策”图示展开,最后用重置按钮确认实验回到局部块、准确名称和初始检查点。
1. 先锁定问题域与单位
第 11 章 · 命名约束实验
名称不是标签,而是阅读接口
切换问题域、作用域或规则,再打开误导模式,观察读者能否从名字推出角色和单位。
低风险:名称给出了角色、范围或单位线索
练习与答案
练习
问题 1:按作用域改名
把 v 改成一个在模块边界仍然诚实的变量名。假设它表示“发票总额,单位为分”,并说明你为什么没有只写 total。
问题 2:改 Demo 代码并验证误导模式
把下面的名字替换成更可读的版本,然后在实验中依次选择“模块边界”和“注入误导模式”,写下审查结果应如何变化。
const value = 1299;
if (flag) send(value);问题 3:判断缩写是否值得保留
团队想把 customer 一律缩写成 cust。请写出保留它前要检查的两个条件,并给出一个应使用全称的边界。
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 语义名称
把对象和角色写进名字,让读者不必先打开实现就能猜到它表示什么。
- 作用域
一个变量能被直接看到和使用的范围;范围越大,名字通常越需要补充上下文。
- 计算值限定词
说明值处于哪个处理阶段的词,例如原始、缓存或估算,帮助读者判断它的承诺。
- 布尔变量
只有真和假两种状态的变量,适合用能读成问题的名字表达。
- 枚举类型
用一组有名字的成员表示有限选择,避免让裸数字承载状态含义。
- 类型缩写
团队为常用类型约定的短写;只有全称唯一、可搜索、跨边界稳定时才值得使用。
资料边界与独立改写
本章的单元和 34 个目录节点依据 《代码大全(第 2 版)》2006 年中文公开试读目录 核对;英文版书目信息和出版范围参考 Microsoft Press 官方书页。关于命名、类型和接口边界的迁移说明参考 C++ Core Guidelines 与 ECMAScript 语言规范。这些资料用于范围、书目和现代实践的边界核对,不冒充原书未公开正文。
实验图、代码片段、练习与答案均为围绕本章目录的独立教学重写;它们用于让读者检验命名决策,不是原书段落的翻译或逐字复现。