引言
如今,技术圈流行的术语和“最佳实践”层出不穷,但我们需要关注更基础的问题:开发人员在阅读代码时的困惑程度。
困惑会浪费时间和金钱,而困惑的根源是高认知负荷。这并不是某种高深抽象的概念,而是人类认知的基本限制。
什么是认知负荷?
认知负荷是指开发人员为完成一项任务所需要的思考量。
阅读代码时,开发人员需要记住变量的值、控制逻辑、调用顺序等内容。普通人平均只能在工作记忆中处理四个这样的信息块。一旦超过这个阈值,理解能力就会显著下降。
举例说明:
当我们被要求修复一个完全陌生的项目时,得知这个项目由一位“超级聪明”的开发者编写,采用了许多复杂的架构、流行的库和前沿技术——这意味着我们要面对高认知负荷。
结论:
我们应该尽量减少项目中的认知负荷。
认知负荷的类型
- 内在认知负荷
由任务本身的复杂性决定,无法减少。这是软件开发的本质问题。 - 外在认知负荷
由信息的呈现方式引起,与任务本身无关,例如开发者的个人习惯。这个负荷是可以大幅减少的,也是我们关注的重点。
常见场景及优化方法
1. 复杂条件语句
if (val > someConstant
&& (condition2 || condition3)
&& (condition4 && !condition5)) {
...
}
优化方法:引入中间变量
isValid = val > someConstant;
isAllowed = condition2 || condition3;
isSecure = condition4 && !condition5;
if (isValid && isAllowed && isSecure) {
...
}
通过使用具有描述性名称的中间变量,减少开发人员的记忆负担。
2. 继承层级过深
AdminController → UserController → GuestController → BaseController
需要更改 AdminController 的功能,但功能散落在多个父类中,还可能影响到子类 SuperuserController,分析代码时认知负荷急剧增加。
优化方法:优先使用组合代替继承
避免深层继承关系,减少对代码理解的成本。
3. 过多的小方法、类或模块
虽然“方法应小于 15 行”或“类应保持小巧”这样的准则听起来不错,但如果模块过于浅显,反而会增加认知负荷。
案例对比:
- 项目 A:80 个浅层模块,难以理清模块之间的交互。
- 项目 B:7 个深层模块,接口简单但功能强大。
结论:
模块的深度比数量更重要。隐藏复杂性,让接口简单明了。
4. 滥用语言特性
新语言特性虽然令人兴奋,但过多使用会导致代码复杂,后续维护时需要重新理解当初的决策。
优化方法:限制选择,避免滥用非必要特性。
语言特性应彼此独立,不增加理解负担。
5. 过度抽象的分层架构
虽然分层架构表面上隐藏了复杂性,但实际上可能增加理解的间接成本。每次追踪错误都需要跳跃多个层级,增加认知负荷。
优化方法:减少不必要的抽象,仅在确实需要扩展点时添加。
抽象的层级并不是免费的,切忌为架构而架构。
领域驱动设计(DDD)常见误区
DDD 的核心是帮助团队理解问题领域,而不是解决方案本身。过度关注具体实现(如文件夹结构、服务、仓库)会增加额外的认知负荷。
优化建议:
专注于问题领域的沟通,避免让未来的开发者在你独特的实现逻辑中迷失。
如何降低认知负荷?
- 引入初级开发者参与代码审查
他们更容易发现复杂和令人困惑的代码部分。 - 衡量团队困惑程度
如果新成员需要长时间才能理解项目,说明代码需要优化。 - 降低初学者的门槛
保持代码清晰简洁,让新人能够在加入后短时间内开始贡献。
结论
如果你感觉这篇文章阅读起来很费力,那正是我们讨论的高认知负荷的体现。
我们应尽量减少一切非必要的认知负荷,让代码更易于理解,让开发更高效!