3.4 变量与常量:消灭魔法数字


3.4 变量与常量:消灭魔法数字

本节摘要:变量与常量规范的两条主线是作用域最小化与字面量具名化。本节讲声明即初始化、就近声明原则,讲魔术数字与魔术字符串如何伤害可读性与可维护性,讲常量的命名与集中管理、枚举对一组相关常量的表达力,以及临时变量的克制使用。

学习目标

阅读完本节,你应当能够:

  1. 用最小作用域原则重排变量的声明位置
  2. 说明声明即初始化如何消除一类未定义状态
  3. 把代码中的魔术数字与魔术字符串改写为具名常量
  4. 判断一组常量何时应升级为枚举
  5. 制定配置常量的集中管理方案

一、问题与直觉:status == 2 是什么意思

一段到处可见的代码:if (order.status == 2)。二是什么?已支付?已发货?已取消?读代码的人只能去查(运气好找到枚举定义,运气不好翻数据库文档)。这就是魔术数字——凭空出现、无解释、数字本身不承载语义的字面量。它的孪生兄弟是魔术字符串:if (user.type == "vip2"),除了语义不明之外还多一层风险:这个字符串散布在十七处,改一次资费命名要改十七处,漏一处就是线上不一致。

魔术值的第二宗罪是修改成本。字面量不可搜索语义——搜 2 会命中几千个无关结果,实际上没人敢改它。第三宗罪是拼写错误静默通过:常量拼错编译器立刻报错,字面量 "adminn" 拼错后代码照常运行,只是永远走不进那个分支——这类缺陷最难排查,因为一切看起来都"正常运行"。

解法便宜得出奇:给值一个名字。if (order.status == OrderStatus.PAID),语义、可搜索、防拼写三宗罪一次治愈。本节后半部分展开怎么做;先看变量这一半的规范。

二、核心原理:变量的三条纪律

2.1 纪律一:作用域最小化

变量应在尽可能小的作用域内声明:循环内用的变量声明在循环里,分支内用的声明在分支里。作用域小的收益:读者在小区间内就能掌握变量的完整生命周期,不必警惕"它后面还会被改吗";名字冲突与误用机会随作用域收缩而消失;变量能被垃圾回收的时机也更早。

对照示例(概念代码):

# 反例 变量在远处声明 读者要记住它很久 result = None if condition: result = "Success" else: result = "Failure" print(result) # 正例 声明即初始化 就近使用 if condition: outcome = "Success" else: outcome = "Failure" print(outcome)

2.2 纪律二:声明即初始化

未初始化的变量处于"薛定谔状态"——在读到赋值行之前,它是未定义的。任何提前读取都是隐患,编译型语言报错还好,解释型语言直接运行时爆炸。声明时就给初值(哪怕是个中性的默认值),让变量从存在那一刻起就语义完整。确实无法立即给出有意义初值的场景(比如值取决于后续分支),用"结果对象"或延迟声明处理,而不是先占个坑。

2.3 纪律三:临时变量克制而具名

临时变量不是垃圾桶。tmptemp1data2 这类名字把"我不知道该怎么叫它"写进了代码。绝大多数"临时变量"其实有明确语义——暂存计算结果的叫 subTotal,暂存格式化输出的叫 formattedPhone。名字升级之后,"临时"变量就变成了普通的、有尊严的局部变量。真正的短期占位(比如交换两个值时的中转)作用域通常只有两三行,叫 tmp 无伤大雅——回到第 2.2 节的通则:命名长度适配作用域。

三、工程实践要点:常量与枚举的用法

3.1 常量的命名与放置

常量用全大写蛇形命名(第 2.2 节),如 MAX_RETRIESDEFAULT_TIMEOUTAPI_TIMEOUT。放置原则按使用范围分层:模块内使用的放模块顶部(与导入区之后),让读者进入文件先看到全部"参数旋钮";跨模块共享的业务常量集中到专门的常量模块,统一进出口,防止同义常量在多处各定义一份然后悄悄分叉。

3.2 枚举:一组相关常量的正确形态

当常量成组出现(订单状态有一串、用户角色有一串),散装常量不如枚举(概念代码):

# 散装常量 集合关系靠人脑维护 STATUS_PENDING = 1 STATUS_PAID = 2 STATUS_CANCELLED = 3 # 枚举 关系显式且可遍历可校验 enum OrderStatus: PENDING PAID CANCELLED

枚举的表达力优势:成员自动聚合一处、可遍历(校验入参是否合法一循环搞定)、类型系统帮你拦截"拿订单状态与用户角色比较"这类笔误。散装常量做不到任何一条。

3.3 配置常量的集中管理

数据库连接串、外部服务地址、默认分页大小这类会随环境变化的值,比业务常量多一层要求:不能硬编码在业务代码里。规范做法是集中到配置层(配置文件或环境变量),代码只引用配置项名。硬编码的连接串与密钥除了难改之外更是安全事故源——代码入库等于密钥公开,扫描工具也会把它标红。

⚠️ 常见坑:为"性能"保留魔术数字("定义常量多一次查找")。现代编译器与解释器对常量的处理早就不构成可测量差异,用可读性换一个不存在的性能,是笔稳亏的买卖。真到了纳秒必争的热点,先拿分析数据说话(第 2.1 节的权衡准则)。

💡 关键直觉:每消灭一个魔术值,就把一处"必须记住或查阅"的负担变成一处"读名字即懂"的享受。常量化是全代码库范围的一次性还债,利息是此后每次阅读的零成本。

值的形态 何时用 示例 升级路径
字面量 仅真正的数学或语言常理 0、1、2 的下标步进 保持
具名常量 单个不变的配置或阈值 MAX_RETRIES 直接定义
枚举 一组相关的离散状态 OrderStatus 散装常量聚合成枚举
配置项 随环境变化的值 数据库连接串 移入配置层

本节速览

  • 魔术值三宗罪:语义不明、不可搜索、拼写错误静默通过
  • 作用域最小化:变量活在被需要的最小区间,生命周期一目了然
  • 声明即初始化:消灭未定义状态这一整类隐患
  • 临时变量具名化:tmp 几乎总有更诚实的名字
  • 常量分层放置:模块顶部或集中常量模块,配置值进配置层绝不硬编码
  • 枚举优于散装常量:聚合、可遍历、类型安全三重表达力
  • 性能借口不成立:常量化的运行时代价不可测量

下一章从"组织代码"升级到"设计逻辑":错误处理、控制流与编程范式的规范。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U