當一個抽象只提供互補方法的其中一邊——例如有
initialize()卻沒dispose()、有add()卻沒remove()——就出現 Incomplete Abstraction 壞味道。
核心定義#
抽象的使能技巧之一是「讓抽象連貫且完整」。當互補或彼此關聯的方法只支援一半時:
- 客戶端被迫自行補實作,常常為此直接存取內部資料——封裝跟著被破壞。
- 一半的責任被推到別的地方實作,內聚降低,違反 SRP。
常見成因#
- 缺乏整體視角:開發者只專注於眼前需求,忽略一組方法是否被完整地一起提供。
- 不遵守語言/函式庫慣例:
- Java 的
HashMap依賴hashCode()與equals()一起被覆寫;只覆寫一個會導致檢索異常。 - C++ 的「rule of three」:要嘛全部定義 default constructor、copy constructor、assignment operator、destructor(多型情境下需 virtual destructor),要嘛全都不定義。
- Java 的
範例#
範例 1:JDK 的 ButtonModel#
javax.swing.ButtonModel 提供了 setGroup(),文件說明「識別 button 所屬的群組」,但沒有對稱的 getGroup()。
範例 2:Abstract Factory 樣式#
Abstract Factory pattern ↗ 的標準描述只定義「建立物件的職責」,未明確包含「刪除物件」。在不支援垃圾回收的語言中,套用此樣式而忽略物件刪除,就會產生 Incomplete Abstraction。

Figure 3.9: Abstract Factory 樣式的類別圖
軼事:架構師假設「自己懂就好」#
一個智慧裝置設定工具發布前出現嚴重記憶體洩漏。專家追查發現:架構師的設計文件只列出「建立」方法,預期開發者會「自己理解」要對應建立刪除方法。開發者卻被要求嚴格遵守設計文件,因而完全沒處理刪除——洩漏因而蔓延整個程式碼。
教訓:
- 設計文件必須明確——不能依賴「對方應該懂」。
- 團隊文化要鼓勵開發者對架構提出回饋;Agile 倡導的「集體擁有設計」可以避免這類盲點。
建議重構#
為缺失的對稱/互補方法直接補上。但若是已公開的 API,要小心衝擊既有客戶端。
書中常見對稱方法配對舉例:
| 動作 | 對應 |
|---|---|
| open | close |
| start | stop |
| add | remove |
| push | pop(Stack) |
| source | sink(Stream) |
| initialize | dispose |
| enable | disable |
| show | hide |
- 範例 1:理想上應在
ButtonModel介面補上getGroup();但因為 JDK 是公開 API,貿然在介面新增方法會破壞所有實作者。最終 JDK 1.3 在子類別DefaultButtonModel中補上getGroup(),是個折衷。

Figure 3.10: JDK 1.3 中 ButtonModel 介面的重構
- 範例 2:在
AbstractFactory中明確加入物件刪除責任;或為 Factory Method 樣式採用 Factory Disposal Method 樣式。
介面演化困難——這正是設計 API 時要特別警惕 Incomplete Abstraction 的原因。
對品質屬性的衝擊#
- 理解性:使用者難以理解「為什麼少了那個對稱方法」。
- 可變更性與可擴充性:互補方法分散在多處時,每次變更都要追溯多個抽象。
- 可重用性:被迫在新脈絡裡補實作不支援的部分。
- 可靠性:客戶端為補洞而直接存取內部資料,極易引發執行期錯誤——例如有 lock 卻沒 unlock 時的 workaround。
別名#
- Class supports incomplete behavior:類別的公開介面不完整。
- Half-hearted operations:互補方法以不完整或不一致的方式提供。
實務考量#
刻意不提供對稱方法#
唯讀集合可能只提供
add()而沒有remove()(甚至兩者都沒有)。有意識的設計選擇不算壞味道。
用一個帶布林參數的方法取代成對方法#
java.awt.MenuItem 與 java.awt.Component 早期分別有 disable() 與 enable(),後來被 setEnabled(boolean) 取代;同理 show()/hide() 被 setVisible(boolean) 取代。
由於這些方法以布林參數取代成對的動作,沒有對應的
getEnabled()/getVisible()是合理的——不算 Incomplete Abstraction。