大規模 C++ 軟件開發 捲1:過程與架構 Large-Scale C++ Volume I: Process and Architecture (Addison-Wesley Professional Computing Series)

[美]約翰·拉科斯(John Lakos)

  • 大規模 C++ 軟件開發 捲1:過程與架構-preview-1
  • 大規模 C++ 軟件開發 捲1:過程與架構-preview-2
大規模 C++ 軟件開發 捲1:過程與架構-preview-1

買這商品的人也買了...

商品描述

本書通過具體示例演示大規模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