「整合」(Integration)是指將各個獨立開發的軟體元件合併為一個完整系統的過程。整合的議題與建構順序(Construction Sequence)密不可分——你無法整合尚未建構完成的東西。本章從整合的角度,同時探討這兩個主題。
29.1 整合方式的重要性#

Figure 29-1: The football stadium add-on at the University of Washington collapsed because it wasn't strong enough to support itself during construction. It likely would have been strong enough when completed, but it was constructed in the wrong order—an integration error.
就像建築工程一樣,軟體在施工過程中的每個階段都必須足夠穩固,而不僅僅是在完工之後。如果以錯誤的順序建構與整合軟體,程式碼會更難撰寫、更難測試、更難除錯。整合雖常被視為測試活動的一部分,但其複雜度足以使它被獨立看待。
仔細規劃整合方式可帶來的好處:
- 更容易診斷缺陷
- 更少的缺陷與更少的鷹架程式碼(Scaffolding)
- 更早產出可運作的產品
- 更短的開發時程與更可靠的進度估算
- 改善客戶關係與團隊士氣
- 更高的程式碼品質
29.2 整合頻率——階段式整合還是增量整合?#
階段式整合 (Phased Integration)#
階段式整合又稱大爆炸整合(Big Bang Integration),步驟如下:
- 各自設計、編碼、測試每個類別(單元開發)
- 一次將所有類別組合成完整系統(系統整合)
- 測試與除錯整個系統(有人戲稱為「系統瓦解」)
階段式整合的核心問題:所有類別第一次組合在一起時,大量問題同時浮現。由於所有類別都是嫌疑犯,錯誤難以定位,而且多個問題之間會互相遮蔽與交互作用。除了極小型(tiny)專案外,幾乎不建議使用。
增量整合 (Incremental Integration)#
增量整合採取逐一加入的策略:
- 先開發一個小型、可運作的系統骨架(Skeleton),並徹底測試
- 設計、編碼、測試一個新類別
- 將新類別整合到骨架中,測試組合體;確認可運作後,才加入下一個類別
增量整合相較於階段式整合的優勢:
| 優勢 | 說明 |
|---|---|
| 錯誤容易定位 | 新問題幾乎一定與剛加入的類別有關。研究顯示,有一個專案 39% 的錯誤屬於模組間的介面錯誤,增量整合能大幅縮小偵錯範圍 |
| 系統更早可運作 | 團隊能更早看到進展,改善士氣與客戶信心 |
| 更充分的單元測試 | 每個類別在整合過程中就被作為系統的一部分反覆測試 |
| 可縮短開發時程 | 設計與實作可部分並行 |
flowchart LR
subgraph phased["階段式整合"]
direction LR
P1["個別開發<br/>各類別"] --> P2["一次全部<br/>組合"] --> P3["測試與<br/>除錯"]
end
subgraph incremental["增量整合"]
direction LR
I1["建構系統<br/>骨架"] --> I2["測試骨架"] --> I3["加入一個<br/>類別"] --> I4["測試組合體"] --> I5{"確認<br/>可運作?"}
I5 -- "是,繼續加入下一個" --> I3
end
style P1 fill:#f8d7da,stroke:#dc3545
style P2 fill:#f8d7da,stroke:#dc3545
style P3 fill:#f8d7da,stroke:#dc3545
style I1 fill:#d4edda,stroke:#28a745
style I2 fill:#d4edda,stroke:#28a745
style I3 fill:#d4edda,stroke:#28a745
style I4 fill:#d4edda,stroke:#28a745
style I5 fill:#d4edda,stroke:#28a745
style phased fill:#fff5f5,stroke:#dc3545
style incremental fill:#f0fff0,stroke:#28a74529.3 增量整合的策略#
增量整合有多種策略可供選擇,沒有一種在所有情況下都是最佳的。實務上,最好的做法是針對特定專案量身打造混合策略。
由上而下整合 (Top-Down Integration)#
從階層頂端的類別(如 main()、主視窗、應用程式控制迴圈)開始,使用虛設常式(Stub)代替尚未整合的低層類別,再逐步以真實類別取代虛設常式。
優點:
- 控制邏輯較早被測試,大型設計問題能快速暴露
- 若 UI 位於頂層,可儘早呈現可運作的介面
- 可在低層設計完成之前就開始整合高層類別
缺點:
- 困難的系統介面(通常在底層)留到最後才測試,低層問題可能向上冒泡
- 需要大量的虛設常式,而虛設常式本身也容易含有錯誤
- 嚴格的純由上而下整合幾乎不可能實行
一個實用的替代方案是垂直切片(Vertical-Slice)整合:從上到下逐一實作各功能區塊,而非嚴格地一層一層往下。
由下而上整合 (Bottom-Up Integration)#
從階層底部的類別開始,撰寫測試驅動程式(Test Driver)來驅動底層類別,隨著高層類別加入,逐步以真實類別取代驅動程式。
優點:
- 可較早測試可能棘手的系統介面
- 錯誤來源限縮於新整合的類別
缺點:
- 高層的概念設計問題留到最後才被發現,可能導致大量返工
- 需要在整合前完成整體設計,否則低層決策可能反過來主導高層設計,違反資訊隱藏原則
三明治整合 (Sandwich Integration)#
同時從頂層(高階商業物件)和底層(裝置介面、常用工具類別)開始整合,將中間層留到最後。
這種方式結合了由上而下與由下而上的優勢,能優先處理經常出問題的類別,並減少所需的鷹架程式碼。
風險導向整合 (Risk-Oriented Integration)#
又稱「困難部分優先」(Hard Part First)。先辨識每個類別的風險等級,把最具挑戰性的部分優先實作與整合:
- 頂層介面通常高風險
- 系統底層介面也常是高風險
- 中間層中,演算法不明確或效能目標嚴苛的類別亦為高風險
較簡單、低風險的部分留到後面。巧合的是,這種方式往往也像三明治整合一樣先做頂層和底層。
功能導向整合 (Feature-Oriented Integration)#
每次整合一個完整功能(Feature)——一個可識別的系統功能。通常先建構一個能支撐其他功能的骨架(如互動式選單系統),再將各功能掛上去。
三大優點:
- 幾乎不需要鷹架程式碼,因為每個功能都是自足的
- 每次新增功能都帶來可見的功能增量,可提早交付部分功能
- 與物件導向設計天然契合,物件往往能良好地對應到功能
點擊展開:功能導向整合的注意事項
- 功能之間越獨立,整合就越容易
- 各功能可共用低層程式庫,但中間層程式碼最好不要共用
- 純功能導向整合仍有困難,某些低層程式碼必須先行整合
T 型整合 (T-Shaped Integration)#
先選擇一個垂直切片進行早期開發與整合,這個切片應涵蓋系統從頭到尾的完整路徑,以驗證架構假設。垂直切片完成後,再橫向發展系統的整體廣度(如選單系統等框架)。
T 型整合常與風險導向或功能導向整合搭配使用。
整合策略總結:由下而上、由上而下、三明治、風險導向、功能導向、T 型——這些名稱看似即興發明,因為它們確實是。這些策略都是啟發式方法(Heuristic),而非機械性的演算法。你應該針對專案的具體需求,組合出自己的獨特策略。
mindmap
root(("增量整合策略"))
由上而下
從頂層類別開始
使用虛設常式
由下而上
從底層類別開始
使用測試驅動程式
三明治
同時從頂層與底層
中間層留到最後
風險導向
困難部分優先
低風險留到後面
功能導向
逐功能整合
幾乎不需鷹架程式碼
T 型
垂直切片驗證架構
橫向擴展系統廣度29.4 每日組建與冒煙測試#
每日組建與冒煙測試(Daily Build and Smoke Test)是一種強大的整合實踐:每天將所有檔案編譯、連結為可執行程式,並執行一輪簡單的測試來確認產品是否基本可運作。
核心效益#
| 效益 | 說明 |
|---|---|
| 降低品質風險 | 每天將系統帶回已知的良好狀態,不允許品質惡化到需要耗費大量時間修復的程度 |
| 容易診斷缺陷 | 如果第 17 天可運作、第 18 天壞了,問題就出在這兩次組建之間的變更 |
| 改善士氣 | 每天都能看到產品多運作了一些,保持團隊動力 |
| 讓隱藏工作浮現 | 避免未完成的整合工作累積到專案末期才爆發 |
實踐要點#
| 要點 | 說明 |
|---|---|
| 每天組建 | 將每日組建視為專案的心跳。若改為每週組建,一旦某次組建失敗,可能連續數週都沒有好的組建 |
| 自動化 | 組建與冒煙測試都必須自動化,否則無法持續維護 |
| 冒煙測試要與時俱進 | 隨著系統成長,測試也必須擴展,否則會產生虛假的品質信心 |
| 破壞組建必須嚴肅看待 | 修復壞掉的組建是最高優先事項。可設置輕鬆的懲罰機制來強化意識 |
| 開發者先自行測試 | 在提交程式碼前,先在個人環境中通過冒煙測試 |
| 設置暫存區 | 新程式碼先放入暫存區,確認不會破壞組建後再合併到主程式碼 |
| 在壓力下仍要堅持 | 壓力越大,開發者越容易走捷徑,此時每日組建的紀律更為重要 |
持續整合 (Continuous Integration)#
有些團隊主張持續整合,即每隔數小時就整合一次。然而對多數專案而言,每日整合已經足夠。讓程式碼在短時間內略微失去同步是有價值的,特別是在進行較大規模的變更時。每天一次的同步點,對多數團隊來說已是夠頻繁的交會點。
更多資源#
- Lakos, John. Large-Scale C++ Software Design(1996)——系統的「實體設計」(檔案、目錄、程式庫的階層結構)如何影響建構能力
- Myers, Glenford J. The Art of Software Testing(1979)——將整合視為測試活動的經典著作
- McConnell, Steve. Rapid Development(1996)——增量式開發模型與「為變更而設計」的詳細討論
- Boehm, Barry W. A Spiral Model of Software Development and Enhancement(1988)——以風險管理為核心的螺旋模型
- Beck, Kent. Extreme Programming Explained(2000)——增量式開發思想的現代精簡呈現
要點#
- 建構順序與整合方式會影響類別的設計、編碼與測試順序
- 精心規劃的整合順序能減少測試工作量並簡化除錯過程
- 增量整合有多種變體,除非專案規模極小,任何一種都優於階段式整合
- 最佳的整合策略通常是由上而下、由下而上、風險導向等多種方式的組合;T 型整合與垂直切片整合是兩種經常有效的做法
- 每日組建與冒煙測試能減少整合問題、改善開發者士氣,並提供有用的專案管理資訊