本章主軸#
本章是一段「重新定義基本概念」的章節。作者退一步反思物件、封裝、繼承這些 OO 基礎詞彙,並提出一套從設計模式中提煉出來的新觀點。讀者學完本章,會以全新的方式看待物件導向,後續每個模式都會更好理解。
本章還會把設計模式與敏捷(agile)開發實踐連結起來。乍看兩者似乎對立,但作者指出兩者所追求的程式碼特質高度相關。
對「物件」的新觀點#
傳統觀點:資料 + 方法#
把物件視為「聰明的資料」——加幾個方法上去就成了物件。這只是從實作層次的觀察,太狹隘。
新觀點:擁有責任的東西#
從概念層次重新定義:
物件是擁有責任的實體,這些責任定義它的行為。
好處:
- 開發可以分兩步走:先做不擔心細節的設計、再做實作
- 透過公開介面與物件溝通,不必知道內部如何完成工作
- 可以放心委派
把焦點從「實作」轉到「動機」是設計模式中的一貫主題——透過介面隱藏實作,可以與使用方完全解耦。
對「封裝」的新觀點#
傳統觀點:資料隱藏#
作者問學生「誰聽過封裝就是資料隱藏?」幾乎全部舉手。
作者的雨傘比喻#
「我有一把雨傘——它能容納幾個人、可以移動、有立體聲、能調溫、不用我自己推也能跑 ⋯⋯。其實大家叫它『汽車』,但對我而言它就是擋雨的工具。」
把汽車只定義為雨傘,正如把封裝只定義為資料隱藏——是一種限制思維的「定義陷阱」。
新觀點:任何形式的隱藏#
封裝可以隱藏的東西包括:
- 資料
- 實作(implementation)
- 衍生類別(type encapsulation)
- 設計細節
- 實例化規則
把 Circle 包住 XXCircle,就是隱藏了實作;用抽象類別 Shape 對外,client 看不到 Circle、Square,就是隱藏了型別。GoF 提到「封裝」時通常指的就是這種型別封裝。
對「繼承」的新觀點#
傳統用法:為了重用而特化#
例如先有 Pentagon,再衍生 PentagonSpecialBorder。看似乾淨,但問題是:
- 凝聚變弱:
Pentagon開始要管邊框、內部花紋等不相關的事 - 難以重用:邊框邏輯被綁在
Pentagon家族中,其他形狀拿不到 - 不夠擴展:再加一個變動軸(例如陰影)就會類別爆炸
新用法:用繼承來「分類行為」#
繼承不是用來特化,而是用來把行為相同的事物收為同類。每個 ConcreteClass 是「相同概念」的不同實作。
找出變動點並封裝#
這是 GoF 的核心心法:
思考你的設計中哪些東西會變,並將會變的概念封裝起來。
要把這句話聽得進去,必須先接受「封裝是任何形式的隱藏」這個新觀點。實作上:
- 用抽象類別或介面把「變動的概念」隱藏起來
- 使用方持有這個抽象類別的參考
- 透過抽象層分離雙方,達到鬆耦合
變動的兩種放置方式:資料 vs 行為#
動物例子#
需求:
- 不同動物腳數不同(資料變動)
- 不同動物移動方式不同(行為變動)
腳數很容易:放個資料成員就好。但是移動:
- 用變數加 switch?多個變動軸交織後會很糟(飲食、地形、混合行為)
- 用繼承?特化爆炸
- 用內含物件:
Animal內持有AnimalMovement物件 → 可獨立替換

Figure 8-3: Animal 持有 AnimalMovement 物件——以聚合容納行為變化

Figure 8-4: 把內含物件以資料成員的形式呈現
把行為包成物件,與把資料放成成員,本質上是同一件事——只不過內含的是「會變動的行為」而非「會變動的數值」。
在真正物件導向的語言裡,連數值(int、double)也都是物件,只是被特殊語法包裝。
共通與差異分析(CVA)#
來自 Jim Coplien 的方法:
- Commonality analysis:找出家族成員的共通結構——「他們在哪裡相同」
- Variability analysis:找出家族成員的變化方式——「他們在哪裡不同」
對映關係:
| 分析 | 對應的 OO 元素 | 對應的視角 |
|---|---|---|
| 共通分析 | 抽象類別(abstract class) | 概念視角 |
| 規格設計 | 介面(interface) | 規格視角 |
| 差異分析 | 具象類別(concrete class) | 實作視角 |

Figure 8-5: 共通與差異分析、三種視角、抽象類別與其衍生類別之間的關係
用 CVA 找物件,比「在需求中找名詞」更可靠。它能避免設計出又高又脆的繼承樹。
設計過程化為兩步#
| 設計什麼 | 應問自己 |
|---|---|
| 抽象類別(共通) | 要怎樣的介面才能涵蓋這個概念的所有責任? |
| 衍生類別(差異) | 給定這個介面,這個變化要怎麼實作? |
敏捷開發強調的程式碼特質#
設計模式與敏捷看似不同,但都共同追求:
- 無冗餘(no redundancy)——「One rule, one place」原則
- 可讀性(readability)——Ron Jeffries 的「program by intention」
- 可測試性(testability)——Kent Beck 的測試先行(TDD)
這些特質與「強凝聚、鬆耦合、封裝」高度相關:
- 凝聚 → 易測試
- 解耦 → 易測試
- 冗餘 → 測試成本變高
- 可讀 → 測試一目了然
- 封裝 → 測試對外無依賴
「Program by intention」——遇到要實作的功能,先當作它已經存在,給一個清楚命名的方法呼叫並繼續寫;之後再回頭實作這個方法。
這幾乎就是 GoF 的「Design to interfaces」翻成另一句話。
本章要記住的事#
- 物件 = 「擁有責任的東西」,而非「資料 + 方法」
- 封裝是任何形式的「隱藏」,不只資料
- 繼承的最佳用途是「分類同類行為」,不是「特化以重用」
- 把行為變動放進物件,與把資料變動放進成員,是同一個思路
- 共通與差異分析是比「找名詞」更可靠的設計切入點
- 設計模式與敏捷追求同一組程式碼特質