如何做和让别人做同样重要。爱德华多·纳穆尔
许多软件开发项目处于糟糕的状况,有些甚至可能处于严重的危机之中,这仍然是一个令人悲伤的现实。原因是多方面的。例如,一些项目因为糟糕的项目管理而受到影响。在其他项目中,条件和需求是不断变化的,但是过程不支持这种高动态的环境。
在一些项目中有纯粹的技术原因:他们的代码质量很差。这并不一定意味着代码不能正常工作。它的外部质量,由质量保证部门使用黑盒、用户或验收测试来测量,可能相当高。它可以毫无怨言的通过 QA,检测报告上说他们没发现什么问题。此外,软件的用户可能会感到满意和高兴,而且它的开发已经按时按预算完成了(…我知道这很少见)。一切似乎都很好…真的一切吗?
然而,这段代码的内部质量可能会很差,虽然它可能会正常工作。通常代码很难理解,维护和扩展起来也很糟糕。无数的软件单元,像类或函数,都非常大,有些有数千行代码。软件单元之间过多的依赖会导致不必要的副作用。该软件没有可感知的架构。它的结构似乎是随机产生的,一些开发人员谈论“历史上生长的软件”或“偶然的架构”类、函数、变量和常量都有糟糕而神秘的名字,代码中充斥着大量的注释:其中一些已经过时,只是描述了显而易见的事情,或者完全错误。开发人员害怕改变一些东西或扩展软件,因为他们知道软件是腐烂的和脆弱的,他们知道单元测试覆盖率很差,如果根本没有单元测试的话。“永远不要碰正在运行的系统”是在这类项目中经常听到的说法。一个新特性的实现不需要几天就可以部署;需要几周甚至几个月。
这种糟糕的软件通常被称为一团泥巴。1997 年,Brian Foote 和 Joseph W. Yoder 在第四届程序模式语言会议的一篇论文中首次使用了这个术语。Foote 和 Yoder 将这个大泥球描述为“……一个杂乱无章、杂乱无章、杂乱无章、用胶带和铁丝捆成的意大利面条式的丛林。”这样的软件系统是昂贵且浪费时间的维护噩梦,它们会使一个开发组织陷入困境!
刚才描述的病态现象可以在所有工业部门和领域的软件项目中找到。使用的编程语言并不重要。您会发现用 Java、PHP、C、C#、C++ 或任何其他或多或少流行的语言编写的大量 Muds。但是为什么会这样呢?
首先,有一种似乎像是自然规律的东西。就像任何其他封闭而复杂的系统一样,随着时间的推移,软件往往会变得凌乱不堪。这种现象被称为软件熵。这个术语是基于热力学第二定律。它指出,一个封闭系统的无序是无法减少的;只能保持不变或者增加。软件似乎就是这样运行的。每次添加新功能或更改某些内容时,代码都会变得更加混乱。也有许多影响因素可以传递软件熵,例如:
- 不切实际的项目时间表将增加压力,因此将迫使开发人员修补东西,并以糟糕和不专业的方式工作。
- 当今软件系统的巨大复杂性。
- 开发人员有不同的技能水平和经验。
- 全球分布的跨文化团队,加强沟通问题。
- 开发主要关注软件的功能方面(功能需求和系统用例),从而关注质量需求(也称为非功能需求),如性能效率、可维护性、可用性、可移植性、安全性等。被忽视,或者在最坏的情况下被完全遗忘。
- 不合适的开发环境和糟糕的工具。
- 管理层专注于赚快钱,不理解可持续软件开发的价值。
- 快速而肮脏的攻击和不符合设计的实现(也叫破窗)。
The Broken Window Theory
破窗理论是在美国犯罪研究中发展起来的。该理论指出,废弃建筑上的一扇被毁坏的窗户可能会引发整个街区的毁坏。破碎的窗户向环境发出了致命的信号:“你看,没人关心这栋楼!”这吸引了进一步的腐败、破坏和其他反社会行为。破窗理论已被用作若干刑事政策改革的基础,特别是零容忍战略的制定。
在软件开发中,这一理论被采纳并应用于代码质量。不符合软件设计的黑客和糟糕的实现被称为“破窗”如果这些糟糕的实现不被修复,更多的黑客可能会出现在他们的附近。因此,代码的破损开始了。
不要容忍代码中的“破窗”——修复它们!
然而,似乎特定的 C 和 C++ 项目更容易出错,比其他项目更容易陷入糟糕的状态。甚至万维网上也充斥着糟糕的、但显然非常快且高度优化的 C++ 代码示例,它们具有残酷的语法,完全忽略了良好设计和编写良好代码的基本原则。
其中一个原因可能是 C++ 是一种中间层次的多范例编程语言,也就是说,它包含了高级和低级语言特性。使用 C++ 你可以编写具有复杂用户界面的大型分布式商业软件系统,以及具有实时行为的小型嵌入式系统软件,这些软件与底层硬件紧密相关。多范例语言意味着你能够编写过程化的、函数式的或面向对象的程序,甚至是三种范例的混合。此外,C++ 允许模板元编程(TMP),这是一种编译器使用所谓的模板来生成临时源代码的技术,该临时源代码由编译器与其余源代码合并,然后进行编译。自从 ISO 标准 C++11 发布以来,甚至增加了更多的方式,例如,现在 lambda 表达式以非常优雅的方式支持带有匿名函数的函数式编程。由于这些不同的能力,C++ 以非常复杂、繁琐而著称。
坏软件的另一个原因可能是许多开发人员没有 IT 背景。如今,任何人都可以开始开发软件,无论她是否有大学学位或任何其他计算机科学学徒资格。绝大多数 C++ 开发人员都不是专家。特别是在汽车、铁路运输、航空航天、电气/电子或机械工程等技术领域,许多工程师在过去的几十年里在没有接受计算机科学教育的情况下从事编程工作。随着复杂性的增长和技术系统包含越来越多的软件,迫切需要程序员。现有劳动力满足了这一需求。电气工程师、数学家、物理学家以及许多严格意义上的非技术学科的人开始开发软件,并主要通过自学和简单动手来学习。他们已经尽了最大的努力。
基本上绝对没毛病。但是有时候仅仅知道工具和编程语言是不够的!软件开发不同于编程。这个世界充满了由未经适当培训的软件开发人员拼凑而成的软件。在抽象层次上,开发人员必须考虑许多事情来创建一个可持续的系统,例如,架构和设计。如何构建一个系统来实现特定的质量目标?这种面向对象的东西有什么用途,我如何高效地使用它?某个框架或者库的优缺点是什么?各种算法的区别是什么,为什么没有一种算法适合所有类似的问题?确定性有限自动机到底是什么,为什么它有助于应对复杂性?!
但是没有理由灰心丧气!对于一个软件的持续健康来说,真正重要的是有人关心它,干净的代码是关键!
一个主要的误解是将干净的代码与可以被称为“美丽的代码”的东西混淆了干净的代码没有美丽的理由。专业程序员不会因为写出漂亮的代码而获得报酬。他们被开发公司聘为创造客户价值的专家。
如果任何团队成员都能轻松理解和维护代码,那么代码就是干净的。
干净的代码是快速的基础。如果你的代码是干净的,测试覆盖率是好的,一个改变或者一个新的功能只需要几个小时或者几天——而不是几周或者几个月——直到它被实现、测试和部署。
干净的代码是可持续软件的基础,并保持软件开发项目长期运行,而不会积累大量的技术债务。开发人员必须积极地维护软件并确保它保持良好的状态,因为代码对于软件开发组织的生存至关重要。
干净的代码也是让你成为快乐的开发者的关键。它导致了一种没有压力的生活。如果你的代码是干净的,并且你对它感到满意,你可以在任何情况下保持冷静,即使是在一个紧张的项目截止日期前。
上面提到的所有观点都是正确的,但关键的一点是:干净的代码省钱!本质上是经济效率的问题。每年,开发组织都会因为他们的代码状态不佳而损失很多钱。
C 很容易搬起石头砸自己的脚。C++ 让它变得更难,但是当你这么做的时候,你会炸掉你的整条腿!——比雅尼·斯特劳斯特鲁普,比雅尼·斯特劳斯特鲁普的常见问题:你真的这么说了吗?
每一种编程语言都是一种工具,每一种都有它的优点和缺点。软件架构师工作的一个重要部分是选择完全适合项目的编程语言——或者现在的编程语言集。这是一个重要的架构决策,不应该根据直觉或个人偏好来做出。同样,像“在我们公司,我们用做任何事情”这样的原则可能不是一个好的指南。
作为一种多范例编程语言,C++ 是一个融合了不同思想和概念的熔炉。当涉及到操作系统、设备驱动程序、嵌入式系统、数据库管理系统、大型计算机游戏、3D 动画和计算机辅助设计、实时音频和视频处理、大数据管理以及许多其他性能关键型应用程序的开发时,该语言一直是一个极好的选择。在某些领域,C++ 是通用语言。拥有数十亿行代码的大型 C++ 代码库仍然存在并在使用中。
几年前,一个广泛传播的观点是 C++ 很难学习和使用。对于经常承担编写大型复杂程序任务的程序员来说,这种语言可能很复杂,令人望而生畏。由于这个原因,主要是解释型和托管型语言,比如 Java 或 C#,开始流行起来。这些语言的制造商的过度营销完成了剩下的部分。结果,托管语言在某些领域占据了主导地位,但本机编译语言在其他领域仍然占据主导地位。编程语言不是宗教。如果你不需要 C++ 的性能,但是 Java 可以让你的工作变得更简单,那就使用它吧。
有人说 C++ 目前正在复兴。有些人甚至谈到了一场革命。他们说今天的现代 C++ 已经不能与 20 世纪 90 年代早期的“历史 C++ 相提并论了。这一趋势的催化剂主要是 2011 年 9 月 C++ 标准 ISO/IEC 14882:2011 [ISO11]的出现,更好的说法是 C++11。
毫无疑问,C++11 带来了一些伟大的创新。看起来这个标准的发布已经启动了一些东西。在这本书出版的同时,C++ 标准化委员会已经完成了新 C++17 标准的工作,现在正处于 ISO 投票的最后阶段。此外,C++20 已经开始起步了。
目前,在本地开发领域发生了很多事情,尤其是在制造行业的公司中,因为软件已经成为技术系统最重要的增值因素。如今,C++ 的开发工具更加强大,并且有大量有用的库和框架可用。但是我不认为这整个发展是一场革命。我认为这是通常的进化。此外,编程语言必须不断改进和适应,以满足新的需求,C++98 和 C++03(主要是 C++98 上的一个错误修复版本)都有点过时了。
作为一名培训师和顾问,我有机会了解许多正在开发软件的公司。此外,我非常密切地观察开发人员的情况。我发现了一个缺口。
我的印象是,C++ 程序员被那些提倡软件工艺和干净代码开发的人忽略了。许多原则和实践在 Java 环境中以及在 web 或游戏开发的时髦世界中是相对众所周知的,但在 C++ 世界中似乎基本上是未知的。开创性的书籍,如安德鲁·亨特和戴维·托马斯的《务实的程序员》,或者罗伯特·c·马丁的《干净的代码》,通常都不为人所知。
这本书试图缩小这一差距,因为即使使用 C++,代码也可以写得很干净!如果你想自学如何编写干净的 C++,这本书是给你的。
这本书不是 C++ 入门!你应该已经熟悉这门语言的基本概念,以便有效地使用本书中的知识。如果你只是想从 C++ 开发开始,还没有语言的基础知识,你应该先学习它们,这可以通过其他书籍或者一个好的 C++ 入门培训来完成。
此外,这本书不包含任何深奥的黑客或组装。我知道用 C++ 可以做很多疯狂和令人兴奋的事情,但是这些通常不符合干净代码的精神,应该很少用于干净和现代的 C++ 程序。如果你真的对神秘的 C++ 指针健身操着迷,这本书不适合你。
对于本书中的一些代码示例,使用了标准 C++11 (ISO/IEC 14882:2011)、C++14 (ISO/IEC 14882:2014)以及一些最新的 C++17 的各种语言特性。如果您不熟悉这些功能,不要担心。我将借助边栏简要介绍其中一些。请注意,实际上并不是每个 C++ 编译器都完全支持所有新的语言特性。
除此之外,本书旨在帮助所有技能水平的 C++ 开发人员,并通过示例展示如何编写可理解的、灵活的、可维护的和高效的 C++ 代码。即使你是一个经验丰富的 C++ 开发人员,我认为这本书中的一些金块和数据点也会对你的工作有所帮助。提出的原则和实践可以应用于新的软件系统,有时称为绿地项目;以及历史悠久的遗留系统,这些系统通常被轻蔑地称为棕色地带项目。
本书使用了以下印刷惯例:
- 斜体用于介绍新的术语和名称。
- 粗体在段落中用于强调术语或重要陈述。
Monospaced font
在段落中用来指代程序元素,如类名、变量名或函数名、语句和 C++ 关键字。这种字体也用于显示命令行输入、网站地址(URL)、击键序列或程序产生的输出。
有时我会向你传递一些与周围内容无关的小信息,这些信息可以被认为是与内容无关的。这种部分被称为边栏。有时,我会使用侧边栏来呈现围绕主题的附加或对比讨论。示例:
This Header Contains the Title of a Sidebar
这是边栏中的文本。
另一种特殊用途的侧边栏用于注释、提示和警告。它们用来给你一些特殊的信息,提供一个有用的建议,或者警告你一些危险的应该避免的事情。示例:
Note
这是通知的正文。
代码示例和代码片段将与文本分开出现,语法突出显示(C++ 语言的关键字是粗体),并采用等宽字体。较长的代码段通常有标题。为了引用文本中代码示例的特定行,代码示例有时用行号来修饰。
01 class Clazz {
02 public:
03 Clazz();
04 virtual ∼Clazz();
05 void doSomething();
06
07 private:
08 int _attribute;
09
10 void function();
11 };
Listing 1-1.A line-numbered code sample
为了更好地关注代码的特定方面,不相关的部分有时会被隐藏起来,并用省略号(…)表示,如下例所示:
void Clazz::function() {
// ...
}
关于我在本书中使用的编码风格,只说几句话。
你可能会觉得我的编程风格与典型的 Java 代码非常相似,混合了 Kernighan 和 Ritchie (K&R)的风格。在我近 20 年的软件开发生涯中,甚至在我职业生涯的后期,除了 C++,我还学习了其他编程语言,例如 ANSI-C、Java、Delphi、Scala 和一些脚本语言。因此,我采用了自己的编程风格,这是一个不同影响的熔炉。
也许您不喜欢我的风格,您更喜欢 Linus Torvald 的内核风格、Allman 风格或任何其他流行的 C++ 编码标准。这当然是完全可以的。我喜欢我的风格,你喜欢你的。
本书附有配套网站: www.clean-cpp.com
。
该网站包括:
- 一个讨论论坛,读者可以与其他读者讨论特定的主题,当然也可以与作者讨论。
- 对本书中可能尚未涉及的其他主题的讨论。
- 这本书里所有数字的高分辨率版本。
本书中的大部分源代码示例以及其他有用的附加内容都可以在 GitHub 上找到,网址是:
T2https://github.com/clean-cpp
您可以通过以下命令使用 Git 来检查代码:
$> git clone
https://github.com/clean-cpp/book-samples.git
T3】
你可以进入 https://github.com/clean-cpp/book-samples
,点击“下载 zip”按钮,获得代码的. ZIP 存档。
本书中的一些插图是 UML 图。统一建模语言(UML)是一种标准化的图形语言,用于创建软件和其他系统的模型。在当前的 2.5 版本中,UML 提供了 14 种图表类型来完整地描述一个系统。
如果您不熟悉所有的图类型,也不用担心;我在本书中只使用了其中的几个。我不时地展示 UML 图,以提供对某些问题的快速概述,这些问题可能无法通过仅仅阅读代码来足够快地检测出来。在附录 A 中,您可以找到所用符号的简要概述。