物件導向分析與設計為軟體建構提供了一種結構化的方法。此方法論著重於以資料(或物件)為核心來組織程式碼,而非以函式與邏輯為中心。理解基本建構塊對於建立可維護、可擴展且具韌性的系統至關重要。本指南詳細說明構成任何物件導向架構的核心要素。

🔍 基礎:類別與物件
此範式的基础存在兩個不同但相關的觀念:類別與物件。在初始設計階段混淆這兩者是常見的陷阱。區分定義與實例至關重要。
- 類別:一種藍圖或範本。它定義了結構與行為。它描述存在哪些屬性以及可執行哪些操作。在尚未實例化之前,它不會像實例那樣佔用記憶體。
- 物件:類別的具體實例。當程式執行時,會根據類別定義建立物件。每個物件都持有其自身的狀態。
考慮一個管理數位庫存系統。產品類別定義了產品的樣貌:它包含名稱、價格與庫存數量。當系統載入資料時,會建立個別產品物件。其中一個物件可能代表特定筆電,另一個則代表特定滑鼠。兩者擁有相同的結構,但儲存不同的資料值。
類別的關鍵特性
- 狀態:儲存在變數中的資料,通常稱為欄位或屬性。
- 行為:透過方法或函式執行的邏輯。
- 身份:區分不同實例的唯一方式。
🛡️ 封裝:保護資料
封裝是一種將資料與方法結合,同時限制直接存取物件部分組件的機制。它是一種隱藏物件內部狀態,並要求所有互動必須透過明確定義的介面進行的實踐。
為何封裝至關重要
- 資料完整性:透過控制資料的修改方式,可防止出現無效狀態。例如,銀行帳戶物件不應允許直接將餘額設為負數。
- 抽象化:物件的使用者只需知道物件能做什麼,而不需要了解它是如何做到的。
- 維護:若內部實作發生變更,只要介面保持不變,外部程式碼就不會失效。
在實務上,這是透過存取修飾符實現的。這些關鍵字決定了類別成員的可見性。常見的可見性層級包括公用、私有與受保護。私有成員僅可在類別內部存取。公用成員可從任何地方存取。受保護成員可在類別內部及子類別中存取。
🌳 抽象化:簡化複雜性
抽象化著重於隱藏複雜的實作細節,僅暴露必要的功能。它讓開發人員能夠使用高階概念進行工作,而不必陷入低階細節的泥沼。這能降低分析階段的認知負荷。
抽象化的類型
- 抽象類別:這些類別無法獨立執行實例化。它們設計為供其他類別延伸。它們可同時包含抽象方法(無實作)與具體方法(有實作)。
- 介面:一種契約,指定類別必須實作的一套方法。它不定義方法如何運作,僅規定這些方法必須存在。
抽象化支援關注點分離。與「付款處理程式」互動的使用者無需了解所使用的具體加密演算法。他們只需呼叫「處理付款」方法。這種分離使系統更易於理解與推論。
🔄 繼承:程式碼重用
繼承允許新類別採用現有類別的屬性與行為。現有類別稱為父類別或超類別;新類別則稱為子類別或次類別。這促進了程式碼重用,並建立邏輯階層結構。
繼承的好處
- 減少重複:共用邏輯只需在父類別中撰寫一次。
- 可擴展性:可新增新類型,而無需修改現有程式碼。
- 多型:繼承支援多型行為,允許不同類別被視為同一父類別的實例。
然而,繼承必須謹慎使用。過深的階層結構可能難以維護。父類別與子類別之間若耦合過緊,當基礎類別需要變更時可能引發問題。對於複雜關係,組合(Composition)通常是更佳的替代方案。
🎭 多型:動態的彈性
多型允許不同類別的物件以不同方式回應相同的方法呼叫。它使單一介面能代表不同的底層形式。這對建立彈性且可擴展的系統至關重要。
多型的形式
- 編譯時(靜態):透過方法重載實現。同一類別中的多個方法具有相同名稱,但參數列表不同。
- 執行時(動態):透過方法覆寫實現。子類別提供其父類別中已定義方法的特定實作。
考慮一個圖形渲染系統。您可能擁有一個「形狀 類別,其中包含一個 繪製 方法。 圓形 與 正方形 類別繼承自 形狀。當渲染引擎在形狀清單上呼叫 繪製 時,它無需知道具體的類型。每個形狀都知道如何自行繪製。這將渲染器與具體的幾何類型解耦。
🔗 關聯與關聯關係
物件並非孤立存在,它們彼此互動。明確定義這些關係是設計階段的關鍵部分。物件之間的關聯方式會影響耦合度與內聚度。
常見關係類型
- 關聯: 一種結構關係,其中一個物件使用另一個物件。它通常是多對多的關係。
- 聚合: 一種特定類型的關聯,其中整體與部分可以獨立存在。例如,一個
部門擁有員工。若部門被移除,員工仍然存在。 - 組合: 一種更強的聚合形式。部分無法在沒有整體的情況下存在。若
房屋被摧毀,則房間物件將不再存在。 - 依賴:一種關係,其中一個物件依賴另一個物件來執行任務。這種關係通常是暫時的。
比較表:聚合與組合
| 特性 | 聚合 | 組合 |
|---|---|---|
| 所有權 | 弱所有權 | 強所有權 |
| 生命週期 | 子物件獨立存在 | 子物件隨父物件一同消亡 |
| 範例 | 圖書館與書籍 | 房屋與房間 |
| 實作 | 透過建構子或設定子傳遞參考 | 在父物件內部建立 |
⚙️ 行為機制:方法與訊息
物件之間的互動透過訊息發生。在此情境中,訊息是要求物件執行某個動作的請求。此動作由方法實作。
方法生命週期
- 呼叫:客戶端向伺服器物件發送訊息。
- 執行:伺服器物件執行方法程式碼。
- 回傳:方法將結果或值回傳給客戶端。
有效設計確保方法具有單一職責。方法應專注做好一件事。若方法執行過多任務,將難以測試與維護。這符合單一職責原則,該原則指出類別應僅有一個變更理由。
🧩 進階結構概念
除了基礎概念外,還有數個進階概念可優化系統結構。這些工具有助於管理大型應用程式的複雜度。
介面與契約
介面定義了一個契約。它們指定了實作類別必須提供的一組方法。這使得不同類別在遵循相同介面時可以互換使用。它促進了鬆耦合。依賴介面的程式碼較不依賴具體的實作。
抽象工廠與建立型範式
建立物件可能相當複雜。建立型範式提供了一種管理物件建立的方式。與其到處直接使用new,工廠方法或抽象工廠則負責執行例項化。這將建立邏輯集中化。它使得在不變更客戶端程式碼的情況下,更容易替換實作。
設計原則的實際應用
多項原則指導這些元件的排列方式。應用這些原則可確保系統隨時間推移仍保持穩定。
- 高內聚:類別內的元件應高度相關。它們應協同工作以達成單一目的。
- 低耦合:類別之間的相依性應盡量減少。一個類別的變更不應在系統中產生連鎖反應。
- 開放/封閉原則:類別應對擴充開放,但對修改封閉。你應透過新增類別來加入新行為,而非修改現有程式碼。
📊 管理狀態與身分
狀態管理是物件導向系統的一個關鍵面向。物件會隨著時間對訊息做出回應而改變狀態。追蹤此狀態對於除錯與一致性至關重要。
狀態一致性
- 不可變性:某些物件設計為在建立後不改變狀態。這簡化了對程式碼的推論。它在並發環境中特別有用。
- 狀態的封裝:狀態變數應為私有。應使用存取器(getter)來讀取狀態,並使用變異器(setter)來變更它。這可確保不變式得以維持。
身分與相等性
理解身分與相等性的差異至關重要。身分指的是兩個參考是否指向記憶體中的同一個物件。相等性指的是兩個物件是否具有相同的内容或值。系統通常需要根據資料而非記憶體位址來檢查相等性。
🚀 為變更而設計
需求會演變。系統必須適應。本文討論的核心元件提供了變更所需的彈性。透過使用抽象與介面,你可以將系統中會變更的部分隔離。透過使用封裝,你可以保護內部邏輯免受外部干擾。
分析系統時,先從識別名詞(類別)和動詞(方法)開始。接著,定義它們之間的關係。確保階層結構合乎邏輯且不過於深層。當關係不是is-a關係時,應優先使用組合而非繼承。
應避免的常見陷阱
- 上帝物件:知道太多或做太多的類別。應將其拆解為更小、更專注的類別。
- 深層繼承樹:這使得難以理解方法定義的位置。在可能的情况下,應扁平化繼承層級。
- 抽象洩漏:強迫呼叫者理解實作細節。請保持介面簡潔。
📝 結構元素摘要
回顧一下,一個穩健的物件導向系統依賴於結構與行為之間謹慎的平衡。以下列表總結了基本組件。
- 類別:類型的定義。
- 物件:類型的執行階段實例。
- 屬性:物件所持有的狀態資料。
- 方法:物件執行的行為邏輯。
- 介面:定義行為的契約。
- 關聯:連接物件的連結。
- 封裝:內部狀態的保護。
- 繼承:程式碼重用的機制。
- 多型:將物件視為統一處理的能力。
掌握這些元素使架構師能夠建構出對變更具有韌性的系統。重點應保持在清晰度、可維護性與正確性上。當這些核心原則被一致地應用時,所產生的架構將經得起時間的考驗。











