跳转至

2.3 整洁代码

2.2 数据结构 | 首页 | 下一节: 2.4 源代码管理


核心实践

  • 命名是代码可读性的关键:文件、类、变量、函数的命名要表意清晰——读代码时不应需要查阅定义才能理解意图。
  • 避免过长的函数和类:合理拆分到方法和类中,遵循单一职责原则
  • 遵循项目结构约定:保持目录清晰,让新成员能直觉地找到代码位置。
  • 将复杂的布尔条件提取为命名良好的函数if (user.IsEligibleForDiscount()) 胜过 if (user.Age > 65 && user.YearsAsMember > 5 && !user.HasOutstandingBalance)
  • 尽量让代码自解释:代码写出来,他人就能理解"做了什么"。
  • 以代码的形式写文档:Markdown 文件放在仓库 docs/ 目录中,与代码一同版本管理、一同受 CI 检查(拼写、死链)。文档应描述"为什么这样做"和"怎么做到的"(设计目标、使用场景、组件说明、架构概览)。
  • 遵循语义化版本(SemVer)规范。

语义化版本(SemVer)

版本号格式 MAJOR.MINOR.PATCH

  • MAJOR(主版本):不兼容的 API 变更时递增。1.0.0 之前(0.y.z 阶段)API 不稳定,任意变更。
  • MINOR(次版本):向后兼容的功能新增时递增。标记废弃(deprecation)也必须递增次版本。
  • PATCH(修订版本):向后兼容的 Bug 修复时递增。
  • 预发布标签(如 1.0.0-alpha.1),构建元数据(如 1.0.0+20130313144700)。
  • 已发布的版本不可修改——任何修改必须发布新版本。
  • 主版本递增时,次版本和修订版本归零。

代码注释是谎言

核心论点来自 Pieter Koornhof:如果你需要写注释解释代码做了什么,你已经未能用代码表达清楚意图。

注释为什么成为问题: - 陈述显而易见的事实,纯冗余:// The Person's First Name 后面接 public string FirstName { get; set; }。 - 阻碍流畅阅读:注释散落在代码中,阅读者在注释行和代码行之间跳跃,意图不清晰。 - 注释很少随代码更新——代码改了注释没改,从有用变成误导。注释经常成为谎言。 - TODO 注释往往永远不会被完成。 - 注释随代码一同被复制,代码再改时注释不改,信息彻底失真。

"好注释"的例外: - 无法重构的遗留代码文档,记录潜在副作用。 - 框架限制说明:// EntityFramework requires a default constructor。 - 功能分支上的 TODO(合并前必须实现或转为 Backlog 工单)。 - 高度优化的代码,可能难以阅读(但大多数人不常遇到)。 - API 文档注释(如 JavaDoc / JSDoc / XML doc),仅限公开接口。

自律原则:每当想写注释,先问自己"能否重构代码让意图自明?"如果不能,注释是必要的妥协——但不是借口。

组合优于继承

组合优于继承是 GoF《Design Patterns》(1994)推广的 OOP 设计原则:优先用"有什么"(has-a)而非"是什么"(is-a)来实现代码复用。

维度 继承 组合
关系 is-a("是什么") has-a("有什么")
耦合度 高——子类紧密绑定父类实现 低——组件可独立替换
灵活性 静态,编译时决定 动态,运行时替换组件
问题 深层继承树、钻石问题(菱形继承)、脆弱基类 可能需要大量转发方法

示例(游戏对象): - 继承方案:Player 同时继承 Visible、Solid、Movable → 需要多重继承 → 菱形问题。 - 组合方案:GameObject 包含 VisibilityDelegateCollisionDelegateUpdateDelegate 成员 → 构造函数传入不同的组合即可创建 Player(可见+可碰撞+可移动)、Cloud(可见+可移动)、Trap(可碰撞)。

现代语言通过 traits、mixins、类型嵌入、默认接口方法等特性弥补了组合需要写转发方法的问题。实证研究(2013 年对 93 个开源 Java 项目的分析):约 2% 的继承用法可被组合替换,另有 22% 仅用于外部或内部复用——继承没有那么灾难性,但当组合可用时选组合更安全。

测试驱动开发(TDD)

TDD 的核心循环:红(Red)→ 绿(Green)→ 重构(Refactor)

  1. :先写一个失败的测试,描述期望的行为——此时还没有实现代码。
  2. 绿:写最少量的代码让测试通过——不追求完美,只求让测试变绿。
  3. 重构:在测试保护下优化代码结构,消除重复,提升可读性——确保测试仍然通过。
  4. 循环:下一个测试→下一个功能。

TDD 的核心价值不是测试本身,而是驱动设计——迫使你从调用者的角度思考接口,促使低耦合、高内聚的设计自然浮现。测试是副产品,好的设计才是目标。


来源

  • Semantic Versioning 2.0.0: https://semver.org/
  • Pieter Koornhof — Code Comments are Lies: https://sneakycode.net/code-comments-are-lies (original at CodeProject)
  • Wikipedia — Composition over inheritance: https://en.wikipedia.org/wiki/Composition_over_inheritance

2.2 数据结构 | 首页 | 下一节: 2.4 源代码管理