书籍详情

大规模C++软件开发 卷1:过程与架构

大规模C++软件开发 卷1:过程与架构

作者:[美]约翰·拉科斯(John Lakos)

出版社:人民邮电出版社

出版时间:2023-08-01

ISBN:9787115609779

定价:¥149.80

购买这本书可以去
内容简介
  本书通过具体示例演示大规模C++开发的基本设计设想,为各种规模的项目奠定基础,并演示成功进行大规模实际开发所需的过程、方法、技术和工具。通过阅读本书,读者可以逐步改变自己的设计和开发方法。本书旨在使用软件从业人员熟悉的C++构件来解决现实问题,同时确定(并激发)现代C++替代方案。作者利用超过30年的构建大规模、关键任务的企业系统的实践经验,展示了如何创建和增长软件资本。本书专为有经验的C++软件开发者和系统设计师编写,从事大型软件开发工作的架构师或项目负责人等也可以通过阅读本书解决实际工作中的问题。
作者简介
  约翰·拉科斯(John Lakos),《大规模C++程序设计》(Large-Scale C++ Software Design)一书的作者,任职于彭博有限合伙企业,担任高级架构师,同时是全球C++软件开发顾问。2001年他成立了彭博的BDE工作组,按照他的基于组件的方法论、过程和架构来开发细粒度的、可复用的C++软件。他是ACCU、C++Now、CppCon和Meeting C++等业内技术大会的常客,经常发表技术演讲。他自2006年开始任C++标准委员会的投票成员,新一代C++的成型有他的一份功劳,包括C++11的值语义、C++17的PMR分配器和C++20的模块。他于1996年出版的《大规模C++程序设计》一书是用C++做大型系统设计的开山之作,至今还是这个领域的常用参考书。
目录
第0章 动机 1
0.1 目标:进度更快、产品更好、预算更低 1
0.2 应用软件与库软件 3
0.3 协作式软件与可复用软件 8
0.4 层次化可复用软件 12
0.5 易延展软件与稳定软件 16
0.6 物理设计的关键作用 24
0.7 物理形式统一的软件:组件 25
0.8 对层次化复用的量化:一个类比 32
0.9 软件资本 51
0.10 增大投入 57
0.11 保持警觉 62
0.12 小结 65

第 1章 编译器、连接器和组件 70
1.1 知识就是力量:细节决定成败 70
1.1.1 “Hello World!” 70
1.1.2 创建C++程序 71
1.1.3 头文件的作用 72
1.2 C++程序的编译和连接 73
1.2.1 构建流程:编译器和连接器的使用 73
1.2.2 目标文件(.o)的经典原子性 77
1.2.3 .o文件中的节和弱符号 79
1.2.4 静态库 79
1.2.5 “单例”注册表的例子 81
1.2.6 库间依赖 84
1.2.7 连接顺序和构建时行为 87
1.2.8 连接顺序和运行时行为 87
1.2.9 共享(动态连接)库 88
1.3 声明、定义和连结 88
1.3.1 声明与定义 88
1.3.2 (逻辑的)连结与(物理的)连接 92
1.3.3 需要了解连接工具 92
1.3.4 物理“连结”的另一种定义:绑结 92
1.3.5 连接器运作的更多细节 93
1.3.6 对一些需要全程序范围内地址唯一的实体的介绍 94
1.3.7 客户编译器需要看到定义的源代码的构件 96
1.3.8 声明并不一定要带上定义才能起作用 97
1.3.9 客户编译器通常需要看到类定义 97
1.3.10 客户编译器必须看到定义的源代码的其他实体 98
1.3.11 枚举具有外连结,但又会怎样 98
1.3.12 内联函数略有特殊 99
1.3.13 函数模板和类模板 99
1.3.14 函数模板和显式特化 100
1.3.15 类模板及其偏特化 104
1.3.16 extern模板 106
1.3.17 用工具来理解单一定义规则和绑结 108
1.3.18 命名空间 108
1.3.19 对const实体默认连结的阐释 109
1.3.20 本节小结 109
1.4 头文件 111
1.5 包含指令和包含保护符 118
1.5.1 包含指令 118
1.5.2 内置的包含保护符 119
1.5.3 外置的包含保护符(已废弃) 121
1.6 从.h/.cpp文件对到组件 123
1.6.1 组件特性1 123
1.6.2 组件特性2 125
1.6.3 组件特性3 126
1.7 符号和术语 128
1.7.1 概要 128
1.7.2 Is-A逻辑关系 130
1.7.3 Uses-In-The-Interface逻辑关系 130
1.7.4 Uses-In-The-Implementation逻辑关系 131
1.7.5 Uses-In-Name-Only逻辑关系和协议类 133
1.7.6 In-Structure-Only(ISO)协作式逻辑关系 135
1.7.7 受约束模板和接口继承的相似之处 136
1.7.8 受约束模板和接口继承的不同之处 137
1.7.9 3种“继承型”关系各有所长 138
1.7.10 给模板的类型约束编写注释 139
1.7.11 本节小结 140
1.8 Depends-On关系 141
1.9 隐含依赖 144
1.10 层级编号 149
1.11 抽取实际的依赖 151
组件特性4 152
1.12 小结 153

第 2章 打包和设计规则 159
2.1 观全貌 159
2.2 物理聚合 161
2.2.1 物理聚合的一般定义 161
2.2.2 物理聚合谱的小端 162
2.2.3 物理聚合谱的大端 162
2.2.4 聚合的概念原子性 163
2.2.5 聚合依赖的广义定义 163
2.2.6 架构显著性 164
2.2.7 一般发布单元的架构显著性 164
2.2.8 发布单元中具有架构显著性的部分 164
2.2.9 发布单元的什么部分不是架构显著的 164
2.2.10 组件“自然地”具有架构显著性 164
2.2.11 组件必须是一对.h/.cpp文件吗 165
2.2.12 何时不宜写成一对.h/.cpp文件 165
2.2.13 对.cpp文件的划分仅是组织上的改变 165
2.2.14 实体清单和可容许依赖 165
2.2.15 对可容许依赖的包络的表达需求 167
2.2.16 物理层次需平衡得当 167
2.2.17 不仅要层次化,而且要讲究平衡 168
2.2.18 物理聚合超过3级即算过多 169
2.2.19 即使是大型系统,3级也已足够 170
2.2.20 发布单元总有2级或3级的物理聚合 171
2.2.21 平衡得当的3级聚合就已足够 171
2.2.22 发布单元应该是最为架构显著的 171
2.2.23 架构显著的名称必须唯一 171
2.2.24 不要出现循环物理依赖 172
2.2.25 本节小结 173
2.3 逻辑连贯和物理连贯 173
2.4 逻辑名称衔接和物理名称衔接 175
2.4.1 过去对命名空间污染的应对措施 175
2.4.2 名称务必唯一,衔接的命名有益于人 175
2.4.3 既不衔接又不有助记忆的命名荒谬至极 176
2.4.4 需要相互衔接的名称 177
2.4.5 过去/现在对包的定义 177
2.4.6 使用点就应足够敲定位置 177
2.4.7 专有软件须有企业级命名空间 182
2.4.8 逻辑构件署名应锚定于其组件 182
2.4.9 在包级命名空间的作用域中只有类、结构体和自由运算符 183
2.4.10 包的前缀命名不仅仅是编程风格 189
2.4.11 包前缀即其所在包组名 192
2.4.12 using指令和声明往往是坏主意 193
2.4.13 本节小结 196
2.5 组件源代码的组织 196
2.6 组件设计规则 202
2.7 组件私有类和附属组件 219
2.7.1 组件私有类 220
2.7.2 有几种实现方案可待选择 220
2.7.3 下划线的约定用法 220
2.7.4 使用组件私有类的经典案例 224
2.7.5 附属组件 227
2.7.6 本节小结 229
2.8 包 229
2.8.1 用包来分解子系统 229
2.8.2 包间循环是不好的 234
2.8.3 布置、作用域和规模是首要考量 235
2.8.4 包前缀的唯一性对沟通大有裨益 236
2.8.5 本节小结 238
2.9 包组 238
2.9.1 物理聚合的第三层级 238
2.9.2 在部署时对包组的组织 245
2.9.3 在实践中如何使用包组 245
2.9.4 去中心化的(自治的)包的创建 248
2.9.5 本节小结 249
2.10 包和包组的命名 249
2.10.1 平铺直叙的包名不一定好 249
2.10.2 包组的名称 250
2.10.3 包的名称 250
2.10.4 本节小结 252
2.11 子包 252
2.12 遗留软件、开源软件和第三方软件 254
2.13 应用 255
2.14 层次化可测试性的需求 258
2.14.1 将本书的方法论运用于细粒度的单元测试中 258
2.14.2 本节安排(还有卷2及特别是卷3的引子) 258
2.14.3 测试要能层次化地推进 258
2.14.4 测试时的局部组件依赖的相对导入 263
2.14.5 可容许的跨包的测试驱动程序依赖 266
2.14.6 尽量减少测试驱动程序对外部环境的依赖 268
2.14.7 坚持统一(独立)的测试驱动程序调用接口 269
2.14.8 本节小结 270
2.15 从开发到部署 271
2.15.1 不应在软件的灵活部署方面让步 271
2.15.2 .h和.o文件名的唯一性非常关键 271
2.15.3 在开发过程中软件组织会有所变化 271
2.15.4 在全公司范围内让名称保持唯一有助于重构 272
2.15.5 在构建过程中软件组织都可能有所变化 272
2.15.6 即使在正常情况下部署中仍需要灵活性 272
2.15.7 让定制化部署成为可能是灵活性之价值的重要体现 273
2.15.8 头文件中风格化呈现的灵活性 273
2.15.9 库的部署方式不应架构显著 273
2.15.10 出于工程原因对已部署的软件进行划分 274
2.15.11 出于业务原因对已部署的软件进行划分 275
2.15.12 本节小结 276
2.16 元数据 276
2.16.1 元数据即“法令” 277
2.16.2 元数据的类型 277
2.16.3 元数据的呈现 281
2.16.4 本节小结 282
2.17 小结 283

第3章 物理设计和分解 290
3.1 从物理的角度思考 290
3.1.1 纯经典的(逻辑的)软件设计是幼稚的 291
3.1.2 组件充当细粒度的模块 291
3.1.3 软件的设计空间是有方向性的 291
3.1.4 软件有其绝对位置 292
3.1.5 并置与否的准则应该看本质,不应流于表面 293
3.1.6 不规整的非初等功能搜寻十分麻烦 293
3.1.7 包的作用域是一项重要的设计考量 293
3.1.8 禁止循环物理依赖带来的一些限制 295
3.1.9 对友元的约束有意排除了某些逻辑设计 297
3.1.10 一个有正当理由要求包装的 案例 297
3.1.11 本节小结 302
3.2 避免糟糕的物理模块化 303
3.2.1 有很多糟糕的模块化准则,语法是其中之一 303
3.2.2 将用途广泛的软件分解并加入库中非常重要 303
3.2.3 迫于压力未能维持应用/库的模块化 303
3.2.4 可复用组件的持续降级至关重要 304
3.2.5 对应用开发者而言,物理依赖不是实现细节 305
3.2.6 迭代器有助于减少初等功能的开发量 309
3.2.7 既要最小也要初等:实用结构体 309
3.2.8 总结性示例:封装型多边形类接口 309
3.2.9 语义与语法作为模块化准则 322
3.2.10 本节小结 323
3.3 逻辑相近的事物在物理上应分组在一起 324
3.3.1 类并置的4个明确准则 324
3.3.2 组件之上的并置 327
3.3.3 何时让辅助类供其组件私用 327
3.3.4 模板特化的并置 329
3.3.5 附属组件的使用 329
3.3.6 将紧密的相互协作并置于单个发布单元中 330
3.3.7 计算天数的示例 330
3.3.8 最后的示例:单线程引用计数型函子 336
3.3.9 本节小结 344
3.4 避免循环的连接时依赖 345
3.5 层级划分技术 351
3.5.1 经典层级划分技术 351
3.5.2 升级 352
3.5.3 降级 357
3.5.4 不透明指针 359
3.5.5 哑数据 365
3.5.6 冗余 367
3.5.7 回调 371
3.5.8 管理器类 389
3.5.9 分解 391
3.5.10 升级封装 392
3.5.11 本节小结 407
3.6 避免过度的连接时依赖 408
3.6.1 起初分解妥当的日期类会随时间退化 408
3.6.2 将工作日功能添加到日期类中(坏主意) 414
3.6.3 提供一个物理上整块式的平台适配器(坏主意) 415
3.6.4 本节小结 418
3.7 横展架构与分层架构 418
3.7.1 另一个与建筑业的类比 419
3.7.2 (经典的)分层架构 419
3.7.3 对纯组合式设计加以改进 421
3.7.4 最小化累积组件依赖度 421
3.7.5 基于继承的横展架构 424
3.7.6 横展架构与分层架构的测试 427
3.7.7 本节小结 427
3.8 避免不当的连接时依赖 428
3.8.1 不当的物理依赖 428
3.8.2 在单一技术上“押注”(坏主意) 431
3.8.3 本节小结 436
3.9 确保物理互操作性 436
3.9.1 妨碍层次化的复用是坏主意 436
3.9.2 领域特定的条件编译是坏主意 437
3.9.3 在库组件中的应用特定的依赖是坏主意 439
3.9.4 约束并排型复用是坏主意 440
3.9.5 防止故意的滥用不是目的 441
3.9.6 让组件侵占全局资源是坏主意 441
3.9.7 隐藏头文件来实现逻辑封装是坏主意 441
3.9.8 可复用库中存在对不可移植软件的依赖是坏主意 443
3.9.9 将潜在可复用软件隐藏起来是坏主意 446
3.9.10 本节小结 447
3.10 避免不必要的编译时依赖 447
3.10.1 封装不能杜绝编译时耦合 447
3.10.2 共享枚举和编译时耦合 449
3.10.3 C++中的编译时耦合比C语言中更为普遍 451
3.10.4 避免不必要的编译时耦合 451
3.10.5 避免编译时耦合的益处及真实示例 454
3.10.6 本节小结 458
3.11 架构隔离技术 458
3.11.1 封装与隔离的形式化定义 459
3.11.2 用组件的概念阐释封装与 隔离 459
3.11.3 整体隔离与部分隔离 460
3.11.4 架构显著的整体隔离技术 461
3.11.5 纯抽象接口(协议)类 461
3.11.6 完全隔离型具体包装器 组件 467
3.11.7 过程接口 471
3.11.8 隔离和动态加载库 484
3.11.9 面向服务的架构 484
3.11.10 本节小结 485
3.12 用组件进行设计 485
3.12.1 原先陈述的“需求” 485
3.12.2 实际(外延)的需求 486
3.12.3 用C++类型表示日期值 487
3.12.4 确定今天的日期值 494
3.12.5 确定给定日期值是否为工作日 496
3.12.6 解析和格式化功能 508
3.12.7 值的传输与持久化 510
3.12.8 债券计息日数惯例 510
3.12.9 日期数学 510
3.12.10 日期和日历实用件 513
3.12.11 充实分解透彻的实现 515
3.12.12 本节小结 527
3.13 小结 529
结论 536
参考文献 538
猜您喜欢

读书导航