不只是畫線而已:精通 Class Diagram,提升你的物件導向設計力

在現代軟體工程的宏偉藍圖中,物件導向程式設計(Object-Oriented Programming, OOP)無疑是基石之一。然而,要建構一個結構清晰、易於維護且可擴展的物件導向系統,單憑程式碼本身往往不足以洞察全局。此時,統一塑模語言(Unified Modeling Language, UML)中的類別圖(Class Diagram),這種重要的uml圖便扮演了至關重要的角色。

類別圖是一種靜態結構圖,它不僅是開發團隊之間溝通的共同語言,更是將抽象的業務邏輯與人事地方物轉化為具體系統設計的橋樑,以滿足使用者的需求。它能精確地用來描述系統中的類別、介面、它們的內部屬性與方法,以及彼此之間的關係。

無論您是系統分析師、架構師還是開發工程師,深入理解並熟練運用類別圖,都將是提升設計能力與溝通效率的關鍵。本文旨在做一份詳盡的筆記,帶您全面探索類別圖的世界,從最基礎的構成元素到六種核心關係的細微差別,提供一份詳盡且實用的完整指南。

類別圖的核心元素:建構系統的基石

一個類別圖是由多個基本圖形符號所構成,其中最核心的便是代表「類別」與「介面」的方框。在軟體建模的過程裡,理解這些方框的結構與標示,是看懂類別圖的第一步。

類別 (Class)、抽象類別 (Abstract Class) 與介面 (Interface)

在類別圖中,類別、抽象類別與介面都使用一個三層結構的矩形方框來表示,但細節上有所區別:

名稱區域 (Name Compartment):位於最頂層,用以標示類別或介面的名字,其名稱通常以粗體顯示。

  • 具體類別 (Concrete Class):名稱為正體字,如 BankAccount。
  • 抽象類別 (Abstract Class):名稱為斜體字,如 Shape。這表示該類別不能被直接實例化,必須由子類繼承。
  • 介面 (Interface):除了名稱為斜體字外,通常會加上 <<interface>> 的標註(stereotype),如 <<interface>> Drivable

屬性區域 (Attribute Compartment):位於中間區域,列出類別所擁有的成員變數或屬性 (attributes)。

  • 格式為:可見性 符號 屬性名稱: 資料型態,在屬性名稱與資料型態之間會使用冒號分隔。
  • 例如:- balance: BigDecimal

方法區域 (Method/Operation Compartment):位於最底層,列出類別可執行的方法或操作 (methods)。

  • 格式為:可見性 符號 方法名稱(參數: 型態): 回傳型態
  • 例如:+ deposit(amount: double): boolean

可見性與成員修飾符

為了體現物件導向的「封裝」特性,類別圖使用特定的符號來表示屬性與方法的存取權限,即可見性 (Visibility)。這些符號會放在各成員名字的前面。

符號 可見性 中文 描述
+ Public 公開 任何外部物件都可以存取。
Private 私有 僅限於該類別內部可以存取。
# Protected 保護 該類別及其所有子類別可以存取。
~ Package (在 Java 等語言中)同一套件(Package)內的其他成員可見。

除了可見性,還有一些重要的修飾符:

  • 靜態 (Static):在屬性或方法名稱下方加上底線,或在某些工具中使用 $ 符號,表示該成員屬於類別本身,而非實體。
  • 抽象 (Abstract):方法名稱使用斜體字,表示該方法沒有實作,必須由子類別來完成。
  • 衍生 (Derived):在屬性名稱前面加上正斜線 /,表示該屬性值是從其他屬性推導而來,無需直接儲存。

深入剖析六大關係:定義物件的互動模式

如果說類別是系統中的「名詞」,那麼關係就是連接這些名詞的「動詞」和「介詞」。UML 定義了多種關係,其中最核心的有六種,它們的線條、箭頭樣式各不相同,代表的語義也天差地遠。

關係類型 (中文/英文) 語義 描述 線條與箭頭
繼承 (Generalization) “is a” 一個子類別繼承父類別的屬性與方法。 實線 + 空心三角形箭頭 (指向父類別)
實作 (Realization) “implements” 一個類別實現了某個介面所定義的方法。 虛線 + 空心三角形箭頭 (指向介面)
組合 (Composition) “is part of” 強烈的「整體-部分」關係,生命週期緊密相連。 實線 + 實心菱形 (在整體端)
聚合 (Aggregation) “has a” 較弱的「整體-部分」關係,生命週期各自獨立。 實線 + 空心菱形 (在整體端)
關聯 (Association) “has a” 兩個類別之間存在結構性聯繫,為長期穩定關係。 實線 (可帶有普通箭頭表示導航性)
依賴 (Dependency) “uses a” 一個類別在執行時臨時使用到另一個類別。 虛線 + 普通箭頭 (指向被依賴者)

1. 繼承 (Generalization / Inheritance)

  • 概念:繼承體現了「是一種 (is a)」的關係,是物件導向中代碼複用的關鍵機制。子類別(Subclass)繼承了父類別(Superclass)的公開與保護成員,並可以擴充自己的功能。
  • 符號:一條帶有空心三角形箭頭的實線,箭頭指向父類別。
  • 範例:車子 繼承自 交通工具。車子是一種交通工具。

2. 實作 (Realization / Implementation)

  • 概念:實作表示一個類別遵循了某個介面(Interface)所訂定的「合約」。介面只定義方法簽章,而不提供具體實作,由實作該介面的類別來完成。
  • 符號:一條帶有空心三角形箭頭的虛線,箭頭指向被實作的介面。
  • 範例:鳥 類別實作了 可飛行 (Flyable) 介面。

3. 組合 (Composition)

  • 概念:這是一種強烈的「擁有」關係,代表「…是…的一部分 (is part of)」。整體與部分的生命週期是綁定的:當整體物件被銷毀時,其所有部分物件也必須隨之銷毀。部分無法獨立於整體存在。
  • 符號:一條帶有實心菱形的實線,菱形端連接在「整體」類別上。
  • 範例:人 與 心臟。如果人不存在了,心臟也無法獨立存活。

4. 聚合 (Aggregation)

  • 概念:這是一種較弱的「擁有」關係,同樣表示「整體-部分」,但整體與部分的生命週期是獨立的。部分可以脫離整體而單獨存在,也可以被多個整體所共享。
  • 符號:一條帶有空心菱形的實線,菱形端連接在「整體」類別上。
  • 範例:球隊 與 球員。球員可以加入某支球隊,但若球隊解散,球員依然存在,可以再加入其他球隊。

5. 關聯 (Association)

  • 概念:關聯是一種泛化的結構性連結,表示一個類別的實體長期地「持有」或「知道」另一個類別的實體。如果兩個類別的關係不符合繼承、組合或聚合的明確定義,通常就用關聯來表示,例如A類別與B類別之間的關係。
  • 符號:一條實線。可以在關係的一方或雙方加上普通箭頭 (>) 來表示導航性(Navigability),意指可以從哪個方向存取另一個類別。
  • 範例:學生 與 課程。學生會選修課程,這是一種長期的結構關係。

6. 依賴 (Dependency)

  • 概念:這是最弱的一種關係,表示「使用 (uses a)」。它描述一個類別在某個操作中(例如作為方法的參數、回傳值或局部變數)臨時地使用了另一個類別。被依賴類別的變更可能會影響到依賴它的類別。
  • 符號:一條帶有普通箭頭的虛線,箭頭指向被依賴(被使用)的類別。
  • 範例:主廚 依賴 食材。主廚在「做菜」這個方法中會使用到食材,但主廚本身並不永久持有食材這個屬性。另一個例子是WebServer類別可能會依賴NetworkConnection類別來處理網路請求。

關係的進階屬性:讓圖表資訊更豐富

為了讓類別圖傳達更精確的資訊,我們可以在關係線上附加額外的註記。許多繪圖工具都支援這些進階的表示方式。

  • 多重性 (Multiplicity / Cardinality):標示在關係線的兩端,用來表示一個類別的實體可以與另一個類別的多少個實體相關聯。
標記 描述
1 只能有一個實體
0..1 零個或一個實體
或 0.. 零個或多個實體
1..* 一個或多個實體
m..n 最少 m 個,最多 n 個實體
  • 角色名稱 (Role Name):標示在關係線的末端,用來說明該類別在這次關係中所扮演的角色。例如,在 公司 與 員工 的關聯中,可以分別標上 僱主 和 僱員 的角色名稱。
  • 關係標籤 (Label):可以直接在關係線上添加文字,說明這個關係的動詞或性質,例如 Student “1” –o “1” Bike : rides(學生騎乘腳踏車)。

常見問題 (FAQ)

Q1: 聚合 (Aggregation) 和組合 (Composition) 有什麼根本區別?

A1: 最根本的區別在於生命週期的依賴性。在組合關係中,部分物件的生命週期完全依賴於整體物件,整體一旦銷毀,部分也隨之銷-亡,部分無法獨立存在。而在聚合關係中,部分物件的生命週期是獨立的,即使整體物件被銷毀,部分物件仍然可以存在。簡單記法:組合是「同生共死」,聚合是「可聚可散」。

Q2: 關聯 (Association) 和依賴 (Dependency) 如何區分?

A2: 主要區別在於關係的強度與持續時間。關聯是一種結構性的、較為長期的關係,通常體現為一個類別的「屬性」或「成員變數」是另一個類別的實體。例如 Person 類別有一個 address 屬性,這是 Address 類別的實體。而依賴是一種更弱的、臨時性的使用關係,通常發生在方法層級,例如某個方法的參數或回傳值是另一個類別,方法執行完畢後,這種依賴關係就可能結束。

Q3: 在繪製類別圖時,是否所有細節都必須呈現?

A3: 不必。類別圖的詳細程度取決於其目的與受眾。在專案初期的概念建模中,可以只畫出主要的類別及其最重要的關係,忽略屬性和方法的細節,用於溝通核心架構。而在詳細設計階段,則需要繪製包含所有公開(甚至私有)成員、型別、可見性與完整關係的詳細圖表,以作為開發人員編碼的直接依據。

Q4: 什麼時候應該使用介面 (Interface) 而不是抽象類別 (Abstract Class)?

A4: 這是一個經典的物件導向設計問題。一般原則是:當您想描述一種「是一種 (is-a)」的關係,並且希望共享部分實作程式碼時,使用抽象類別(例如 Dog is an Animal)。當您想定義一種物件「能做什麼 (can-do)」的「能力」或「合約」,而不關心其具體類別時,使用介面(例如 Bird can Fly,Airplane can Fly)。此外,由於大多數語言不支援多重繼承類別,但支援實作多個介面,因此當一個類別需要擁有多種不同能力時,介面是唯一的選擇。

總結

UML 類別圖不僅僅是一張技術圖表,它更是軟體設計思維的具象化呈現。它強迫我們在編寫任何程式碼之前,就必須思考系統的靜態結構、模組的職責劃分以及物件之間的協作模式。從單一類別的內部封裝,到六種關係所定義的外部協作,每一個符號、每一條線都蘊含著深刻的設計決策。

熟練掌握類別圖,能幫助我們在專案初期就識別出潛在的設計缺陷,促進團隊成員間的無障礙溝通,並最終產出更加穩固、靈活且易於維護的軟體系統。希望本篇文章能成為您探索和運用類別圖的得力助手,讓您在拯救 IT 人的每一天中,都能畫出清晰、精準的設計藍圖。

資料來源

返回頂端