捍卫代码整洁之道:Bob 大叔的 100 多条永恒建议

2025-05-28

捍卫代码整洁之道:Bob 大叔的 100 多条永恒建议

Robert C. Martin 的《代码整洁之道》是有史以来最受推荐的编程书籍。随便搜索一下“软件工程师必读书籍”,你几乎肯定能找到这本书。

然而,有些人却对《代码整洁之道》恨之入骨,甚至说现在可能不应该再推荐它了。我认为这种观点是极其错误的。

是的,书中的一些建议确实值得商榷。是的,有些内容感觉过时了,或者随着时间的推移已经过时了。是的,有些例子令人困惑。这些都是事实。但我们不要轻易否定这本书提供的所有好建议!

仅仅因为一些不好的想法就完全忽略一本书,这是几种认知扭曲的完美例子:心理过滤、放大和忽视积极因素,仅举几例。

事实上,鲍勃大叔和其他撰稿人已经在本书的第一章中预先处理了这个问题:

本书中的许多建议都存在争议。你可能不会完全同意,甚至可能强烈反对其中的一些建议。这没关系。我们无法保证最终的权威性。另一方面,本书中的建议是我们经过长期深思熟虑后得出的。这些经验是我们数十年的经验积累和反复试验的结晶。因此,无论你同意与否,如果你不了解并尊重我们的观点,那将是一种遗憾。

事不宜迟,让我们一起来看看《代码整洁之道》这本书里那些隽永的建议吧!我们会逐章讲解,总结鲍勃大叔提出的许多想法。


第 1 章:代码整洁之道

  1. 随着时间的推移,拥有混乱局面的总成本会不断增加。

  2. 从头开始重建遗留系统非常困难。重构和渐进式改进通常是更好的选择。

  3. 在混乱的代码库中,完成原本只需几个小时的任务可能需要几天甚至几周的时间。

  4. 花点时间快速前进。

  5. 干净的代码只做好一件事。糟糕的代码则试图做太多事情。

  6. 干净的代码是经过充分测试的。

  7. 阅读精心编写的代码时,每个函数几乎都能按照您的预期完成。

  8. 如果您不同意具有数十年经验的人所教授的原则,那么您最好在忽视它之前至少考虑一下他们的观点。

  9. 阅读代码的次数远远多于编写代码的次数。

  10. 更易于阅读的代码更容易更改。

  11. 让代码库比你发现它时更好(童子军规则)。


第二章:有意义的名字

  1. 谨慎选择变量名称。

  2. 选择好的名字很难。

  3. 变量或函数的名称应该告诉您它是什么以及如何使用它。

  4. 避免使用单字符变量名,除非是i循环中的计数器变量等常用名称。

  5. 避免在变量名中使用缩写。

  6. 变量名应该是可发音的,以便您可以谈论它们并大声说出来。

  7. 使用易于搜索的变量名。

  8. 类和对象应该具有名词名称。

  9. 方法和函数的名称应为动词或动词-名词对。


第三章:函数

  1. 功能应该很小。

  2. 函数应该做一件事。

  3. 函数应该有描述性的名称。(重复第二章)

  4. 将 if/else 或 switch 语句主体中的代码提取到明确命名的函数中。

  5. 限制函数接受的参数数量。

  6. 如果一个函数需要很多配置参数,请考虑将它们组合成一个配置选项变量。

  7. 函数应该是纯粹的,这意味着它们没有副作用并且不会修改其输入参数。

  8. 函数应该是命令或查询,但不能同时是两者(命令查询分离)。

  9. 抛出错误和异常而不是返回错误代码。

  10. 将重复的代码提取到明确命名的函数中(不要重复自己)。

  11. 单元测试使重构变得更容易。


第四章:评论

  1. 注释可能会撒谎。它们可能一开始就是错误的,也可能原本准确,但随着相关代码的变化而变得过时。

  2. 使用注释来描述为什么某些内容以这种方式编写,而不是解释发生了什么

  3. 通常可以通过使用明确命名的变量并将代码段提取到明确命名的函数中来避免注释。

  4. 为 TODO 注释添加一致前缀,方便搜索。请定期重新查看并清理 TODO 注释。

  5. 不要仅仅为了使用而使用 Javadocs。描述方法功能、方法参数以及方法返回结果的注释,往好了说是冗余,往坏了说是误导。

  6. 评论应包含读者所需的所有相关信息和背景信息。撰写评论时切勿偷懒或含糊其辞。

  7. 由于版本控制和 git blame,日志评论和文件作者评论是不必要的。

  8. 不要注释掉无效代码。直接删除就行。如果你认为以后会用到这些代码,版本控制就是为此而生的。


第五章:格式化

  1. 作为一个团队,选择一套代码格式化规则,并始终如一地应用这些规则。你们同意什么规则并不重要,但你们必须达成一致。

  2. 使用自动化代码格式化程序和代码检查工具。不要依赖人工手动捕捉和纠正每个格式错误。这效率低下、效率低下,而且在代码审查过程中浪费时间。

  3. 在代码中添加垂直空格,在视觉上分隔相关的代码块。各组代码块之间只需添加一个新行即可。

  4. 小文件比大文件更易于阅读、理解和导航。

  5. 变量的声明应该靠近使用的位置。对于小型函数,通常位于函数顶部。

  6. 即使对于短函数或 if 语句,仍要正确格式化它们,而不是将它们写在一行上。


第六章:对象和数据结构

  1. 对象的实现细节应该隐藏在对象接口的背后。通过为对象的使用者提供接口,可以更轻松地重构实现细节,而不会造成重大变更。抽象使重构更容易。

  2. 任何给定的代码片段都不应该了解它正在处理的对象的内部情况。

  3. 当使用对象时,您应该要求它执行命令或查询,而不是询问其内部情况。


第七章:错误处理

  1. 错误处理不应该掩盖模块中其余的代码。

  2. 抛出错误和异常,而不是返回错误代码。(重复第三章)

  3. 编写强制错误的测试,以确保您的代码处理的不仅仅是快乐路径。

  4. 错误消息应该提供信息,提供收到错误消息的人需要的所有上下文,以便有效地进行故障排除。

  5. 将第三方 API 包装在一个薄的抽象层中,使得将来更容易将一个库替换为另一个库。

  6. 将第三方 API 包装在一个薄的抽象层中,可以更轻松地在测试期间模拟库。

  7. 使用特殊情况模式或空对象模式来处理异常行为,例如某些数据不存在时。


第 8 章:界限

  1. 第三方库允许您外包各种问题,从而帮助您更快地交付产品。

  2. 编写测试以确保您对任何给定的第三方库的使用正常运行。

  3. 使用适配器模式来弥合第三方库的 API 和您希望的 API 之间的差距。

  4. 将第三方 API 包装在一个薄的抽象层中,可以方便将来更换库。(重复第七章)

  5. 将第三方 API 包装在一个薄的抽象层中,可以更轻松地在测试期间模拟库。(重复第七章)

  6. 避免让你的应用程序过多地了解任何给定的第三方库的细节。

  7. 依赖你能控制的事物比依赖你不能控制的事物要好。


第 9 章:单元测试

  1. 测试代码应该保持与生产代码一样干净(除了少数例外,通常涉及内存或效率)。

  2. 随着生产代码的变化,测试代码也会变化。

  3. 测试有助于保持生产代码的灵活性和可维护性。

  4. 测试使您可以自信地进行重构,而不必担心在不知不觉中破坏事物,从而实现改变。

  5. 使用 Arrange-Act-Assert 模式(也称为 Build-Operate-Check、Setup-Exercise-Verify 或 Given-When-Then)来构建您的测试。

  6. 使用特定领域的函数使测试更易于编写和更易于阅读。

  7. 每次测试评估一个概念。

  8. 测试应该很快。

  9. 测试应该是独立的。

  10. 测试应该是可重复的。

  11. 测试应该是自我验证的。

  12. 测试应该及时编写,可以在生产代码编写之前或之后不久进行,而不是几个月后。

  13. 如果你让你的测试腐烂,你的代码也会腐烂。


第 10 章:课程

  1. 班级规模应较小。

  2. 类应该只负责一件事,并且应该只有一个改变的原因(单一职责原则)。

  3. 如果您无法为某个类想出一个明确的名称,那么它可能太大了。

  4. 代码运行起来后,你的工作还没有完成。下一步是重构和清理代码。

  5. 在您的应用程序中使用许多小类而不是少数大类可以减少开发人员在执行任何给定任务时需要了解的信息量。

  6. 拥有良好的测试套件可以让您在将大类分解为小类时充满信心地进行重构。

  7. 类应该对扩展开放,但对修改关闭(开放封闭原则)。

  8. 接口和抽象类提供了使测试更容易的接缝。


第 11 章:系统

  1. 使用依赖注入可以让开发人员灵活地将任何具有匹配接口的对象传递给另一个类。

  2. 使用依赖注入在您的应用中创建对象接缝以使测试更容易。

  3. 软件系统并不像必须预先设计好的建筑物,而更像是随着时间的推移而发展壮大的城市,以适应当前的需求。

  4. 将决策推迟到最后一刻。

  5. 使用特定领域的语言,以便领域专家和开发人员使用相同的术语。

  6. 不要让你的系统过于复杂。使用最简单的方法就行。


第十二章:出现

  1. 不可测试的系统是不可验证的,不可验证的系统永远不应该被部署。

  2. 编写测试可以带来更好的设计,因为易于测试的代码通常使用依赖注入、接口和抽象。

  3. 良好的测试套件可以消除您在重构过程中破坏应用程序的担忧。

  4. 代码中的重复会产生更多风险,因为代码中需要更改的地方更多,并且代码中隐藏错误的地方也更多。

  5. 理解你正在写的代码很容易,因为你已经深入地理解了它。而其他人想要快速达到同样的理解水平却不那么容易。

  6. 软件项目的大部分成本用于长期维护。

  7. 测试可以作为应用程序应该如何(以及确实如何)运行的活生生的文档。

  8. 不要在代码运行后就立即放弃。花些时间让它更简洁、更容易理解。

  9. 在不久的将来,下一个阅读你代码的人很可能就是你。写一些易于理解的代码,对未来的自己好一点。

  10. 抵制教条,拥抱实用主义。

  11. 想要真正精通软件工程,需要几十年的时间。你可以向身边的专家学习,并学习常用的设计模式,从而加快学习进程。


第十三章:并发

  1. 编写并发代码很难。

  2. 随机错误和难以重现的问题通常是并发问题。

  3. 测试并不能保证您的应用程序中没有错误,但它确实可以最大限度地降低风险。

  4. 了解常见的并发问题及其可能的解决方案。


第14章 逐次炼化

  1. 干净的代码通常一开始并不干净。你先写了一个脏兮兮的解决方案,然后重构它,让它变得更干净。

  2. 一旦代码“运行”了就停止工作是错误的。在代码运行之后,花点时间让它变得更好。

  3. 混乱逐渐形成。

  4. 如果您发现自己陷入了添加功能太困难或花费太长时间的困境,请停止编写功能并开始重构。

  5. 进行渐进式更改通常比从头开始重建更好。

  6. 使用测试驱动开发(TDD)进行大量非常小的更改。

  7. 良好的软件设计涉及代码中的关注点分离以及将代码拆分为更小的模块、类和文件。

  8. 弄乱之后立即清理比事后清理要容易得多。


第 15 章:JUnit 内部原理

  1. 负面变量名称或条件比正面变量名称或条件稍微难以理解。

  2. 重构是一个充满反复尝试的迭代过程。

  3. 让代码比你发现时更好一点(童子军规则)。(重复第一章)


第 16 章:重构 SerialDate

  1. 代码审查和对我们代码的批评是我们变得更好的方式,我们应该欢迎它们。

  2. 首先让它发挥作用,然后让它正确。

  3. 并非每一行代码都值得测试。


第 17 章:气味和启发式方法

  1. 干净的代码不是一套规则,而是一套推动工作质量的价值体系。

[在本章中,Bob大叔列出了另外66个他提出的代码异味和启发式方法,其中很多在本书的其余部分已经介绍过了。在这里重现它们本质上就是复制粘贴每个条目的标题,所以我没有这样做。相反,我鼓励你读读这本书!]


结论

让我们回到开始的地方:罗伯特·C·马丁 (Robert C. Martin) 的《代码整洁之道》是有史以来最受推荐的编程书籍。

这是有充分理由的。

文章来源:https://dev.to/thawkin3/in-defense-of-clean-code-100-pieces-of-timeless-advice-from-uncle-bob-5flk
PREV
来自 Tech Lead 的经验教训:角色、职责和建议
NEXT
我希望我从未学过编程