重点摘要
1. 编写可读且可维护的干净代码
唯一有效的代码质量衡量标准:每分钟的WTF次数
可读性至关重要。 干净的代码应易于其他开发人员理解。它应简单、优雅且无杂乱。努力编写无需大量注释即可清晰表达意图的代码。使用有意义的变量和函数名称,保持函数小而专注,并逻辑地组织代码。
可维护性使演变成为可能。 难以更改的代码会成为负担。设计灵活且模块化的代码,以适应不断变化的需求。遵循DRY(Don't Repeat Yourself)和SOLID等原则,创建松耦合、高内聚的系统。无情地重构以在不改变行为的情况下改进代码结构。
干净的代码有回报。 虽然编写干净代码需要更多的前期努力,但从长远来看,它节省了大量时间和麻烦。干净的代码更易于调试、扩展和维护。它使开发人员工作更高效,并减少在更改过程中引入错误的风险。将编写干净代码作为开发实践的核心部分。
2. 遵循有意义的命名约定
变量、函数或类的名称应回答所有重要问题。它应告诉你为什么存在、做什么以及如何使用。
使用意图揭示的名称。 选择清晰传达变量、函数和类目的和行为的名称。避免单字母名称或难解的缩写。使用易于搜索的可发音名称。例如:
- 不好:d(以天为单位的经过时间)
- 好:elapsedTimeInDays
保持一致和精确。 在整个代码库中使用一致的命名约定。精确以避免歧义——例如,使用有意义的区分,如getActiveAccounts()和getActiveAccountInfo()。避免添加无价值噪音的编码或前缀。类名应为名词,方法名应为动词。
名称长度应匹配范围。 对于范围较大的变量和函数,使用较长且更具描述性的名称。对于小的局部范围,短名称是可以接受的。名称的长度应与其使用范围成比例。优化可读性和在使用上下文中的理解。
3. 保持函数小而专注
函数应做一件事。它们应做好这件事。它们应只做这件事。
小即美。 函数应小——通常5-10行长。它们应能在一个屏幕上显示并立即被理解。将代码提取到命名良好的辅助函数中,而不是编写长而复杂的函数。小函数更易于理解、测试和维护。
做好一件事。 每个函数应有一个明确的目的。如果一个函数在做多件事,将这些提取到单独的函数中。函数做得太多的迹象包括:
- 多层抽象
- 多个部分或代码块
- 大量参数
保持一个抽象层次。 函数内的语句应都在同一抽象层次。不要将高层逻辑与低层细节混合。将低层操作提取到单独的函数中。这通过保持函数专注和概念简单来提高可读性。
4. 练习正确的格式和组织
代码格式是关于沟通,而沟通是专业开发人员的首要任务。
一致的格式很重要。 在整个代码中使用一致的缩进、换行和间距。这提高了可读性并减少了认知负担。与团队达成格式标准并使用自动化工具来强制执行。关键的格式指南包括:
- 正确的缩进
- 一致的括号位置
- 合理的换行
- 适当的空白
逻辑地组织代码。 将相关代码分组在一起,分隔不相关的代码。使用空行在逻辑部分之间创建“段落”分隔。将相关函数放在一起。使文件专注于单一概念或组件。适当时将大文件拆分为更小、更专注的文件。
遵循标准约定。 遵循语言和社区的标准约定。这使你的代码对其他开发人员更熟悉和可访问。例如,在Java中:
- 类名使用PascalCase
- 方法名使用camelCase
- 常量使用ALL_CAPS
5. 管理依赖并避免重复
重复可能是软件中所有邪恶的根源。
消除重复。 重复的代码是抽象的错失机会。当你看到重复时,将公共代码提取到可重用的函数或类中。这通过集中逻辑并减少不一致更改的风险来提高可维护性。需要注意的重复类型包括:
- 相同的代码块
- 略有变化的相似算法
- 重复的switch/case或if/else链
仔细管理依赖。 最小化模块之间的依赖以减少耦合。使用依赖注入和控制反转使代码更模块化和可测试。遵循依赖倒置原则——依赖抽象而不是具体实现。这使你的代码更灵活且更易于更改。
使用最少知识原则。 模块不应了解其操作对象的内部。这减少了模块之间的耦合。例如,使用Demeter法则——方法应只调用:
- 它自己的对象
- 作为参数传递的对象
- 它创建的对象
- 它的直接组件对象
6. 优雅地处理错误
错误处理很重要,但如果它掩盖了逻辑,那就是错误的。
使用异常而不是错误代码。 异常更简洁,不会混乱代码的主逻辑。它们允许将错误处理与正常路径分开。使用异常时:
- 创建有信息的错误消息
- 提供上下文
- 根据调用者的需要定义异常类
不要返回null。 返回null会导致空指针异常,并使代码充满null检查。相反:
- 对于列表,返回空集合而不是null
- 使用Null对象模式
- 在Java中使用Optional或在函数式语言中使用Maybe
首先编写try-catch-finally语句。 在编写可能抛出异常的代码时,先编写try-catch-finally。这有助于定义调用代码的范围和期望。它确保资源在错误场景中也能正确管理和释放。
7. 编写全面的单元测试
测试代码与生产代码同样重要。
遵循TDD的三条法则。 测试驱动开发(TDD)提高代码质量和设计:
- 在编写任何生产代码之前编写失败的测试
- 仅编写足以展示失败的测试
- 仅编写足以通过测试的生产代码
保持测试干净和可维护。 对测试代码应用与生产代码相同的质量标准。定期重构和改进测试代码。结构良好的测试可作为文档,并使生产代码的无畏重构成为可能。
目标是全面的测试覆盖。 编写覆盖边界情况、边界条件和错误场景的测试——不仅仅是正常路径。使用代码覆盖工具识别测试覆盖的空白。记住,100%的覆盖率并不保证无错误代码,但它提供了重构和更改的信心。
8. 持续重构代码
离开营地时比你找到它时更干净。
机会主义地重构。 每当你处理一段代码时,改进代码结构。遵循童子军规则:离开代码时比你找到它时更好。小的、渐进的改进随着时间的推移累积起来,防止代码腐烂。常见的重构技术包括:
- 提取方法或类
- 为清晰重命名
- 简化复杂的条件
- 消除重复
通过测试安全地重构。 在重构之前始终有一套可靠的测试。进行小的、渐进的更改并频繁运行测试。这使你有信心你的更改不会破坏现有功能。使用自动化重构工具(如果有)以减少引入错误的风险。
在交付价值和重构之间取得平衡。 虽然持续重构很重要,但不要让它阻碍进展。目标是“足够好”而不是完美。将重构工作集中在最有问题或经常更改的代码区域。向利益相关者传达重构的价值,以确保对持续代码改进的支持。
9. 应用面向对象和函数式编程原则
对象将其数据隐藏在抽象后面,并暴露操作该数据的函数。数据结构暴露其数据且没有有意义的函数。
明智地使用面向对象原则。 应用封装、继承和多态等原则,创建灵活、模块化的设计。遵循SOLID原则:
- 单一职责原则
- 开放-封闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖倒置原则
利用函数式编程概念。 即使在面向对象语言中,函数式编程技术也能带来更干净的代码:
- 无副作用的纯函数
- 不可变数据
- 高阶函数
- 函数组合
为问题选择正确的方法。 面向对象和函数式范式各有优缺点。需要建模具有行为的复杂领域时使用面向对象设计。用于数据转换和处理管道时使用函数式方法。许多现代语言支持混合方法,允许你为系统的每个部分使用最佳工具。
10. 仔细考虑并发性
并发是一种解耦策略。它帮助我们解耦完成什么与何时完成。
理解并发挑战。 并发编程引入了复杂性和潜在的微妙错误。常见问题包括:
- 竞争条件
- 死锁
- 丢失信号
- 内存可见性问题
分离并发问题。 将并发相关的代码与其他代码分开。这使其更易于推理和测试。使用Executors、Futures和Actors等抽象来管理并发,而不是直接处理线程。
偏爱不可变性和纯函数。 不可变对象和纯函数本质上是线程安全的。它们通过避免共享可变状态消除了许多并发问题。当需要可变状态时,使用适当的同步技术,并考虑使用原子变量或并发集合。
最后更新日期:
评论
《代码整洁之道》因其关于编写可读、可维护代码的原则而广受好评。读者们赞赏书中关于命名、函数和测试的实用建议。尽管书中对Java的关注和一些过于严格的指导方针常被批评,许多人仍认为这是开发者必读的书籍,尽管有些经验丰富的程序员觉得它的实用性较低。书中的案例研究和重构示例受到一些人的赞扬,但也有一些人认为这些内容过于繁琐。总体而言,评论者一致认为这本书在代码质量方面提供了宝贵的见解,即使并非所有建议都普遍适用。