2.1 可读性:代码首先是写给人的


2.1 可读性:代码首先是写给人的

本节摘要:可读性是代码质量的第一指标——代码首先是写给人读的,其次才是给机器执行的。本节讲清可读性为什么值得投入,以及通过命名、布局、注释、结构、控制流五条路径系统性提升可读性的方法,最后讨论可读性与性能冲突时的权衡准则与可读性自查清单的用法。

先说结论

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

  1. 论证可读性在代码质量体系中的基础地位
  2. 说出提升可读性的五条路径及各自的典型手段
  3. 用卫语句与提前返回把三层以上的嵌套压平
  4. 用常量与枚举消灭代码中的魔术数字和魔术字符串
  5. 在可读性与性能的冲突中做出正确取舍并留下记录

一、问题与直觉:读代码的时间是写代码的十倍

有这样一段真实存在的代码:函数名叫 doProc,三个参数分别叫 abc,函数体一百二十行,六层 if 嵌套,中间夹着 if (x == 2)if (s.equals("Y")) 这样的判断。它功能完全正确,跑在生产上三年没出过事。直到有一天需要给 c 参数加一种新取值,负责修改的工程师花了两天才敢动手——光是确认 x 等于 2 意味着什么、字符串 Y 从哪来,就翻遍了半个代码库。

这段代码的问题全部属于可读性。功能正确的代码不一定是好代码,因为代码的价值要在被理解和被修改时才兑现。工业界反复验证的经验规律是:读代码的时间远多于写代码的时间,一个函数可能花一小时写成,但之后会被几十个工程师阅读上百次。写代码时为读者多想五分钟,是在为整个生命周期做优化。反过来说,不可读的代码是一种负资产——它不仅自身没有价值,还持续消耗每个接触它的人。

有人觉得可读性是"锦上添花",功能才是"雪中送炭"。这个次序恰恰颠倒了:功能错误会在测试中暴露,很快被修复;可读性缺失不会在任何单测中报错,它以更隐蔽的方式收税——每次阅读多花的时间、每次修改战战兢兢引入的缺陷、每个新人多走的学习弯路。

二、核心原理:五条提升路径

2.1 路径一:清晰的命名

命名是可读性最大的杠杆,一个好名字能省掉一整段注释。remainingRetryCount 不需要注释,n 需要。命名的具体规则体系(风格、前缀、通则)是下一节的主角,这里只强调一个判断标准:读者能否不跳转定义就说出这个名字装着什么。答案是否定的名字,都值得重写。

2.2 路径二:合理的布局

视觉布局服务于逻辑结构。缩进展示嵌套层级,空行分隔逻辑段落,运算符两侧的空格让表达式呼吸。看一个对照(概念代码):

# 拥挤难读 if(x>y){r=x-y;}else{r=y-x;} # 呼吸感 if (x > y) { difference = x - y } else { difference = y - x }

语义完全相同,第二种让眼睛能快速切分 token。布局规则琐碎,好消息是它们全部可以交给格式化工具(第 2.3 节、第 6 章),人只需要享受结果。

2.3 路径三:恰当的注释

注释解决代码本身表达不了的问题:为什么选这个算法、为什么这里要特判、这段代码有什么陷阱。注释不该复述代码——i++; // i 加一 这样的注释比没有更糟,因为它训练读者跳过注释。什么时候写、写什么、用什么格式,第 3.1 节会展开。

2.4 路径四:单一职责的小函数

长函数是可读性头号杀手,杀器有二:一是读者要在脑中同时维护太多上下文,二是改动时影响范围模糊。解法是把"一件事"拆成一个函数。看订单处理的对照(概念代码):

# 拆分前:一个函数做五件事 def process_order(order): # 验证订单 ... 十行 # 计算价格 ... 十五行 # 扣库存 ... 十行 # 保存 ... 五行 # 发邮件 ... 八行 # 拆分后:主函数读起来像目录 def process_order(order): validate_order(order) total = calculate_total(order) deduct_inventory(order) save_order(order) send_confirmation(order)

拆分后的 process_order 只有五行,但它让整个流程一目了然;每个子函数可以独立阅读、独立测试、独立修改。判断拆分时机的经验法则:当你需要给函数内部的段落写"第一步""第二步"这类注释时,就是拆分的信号——让函数名代替注释。

2.5 路径五:扁平的控制流

深层嵌套是可读性的另一杀手。三层嵌套意味着读者脑中要维护三个条件的组合状态,每深一层,认知负荷翻倍。解法是卫语句(guard clause)与提前返回:把异常情况在函数开头处理完直接返回,让主逻辑贴左对齐。对照:

# 嵌套版:主逻辑被埋在三层深处 if user is not None: if user.is_active: if user.has_permission: do_admin_action(user) else: do_member_action(user) else: log_inactive(user) else: log_missing() # 卫语句版:主逻辑在最外层 if user is None: log_missing() return if not user.is_active: log_inactive(user) return if user.has_permission: do_admin_action(user) else: do_member_action(user)

配套技巧还有两条:简化布尔表达式——if (a == true && b == false) 应写成 if (a && !b)消灭魔术值——statusCode == 200 换成 status == HTTP_OK"admin" 换成枚举 UserRole.ADMIN。魔术数字与魔术字符串的可读性罪状在于:读者必须猜或查它从哪来、什么含义,而且散落各处时一旦要改就是一场搜索与遗漏的博弈。

💡 关键直觉:可读性的本质是为读者降低带宽占用。命名传递语义,布局传递结构,注释传递动机,小函数传递边界,扁平控制流传递主干——五条路径都在做同一件事:让读者花最少的脑力拿到最多的信息。

三、工程实践要点:权衡与自查

3.1 可读性优先于微小性能

"这个循环里用位运算代替乘法能快几纳秒"——在没有性能证据前,这类优化是用可读性做赌注。正确的次序是:先写可读的版本,用性能分析工具确认瓶颈,再对确认的瓶颈做定向优化,并为每处牺牲可读性的优化写注释说明原因与前提(例如:仅当分析工具显示此处为热点时才启用此写法)。绝大多数代码永远不会成为瓶颈,为它们预付可读性代价是最常见的浪费。

⚠️ 常见坑:为了炫技使用语言冷僻特性压缩代码行数。行数少不等于简单——一个三行的嵌套推导式可能比十行的普通循环难懂得多。简洁性的判断标准是读者的理解成本,不是字符数(这个话题第 4.3 节继续)。

3.2 用自查清单兜底

可读性主观成分高,团队需要一份清单把主观判断部分客观化。推荐的基础条目:命名是否描述性且一致;格式是否符合团队规范;注释是否只解释为什么;函数是否单一职责;嵌套是否不超过两到三层;布尔表达式是否简洁;魔术值是否已常量化;错误信息是否具体;是否做过代码审查。这份清单在每次提交前过一遍,比评审时被打回效率高得多。

路径 一句话规则 典型坏味道 修复手段
命名 名字即注释 单字母、无义缩写 换成描述性名称
布局 让眼睛能切分 挤成一行的表达式 格式化工具
注释 解释为什么 复述代码的废话 删或改写
小函数 一件事一函数 带步骤注释的长函数 提取子函数
控制流 主逻辑贴左侧 三层以上嵌套 卫语句提前返回

3.3 一点判断上的主张

我倾向于把"读者"默认设定为"六个月后对这段代码一无所知的自己"来写代码。以新同事为假想读者容易高估上下文(你会不自觉假设他知道业务背景),以未来自己为假想读者则非常诚实——你清楚自己会忘掉什么。这个心态调整看似微小,却能稳定产出更防御性的命名与更完整的注释。

重点提炼

  • 第一原则:代码首先是写给人读的,读的时间十倍于写,可读性是在优化整个生命周期
  • 五条路径:清晰命名、合理布局、恰当注释、单一职责小函数、扁平控制流
  • 拆分信号:需要给函数内段落写"第几步"注释时,就是提取子函数的时机
  • 卫语句:异常条件开头处理完就返回,让主逻辑贴左对齐
  • 消灭魔术值:数字与字符串字面量一律常量化或枚举化
  • 性能例外:可读性优先,确有热点才定向优化并注释说明

下一节聚焦五条路径中最有力的那条:命名——从 cal_tot 到 calculateTotalPrice 的完整方法论。


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