前言

阅读本书之前,有些人可能会问:又一本关于C++设计模式的书吗?为什么还要出这样的书,而且为什么是现在?关于设计模式的知识,难道不是早就已经写的差不多了吗?

之所以再写一本关于设计模式的书,有以下几个原因。

这是一本非常纯粹的 C++ 书籍 —— 不仅仅是关于“C++实现设计模式”,而是关于“在 C++ 语境下”进行设计模式的书,其侧重点与其他书籍不同。C++ 拥有面向对象语言的能力,所有经典的面向对象设计模式,例如工厂和策略模式,也都可以在 C++ 中实现,本书也会介绍其中一部分。然而,只有利用 C++ 的泛型编程能力时,才能真正发挥这门语言的威力。

设计模式通常指的是反复出现的设计难题,以及被广泛接受的解决方案 —— 在这类模式中,问题和解决方案同等重要。当新的工具出现时,新的解决方案也随之成为可能。随着时间推移,社区会逐渐采纳其中某些方案为整体上最有利的实践,于是便催生出设计模式的新变体 —— 面对相同的挑战,却有了不同的首选解决方案。而能力的扩展也开辟了新的疆域 —— 当手握新工具时,新的设计挑战也随之涌现。

有些人可能会期待看到经典著作《设计模式:可复用面向对象软件的基础》的现代版续篇。但本书可能会让你失望了,并且我认为现在也并非推出这类书籍的恰当时机。“四人组”的那本书在当时是新颖甚至具有革命性的,它将“设计模式”的语言引入了广泛的编程社区。它还建立了设计模式的清晰分类体系,但这一部分却经不起时间的考验:随着模式词汇不断扩展,会发现有些模式难以归入某个特定类别。此外,我们还将设计模式的概念扩展到了面向对象编程之外,发现其中一些模式虽与面向对象的模式有相似之处,但另一些则是完全崭新且迥异的。并且,在C++或其他能力显著不同的编程语言中,解决相同需求的模式可能呈现出完全不同的形态。尽管最初的模式分类仍然有用,但如今例外情况已多于典型示例,而模式的版图也已过于多样化,以至于新的分类体系都会显得过于人为和牵强。或许随着时间的推移,当进一步发展这门技艺时,从扩展后的模式全景图中会涌现出新的趋势,但这一天还尚未到来。

这本书的目标没那么宏大,但却非常务实,聚焦于那些在模式的至少一方面,C++ 能够带来实质性贡献的设计模式。一方面,探讨了诸如访问者(Visitor)模式这样的例子,在这些模式中,C++ 的泛型编程能力能够提供更优的解决方案 —— 而这种更优的方案正是得益于语言从 C++11 到 C++17 演进过程中所引入的新特性。另一方面,泛型编程本质上仍然是编程(只不过程序的执行发生在编译期);编程离不开设计,而设计中存在许多共通的挑战,这些挑战与传统编程中的问题并没有太大区别。因此,许多传统设计模式在泛型编程领域都有对应的“孪生兄弟”,或是“近亲”,本书的重点也主要集中在这类模式上。一个典型的例子是策略(Strategy)模式,它在泛型编程社区中更广为人知的名字是策略(Policy)模式。此外,随着新特性的加入,语言本身能够为新问题提供解决方案,或为老问题提供新解法,而这些最终都会演变为新的设计模式。C++ 协程(coroutines)就是一个例证,它会在本书的最后一章中登场亮相。

最后,像 C++ 这样复杂的语言必然存在一些独特的特性,这些特性常常会引发特定于 C++ 的问题,而这些问题往往也有通用或标准的解决方法。尽管这些 C++ 特有的惯用法(idioms)未必都称得上是“设计模式”,但本书也同样会介绍这些内容。

关于第二版的几点改动说明:首先,新增了一章关于并发编程模式的内容。所有示例均已更新,会在合理的情况下使用 C++17 或 C++20 的特性,但不会无谓地堆砌新语法。许多模式、惯用法及其使用示例都结合了近年来 C++ 编程社区的最新发展和进步进行了修订和完善。

总而言之,本书的编写主要有以下三个原因:

适读人群

本书面向希望从社区智慧中学习的 C++ 开发者 —— 学习那些广泛认可、用于解决常见设计问题的优秀方案。换句话说,这本书为开发者提供了一种途径,使其能够从他人的错误中汲取经验教训。

这不是一本教授 C++ 基础的入门书籍;其目标读者主要是那些对语言的工具和语法已有相当了解,并更希望深入理解这些工具“如何使用”以及“为何如此使用”的开发者。然而,对于那些希望进一步学习 C++、并希望学习过程能由具体且实用的示例,来引导的开发者来说,本书也同样具有参考价值(对于这类读者,建议手边同时备有一本 C++ 参考手册)。最后,对于那些不仅想知道 C++17 或 C++20 中新增了哪些特性,更想了解这些新特性“能用来做什么”的开发者而言,本书也希望能带来启发。

本书内容

第1章, 继承与多态导论,概述了C++的面向对象特性。本章并非作为C++面向对象编程的全面参考,而是着重介绍对后续章节至关重要的内容。

第2章, 类模板与函数模板,概述了C++的泛型编程功能 —— 包括类模板、函数模板和Lambda表达式。本章讲解了模板的实例化与特化,以及模板函数的参数推导和重载解析,为后续章节中更复杂地使用模板做好准备。

第3章, 内存与所有权,介绍了在C++中表达不同内存所有权关系的现代惯用法。这些方法是一系列约定或惯用法 —— 编译器并不会强制执行这些规则,但如果开发者们使用共同的惯用语汇,将有助于彼此更清晰地理解和交流代码意图。

第4章, 交换操作:从简单到微妙,探讨了C++中最基本的操作之一 —— 两个值的交换(swap)。这一操作与C++的其他特性有着出人意料的复杂交互,本章将对此进行详细讨论。

第5章, 深入理解RAII,详细探讨了C++的一个核心概念 —— 资源管理,并介绍了可能是C++中最著名的惯用法:RAII(资源获取即初始化)。这是C++管理资源的标准方法。

第6章, 理解类型擦除,深入剖析了一种在C++中早已存在、但自C++11引入以来日益流行和重要的技术。类型擦除允许开发者编写不显式提及某些类型的抽象程序。

第7章, SFINAE、概念与重载解析管理, 讨论了SFINAE这一C++惯用法:一方面,在C++模板的使用中至关重要,通常在后台透明地发挥作用;另一方面,当需要有意使用时,又要求对C++模板有非常深入和细致的理解。

第8章, 奇异递归模板模式,描述了一种巧妙的基于模板的模式,结合了面向对象编程的优点与模板的灵活性。本章解释了该模式的原理,介绍如何正确使用它来解决实际问题,并有助于读者在后续章节中识别这一模式。

第9章, 命名参数、方法链与建造者模式,介绍了一种在C++中调用函数的非传统技术 —— 使用命名参数而非位置参数。这是每个C++程序中都隐式使用的惯用法,但有意地显式使用则需要深入思考。

第10章, 局部缓冲区优化, 是本书中唯一纯粹面向性能的章节。性能和效率是影响语言本身每一个设计决策的关键考量 —— 语言中的每一项特性在纳入标准之前都会从效率角度进行审查,专门用一章来介绍一种用于提升C++程序性能的常见惯用法也没什么问题。

第11章, 作用域守卫模式, 介绍了一个古老的C++模式,该模式在C++的最新版本中变得面目一新。本章将教你一种在C++中轻松编写异常安全(或更广义地说,错误安全)代码的模式。。

第12章, 友元工厂模式,介绍了另一个在现代C++中焕发新生的古老模式。该模式用于生成与模板关联的函数,例如为模板生成的每种类型创建相应的算术运算符。

第13章, 虚构造函数与工厂模式,探讨了面向对象编程中的另一个经典模式 —— 工厂模式在C++中的应用。此外,本章还展示了如何实现类似于构造函数的多态行为,尽管C++的构造函数本身不能是虚函数。

第14章, 模板方法模式与非虚接口惯用法,描述了一个有趣的交叉模式:将经典的面向对象模式“模板方法”与极具C++特色的“非虚接口”惯用法相结合。两者共同构成了描述C++中虚函数最佳实践的模式。

第15章, 基于策略的设计,介绍了C++设计模式中的一颗明珠 —— 策略模式(在泛型编程中更常被称为Policy模式)。这是一种在编译时应用的模式,即作为泛型编程模式而非面向对象模式来使用。

第16章, 适配器与装饰器模式,讨论了两种应用广泛且密切相关的模式在C++中的实现。本章探讨了这些模式在面向对象设计,以及泛型程序中的应用。

第17章, 访问者模式与多重分发,为本书的经典面向对象编程模式画上句号,聚焦于长期受欢迎的访问者模式。本章首先解释该模式本身,然后重点介绍现代C++如何使访问者模式的实现变得更简单、更健壮且更少出错。

第18章, 并发编程模式,是本书的全新章节。尽管在C++11提供“官方”并发工具之前,C++早已用于编写并发程序,但大量特定于问题的解决方案使得识别通用模式变得困难。本章介绍了已成为C++并发软件设计基本构建模块的几种模式。

如何阅读

要运行本书中的示例,需要一台运行 Windows、Linux 或 macOS 的计算机(C++ 程序甚至可以在 Raspberry Pi 这样小巧的设备上构建)。还需要一个支持 C++ 语言标准至 C++20 的现代 C++ 编译器,例如 GCC、Clang、Visual Studio 或其他兼容编译器(大多数示例仅需 C++14,部分依赖 C++17 特性,少数需要最新的 C++20 支持)。此外,还需要具备 GitHub 和 Git 的基本知识,以便下载包含示例的项目。

如果正在使用本书的数字版本,我们建议您自己输入代码或通过GitHub存储库访问代码(下一节提供链接),将避免复制和粘贴代码。

下载源码

可以从 GitHub 下载本书的示例代码文件 https://github.com/PacktPublishing/Hands-On-Design-Patterns-with-CPP-Second-Edition/。如果代码有更新,将在 GitHub 存储库中更新。

还有丰富的书籍和视频目录中的其他代码包,可在 https://github.com/PacktPublishing/ 上找到。快来看看吧!