代码整洁之道 (Robert C. Martin)

在软件开发过程中,代码并不是写完提交之后就结束了。一个项目真正的生命周期,往往伴随着持续的修改、扩展和维护。需求会不断变化,新的功能会不断加入,原本简单的模块可能逐渐变得复杂。如果代码经过良好的设计和整理,随着时间推移,系统会慢慢演变成难以理解、难以修改,甚至无人敢触碰“遗留代码”。
正如软件工程领域常说的一句话:
代码的第一读者是人,第二读者才是机器。
计算机只关心代码是否能够正确运行,而开发人员需要面对代码背后的意图、结构以及未来变化的可能性。一段能够运行的代码,并不一定是一段优秀的代码。
由Robert C. Martin(人称Uncle Bob)编写的《代码整洁之道》正是围绕这一问题展开讨论。本书从变量命名、函数设计、类的职责划分,到错误处理、测试、架构设计等多个方面,系统总结了如何编写更加清晰、可维护的软件代码。
书中提出:
“Clean code always looks like it was written by someone who cares.”
优秀的代码不仅仅是满足需求,更体现了开发者对软件质量的关注。在实际开发中,我们经常会遇到这样的代码:
- 一个几百行甚至上千行的函数
- 一个承担几十种职责的巨大类
- 大量重复逻辑散落在项目各处
- 修改一个小需求,却需要影响多个模块
- 新成员接手项目,需要花费大量时间理解代码
这些问题并不是某一天突然出现的,而是在一次次“先实现功能,以后再优化”的过程中逐渐累积形成的。《代码整洁之道》提供了一套思考方式:
- 如何通过命名让代码表达真实意图
- 如何设计短小、专注的函数
- 如何让类保持单一职责
- 如何降低模块之间的耦合
- 如何通过测试保护代码质量
- 如何让软件具备持续演进的能力
学习整洁代码,并不是为了追求代码形式上的“漂亮”,而是为了降低软件长期维护的成本,让系统能够面对未来的不确定变化。在本文中,我将结合书中的核心思想,并结合实际工程开发中的案例,对《代码整洁之道》进行深入学习和总结,希望能够探索:
什么样的代码才是真正适合长期维护的软件代码?
第1章 整洁代码
1.5 我们是作者
读和写花费时间的比例超过10:1。
要想干的快,要想早点做完,要想轻松些代码,先让代码易读吧。
1.6 童子军军规
“让营地比你来时干净。”
第2章 有意义的命名
2.2 名副其实
变量、函数或类的名称应该已经答复了所有的大问题。它该告诉你,它为什么存在,它做什么事,应该怎么用。如果名称需要注释来补充,那就不算是名副其实。看到名字,不看实现,也应该大致知道它的职责。
2.3 避免误导
不要用那些会让人产生错误理解的名称。一个好的名字不仅要表达正确的信息,还不能暗示错误的信息。名字传递的信息必须和实际行为一致。
例如:
|
|
变量名user看起来像一个用户集合,但实际上是一个字典,更好的:
|
|
2.4 做有意义的区分
如果两个名字表示不同的东西,那么它们必须体现出真正的区别;不要为了“看起来不同”而添加没有意义的区分。也就是说:名字之间的差异必须传达信息,否则就是噪声。
很多代码为了避免重名,会随便加一些后缀:
|
|
这些名字虽然不同,但是没有任何信息,数字只是为了满足编译器。应该:
|
|
2.5 使用读的出来的名称
名字应该能够被正常读出来,这样人们才能在交流、讨论和思考代码时自然使用它。
2.6 使用可搜索的名称
单字母名称和数字常量有个问题,那就是很难在一大篇文字中找出来。
若变量或常量可能在代码中多处使用,则应赋其以便于搜索的名称。
2.7 避免使用编码
把类型信息或者作用域信息编码到名字中,只会额外增加理解这些名字的成本。
经过编码的名字通常难以发音,也容易输入错误。
匈牙利命名法、成员前缀(例如m_前缀标明成员变量)、接口和实现(例如以I开头标明是接口)不必盲目遵守。
注:这里的编码不是指字符编码(UTF-8、ASCII),而是指把额外的信息塞进名字里面的一种命名约定。例如:
|
|
这里str表示string,i表示int,b表示bool,作者认为:
|
|
更好!
2.8 避免思维映射
名称应当尽可能直接表达真实含义,而不是让读者在脑海中进行额外转换。
例如:
|
|
如果看到:
|
|
读者大脑可能需要寻找上下文d是表示days?还是distance?
2.9 类名
类名和对象应该名词或名词短语,不应当是动词。例如Customer、WikiPage、Account和AddressParser。避免使用Manager、Processor、Data或Info这样的类名。
2.10 方法名
方法名应当是动词或动词短语,如postPayment、deletePage或save。
2.11 别扮可爱
不要为了追求有趣、炫技或者个性化,而使用难以理解的命名。
代码不是展示幽默感的地方,命名应该清晰、准确、专业,而不是俏皮。
2.12 每个概念对应一个词
给每个抽象概念选一个词,并且一以贯之。对于那些会用到你代码的程序员,一以贯之的命名法简直是天降福音。
2.13 别用双关语
避免将同一个单词用于不同目的。同一术语用于不同概念,基本上就是双关语了。
代码作者应当尽力写出易于理解的代码。我们想把代码写得让别人能一目尽览,而不必殚精竭虑地研究。
|
|
这里add表示添加一个元素到集合。但是:
|
|
如果这里add表示两个产品合并,那么就属于是“双关”了。使用combine就清晰多了。
2.14 使用解决方案领域名称
优先使用计算机科学、软件工程、算法、设计模式等领域中的专业术语来命名,而不是使用业务之外的模糊名称。简单来说,程序员应该用程序员能理解的词命名。
2.15 使用源自所涉问题领域的名称
当没有合适的计算机科学术语可以表达时,应该使用业务领域中的专业术语来命名。
解决方案领域(Solution Domain)名称:软件工程中的术语。
问题领域(Problem Domain)名称:业务、行业中的术语。
2.16 添加有意义的语境
有时候,一个变量、函数或类的名字本身并不能表达完整含义,需要通过增加有意义的上下文来帮助读者理解。换句话说:不要让读者依靠猜测上下文来理解名字,而应该通过命名主动提供必要的信息。
例如:
|
|
单独看id没有意义,到底是用户ID,产品ID,还是订单ID?需要上下文!如果是userId = 123或productOrderId = 123就更加明确。
2.17 不要添加没用的语境
只要短名称足够清楚,就要比长名称好,别给名称添加不必要的语境。
例如:userId已经足够明确,如果写customerUserIdentificationNumber就有点过度了。
第3章 函数
第4章 注释
第5章 格式
第6章 对象和数据结构
第7章 错误处理
第8章 边界
第9章 单元测试
第10章 类
第11章 系统
第12章 迭代
第13章 并发编程
第14章 逐步改进
第15章 JUnit内幕
第16章 重构SerialDate
第17章 味道与启发
附录A 并发编程II
相关内容
- 重构 改善既有代码的艺术 第2版 (Martin Fowler)
- 编写可读代码的艺术 (Dustin Boswell / Trevor Foucher)
- C#高效编程 改进C#代码的50个行之有效的办法 第2版 (Bill Wagner)
- 聪明的投资者 第4版 (Benjamin Graham)
- 从零搭建TypeScript工程
- 架构整洁之道 (Robert C. Martin)
- Effective Python 第2版 (Brett Slatkin)
- 流畅的Python 第2版 (Luciano Ramalho)
- Vue.js设计与实现 (霍春阳)
- 计算机网络:自顶向下方法 第8版 (James F·Kurose / Keith W. Ross)
- 深入理解计算机系统 第3版 (Randal E. Bryant / David O'Hallaron)
- Orange'S 一个操作系统的实现 (于渊)
- UNIX环境高级编程 第3版 (W.Richard Stevens / Stephen A.Rago)
- TCP/IP网络编程 (尹圣雨)
- 学习JavaScript数据结构与算法 第3版 (Loiane Groner)
- 深入解析CSS (Keith J. Grant)
- 经理学原理 微观经济学分册 第8版 (格里高里·曼昆)
- 从零开始学机械 (门田和雄)
- 小强升职记 升级版 (邹小强)
- 机器视觉算法与应用 第2版 (Carsten Steger)
支付宝
微信