帶你掌握 UML 類別圖的 6 種關係

更新於 發佈於 閱讀時間約 8 分鐘

Let's learn 6 relationships of class diagram.

來,你現在是一個軟體設計師,請問你想用什麼方式來體現你的軟體設計呢? 你有什麼更好方式表達來讓你的同事理解你的設計進一步跟你一起協作呢? 我認為在軟體設計的溝通上使用圖形化的工具是相當好的辦法。UML 裡的類別圖(Class Diagram)就是一個很好表達軟體架構設計的工具,要畫好類別圖會需要用到 6 種關係線來定義類別間的關係與如何實作,也是我們這次要關注的主題。

UML 的類別圖是軟體設計視覺化的一種體現

畫線是艱難的任務

在網路上查找可以發現有很多類別圖的 6 種關係的說明與示例,通常不太容易難取得共鳴。主要有兩個原因:

  1. 對於這些關係線的定義混淆,導致無法判斷滿足條件與使用時機
  2. 缺少生活相關的具體案例,很難理解這些關係所對應的抽象概念

類別與介面

這裡需要先插個話,補充一點 OOP 的知識。在 OOP 裡面類別(Class)是成員屬性(Member)與函數方法(Operation)的集合,按照其內容物的差異可分為三種:

  1. 一班類別:有成員屬性跟完全實作的函數方法 ,能直接實體化
  2. 抽象類別:有成員屬性跟不完全實作的函數方法,因為實作不完全不能直接實體化
  3. 抽象介面:只定義函數方法且完全不實作,跟抽象類別一樣不能直接實體化

其他的 OOP 的觀念有機會再說,這裡你需要先記得有類別(Class)跟介面(nterface) 這兩種就好。

類別有三種:一班類別、抽象類別、抽象介面

類別圖的 6 條線

我們繼續回到類別關係上,在類別圖裡定義的 6 種關係線分別是:繼承(Inheritance) ,實現 (Realization),合成 (Composition),聚合 (Aggregation),相依 (Dependency) 以及 關聯 (Association)。

Inheritance, 繼承是全盤接收

繼承關係套用程式書籍的說法叫做「父子」類別,負責繼承的是子類別,被繼承的是父類別,它是描述著兩個類別間具有「上下」關係。繼承概念就是「全盤接收」,子類別會接收父類別的所有屬性成員跟函數方法而且可以直接拿來用,所以子類別繼承後的預設的特性跟行為會跟父類別一致。

繼承關係重點是子類別還可以增加自己需要的成員屬性,利用擴充或複寫父類別的函數方法來跟父類別做出差異。就像我們與父母就是繼承關係,一樣也可以透過日後的學習成長有自己的特色與能力來跟父母產生差異,所謂「青出於藍更勝於藍」就是對繼承關係極佳註解。

Example of Inheritance

Example of Inheritance

在 OOP 的實作上,繼承關係可以是單一繼承或是多重繼承,多重繼承可以一次拿到多個父類別現有的能力,相對的設計跟除錯難度也會變得很高,例如當被繼承的兩個父類別都有一樣的函數方法,請問呼叫這個方法的時候,你覺得會叫到哪一個父類別的函數方法呢?

Realization, 實作是擴增能力

實作也是用來描述兩個類別的上下關係跟繼承類似,只要把的父類別換成是一個或多個抽象介面就可以了,所以實作關係也可以視為一種特殊的繼承關係。抽象介面的設計目的是把一組相關的函數方法(能力)定義出來綑綁在一起,負責實作介面的子類別必須按照介面的要求完全實作所有介面規定的函數方法。

實作的概念就是「有目的性的學習成長」,假設你也想成為程式設計師,那麼你就必須學會當程式設計師所需要的能力,例如至少會一種程式語言、要有編寫程式碼的與除錯的能力、要有代碼管理能力跟持續整合的能力等等。只要有完全實作出這組能力就算你是非本科系出身也可以是位合格的程式設計師了。

Example of Realization

Example of Realization

在 OOP 的實作上,類別可以實作一個到多個介面,在實作關係裡不管實作了多少介面,原則是所有介面有定義的函數方法都要全部實作出來。雖然實作多個介面來獲取能力不像多重繼承一次取得多個父類別的能力這麼方便,不過成功實作所有介面後,這種「能力插件」運作起來可以媲美多重繼承的效果。

Composition, Aggregation 合成/聚合是雙胞胎

合成跟聚合這兩種關係很像所以放在一起講,它們描述的是物件與部件的組合關係。在生活中你應該會觀察到大部分物件都是透過很多個部件組合,透過多個物件合作的方式運作的。

要區分合成或聚合的關鍵就是分析物件與它的部件兩者是否「密不可分」!只要你發現物件跟其部件分離後會出現兩個一起掛點的狀況,就表示兩者關係就是合成;相對的,物件跟部件分開後都活得好好的就叫做聚合。

最經典的合成關係莫過於我們的人體與體內的重要器官,像是心臟大腦… 一旦將它們從我們體內被分離出來,肯定兩個都活不了 ;善用「密不可分」的原則就可以很好的判斷兩個類別之間是合成還是聚合關係。

Example of Composite and Aggregation

Example of Composite and Aggregation

從 OOP 的實作上,合成或聚合的作用範圍都在成員屬性等級,物件類別會建立一個對應「部件」類別型態的成員屬性來保存這個部件。觀察「密不可分」的時候可以往物件解構那段程式去找找,如果你發現物件解構的時候也會把部件也解構掉,就表示兩者「密不可分」是為合成關係,否則就是聚合關係。

如果你是使用 C++ 這類需要手動清除記憶體的程式語言,那麼實作出正確的合成或聚合關係就很重要,因為該解構卻沒被解構的物件會造成記憶體洩漏,導致被佔用的記憶體無法釋放出來,隨時間增加你的程式會開始越來越難配置到記憶體空間,會變慢、變得不穩定,此時離當機也不遠了。

Dependency, 相依是我需要你

相依關係可以解釋為兩個類別間的「需要對方」關係,說到這邊你可能會想,難道合成或聚合裡的物件與部件兩者關係不也是相依關係嗎? 要區分這裡的相依關係的關鍵就是看那些被需要的物件出現的位置,以及這些物件是否有「用完就收」特性。

相依關係的作用範圍僅在函數方法等級,觀察重點在一個被需要的物件是否只出現在需求者物件的函數方法的參數列、程式碼區塊裡面內或是函數方法的回傳值裡面?在來看用完就收,當這些函數方法執行完畢後,這些被需要的物件,不會被保留在需求者物件上。

Example of Dependency

Example of Dependency

相依在概念上跟我們日常生活中臨時需要或是租借關係很像,再回到軟體工程師的例子,白天在公司上班要發揮編程能力時「需要」一台電腦跟外接螢幕作為編成函數方法的輸入(參數)。等下班軟體工程師會歸還這些設備並不會把它們帶回家。

到了 OOP 的實作上,判斷相依關係的撇步只需要去觀察調用它的函數方法裡有沒有把傳進來的物件用一個成員屬性存起來即可,如果沒有就是相依,反之我們就要往前面說的合成或聚合關係去確認了。

Association, 關聯是點頭之交

最後一種是叫做關聯關係,只要求兩個類別有”互相認識”對方就可以了是一種最低限度的關係。所謂認識的方式就是把自己類別的某個屬性開放出去給另一個需要認識自己的類別裡面。

Example of Association

Example of Association

這種關聯概念很像我們去訂閱某個資訊或服務,就像前陣子我想在網路書店購買一本原文書結果缺貨,於是我用電子郵件跟網路書店產生關聯,我把電子郵件放給網路書店,請它在有庫存的時候通知我;關聯關係也可是雙向多重的,反過來我也可以自己去訂閱各家書店的書本庫存數寄到我的電子郵件裡,只要發現有庫存我就可以去訂書。

關聯關係是一種平等自由、雙向多重的關係。它讓類別之間可以即時了解對方的狀況,再由類別實體們自己決定是否要根據這個狀態的改變來行動。

在 OOP 的實作上,會把這種關聯性建立在類別變數上,類別變數的概念可以理解為屬於該類別的全域變數,只要這個全域變數有變,則所有繼承這個類別實體物件自然也都會「自動」收到通知,當物件收到到訊息的後會決定如何做出反應。

總結:

以上就是這次要跟你聊的在 UML 裡的類別圖裡會用到的 6 種類別間的關係,如果你是第一次聽到感覺好像很複雜不容易弄懂,這是學習過程中很正常的現象,不需要浪費時間在自我懷疑身上,只要你願意多接觸,多花點時間,稍微靜下心弄清楚了定義(判斷標準),遇到抽象的觀念就嘗試用自己身邊熟悉的事物來類比,轉換成自己的語言,相信你也很快就能掌握 6 條線,讓這些設計語言成為你軟體設計工作與團隊溝通的利器。

avatar-img
16會員
61內容數
WarrenLo's 軟體設計武功祕笈
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Warren Lo的沙龍 的其他內容
UART 轉換完成的 Serial 訊號已經可以用來傳輸通訊了,那為什麼還要把 UART 轉出來的訊號再轉換成成其他的 Serial 介面,像是 RS232/RS485 再進行傳輸呢?原因是 UART 的 Serial 訊號傳輸的距離實在太短了
Serial 通訊數據必須先在 UART 元件把 Parallel 轉成 Serial,EIA Driver 會把 Serial 轉成特定的 Serial 訊號。UART 數據轉換要考慮兩個關鍵點,如何讓資料從直行變橫列(躺平)的方法,還要考慮如何控制 Serial 訊號輸出
聊到 Serial 通訊就一定會提到 COM Port,它是微軟定義的一個經典 Serial 通訊實作。COM Port 在筆電還不普及的年代可以很輕鬆在 PC(桌機)的主機板上找到有標示 COM1 或者 COM2 的通訊接口,這些就是最常見的 COM Port 通常搭載的都是 R232 的通訊規格
問題來了,如果在一組 Master-Slave 在通訊過程中出現其他的裝置「插嘴」的狀況會怎麼樣?因為迴路只有一條,所以只要在同一個通訊迴路上同時有兩台裝置發出訊號,結果就是兩個資料封包互相對撞雙雙損毀…
物聯網裝置跟電腦一樣處理資料過程仰賴記憶體,不同類型的資料必須放在不同的記憶體空間。RD 會按照功能需求去規劃這些數據儲存空間,就像設計師規劃客廳、廚房、衛浴與臥室等居住空間一樣。各個空間的規劃原則上不會允許重疊,如此才能確保不同資料間不會發生互相覆蓋導致記憶體錯誤發生。
Modbus 是比 Ethernet 更簡單的通訊架構,走的是 Master-Slave 模式。協議規定 Slave 裝置們都不可以主動回應,所以 Slave 們在通訊過程中遇到看不懂的、壞掉的資料封包,能做的處理方式就是直接丟掉,從外面看 Slave 裝置的行為就跟「已讀不回」一樣毫無反應
UART 轉換完成的 Serial 訊號已經可以用來傳輸通訊了,那為什麼還要把 UART 轉出來的訊號再轉換成成其他的 Serial 介面,像是 RS232/RS485 再進行傳輸呢?原因是 UART 的 Serial 訊號傳輸的距離實在太短了
Serial 通訊數據必須先在 UART 元件把 Parallel 轉成 Serial,EIA Driver 會把 Serial 轉成特定的 Serial 訊號。UART 數據轉換要考慮兩個關鍵點,如何讓資料從直行變橫列(躺平)的方法,還要考慮如何控制 Serial 訊號輸出
聊到 Serial 通訊就一定會提到 COM Port,它是微軟定義的一個經典 Serial 通訊實作。COM Port 在筆電還不普及的年代可以很輕鬆在 PC(桌機)的主機板上找到有標示 COM1 或者 COM2 的通訊接口,這些就是最常見的 COM Port 通常搭載的都是 R232 的通訊規格
問題來了,如果在一組 Master-Slave 在通訊過程中出現其他的裝置「插嘴」的狀況會怎麼樣?因為迴路只有一條,所以只要在同一個通訊迴路上同時有兩台裝置發出訊號,結果就是兩個資料封包互相對撞雙雙損毀…
物聯網裝置跟電腦一樣處理資料過程仰賴記憶體,不同類型的資料必須放在不同的記憶體空間。RD 會按照功能需求去規劃這些數據儲存空間,就像設計師規劃客廳、廚房、衛浴與臥室等居住空間一樣。各個空間的規劃原則上不會允許重疊,如此才能確保不同資料間不會發生互相覆蓋導致記憶體錯誤發生。
Modbus 是比 Ethernet 更簡單的通訊架構,走的是 Master-Slave 模式。協議規定 Slave 裝置們都不可以主動回應,所以 Slave 們在通訊過程中遇到看不懂的、壞掉的資料封包,能做的處理方式就是直接丟掉,從外面看 Slave 裝置的行為就跟「已讀不回」一樣毫無反應
你可能也想看
Google News 追蹤
Thumbnail
大家好,我是woody,是一名料理創作者,非常努力地在嘗試將複雜的料理簡單化,讓大家也可以體驗到料理的樂趣而我也非常享受料理的過程,今天想跟大家聊聊,除了料理本身,料理創作背後的成本。
Thumbnail
哈囉~很久沒跟各位自我介紹一下了~ 大家好~我是爺恩 我是一名圖文插畫家,有追蹤我一段時間的應該有發現爺恩這個品牌經營了好像.....快五年了(汗)時間過得真快!隨著時間過去,創作這件事好像變得更忙碌了,也很開心跟很多厲害的創作者以及廠商互相合作幫忙,還有最重要的是大家的支持與陪伴🥹。  
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這一章節旨在介紹 PHP 中的物件導向編程(OOP)概念。通過詳細講解類別、建構子、訪問修飾符(公開、私有、受保護)、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等概念,使讀者能夠理解和應用這些 OOP 技術來編寫更具結構性和可維護性的 PHP 代碼。
Thumbnail
本章節的目的是介紹 Kotlin 中的物件導向概念。這包括了類別、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型以及反射等概念。每一個概念都會透過範例程式碼來解釋其功能和用法。
Thumbnail
這個章節主要介紹了Swift程式語言中物件導向程式設計的基本概念,包括類別、建構子、公開、私有、受保護等等的概念。同時,也介紹了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射等進階特性。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
Thumbnail
本章節旨在介紹JavaScript中的物件導向編程。內容包括類別(Class)的定義和使用,建構子的作用,以及公開,私有,受保護(Protected)等不同訪問修飾符的概念。此外,還涵蓋了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型、反射等物件導向的主要觀念。
※ OPP第三大核心-多型 ※ 多型的基本定義: 多型是利用繼承的特性,讓不同的子類別可以實現相同的介面,但在呼叫這些介面的方法時會表現出不同的行為。這使得程式設計更具彈性和擴展性,避免了複雜的條件判斷式,同時促進了代碼的重用。 class Animal { makeSound() {
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
※ class類別 什麼是class? class是創造consturctor function時的語法糖,本質上與使用function創造物件(object)的行為沒有不同。 class的作用: 用來定義、描述要創造的物件(object)具有那些屬性、行為的一個表達式。就像是「車子的設計圖
Thumbnail
本文介紹了Python中的物件導向程式設計的重要概念,包括類別、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射。每個概念都有對應的程式碼範例來說明其用法和功能。這些概念對於理解和使用Python進行物件導向程式設計至關重要。
Thumbnail
本文讓我們來淺談一下類別是什麼? 若想看詳細一點的python官方教學可點此連結 Python 的類別(Class)是一種面向物件導向程式設計的概念,讓你能夠創建具有屬性和方法的物件。類別是對現實世界中事物的抽象,它包含數據和操作這些數據的方法。它非常的抽象,想像一個類別就像是一個蛋糕模具,
Thumbnail
大家好,我是woody,是一名料理創作者,非常努力地在嘗試將複雜的料理簡單化,讓大家也可以體驗到料理的樂趣而我也非常享受料理的過程,今天想跟大家聊聊,除了料理本身,料理創作背後的成本。
Thumbnail
哈囉~很久沒跟各位自我介紹一下了~ 大家好~我是爺恩 我是一名圖文插畫家,有追蹤我一段時間的應該有發現爺恩這個品牌經營了好像.....快五年了(汗)時間過得真快!隨著時間過去,創作這件事好像變得更忙碌了,也很開心跟很多厲害的創作者以及廠商互相合作幫忙,還有最重要的是大家的支持與陪伴🥹。  
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這一章節旨在介紹 PHP 中的物件導向編程(OOP)概念。通過詳細講解類別、建構子、訪問修飾符(公開、私有、受保護)、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等概念,使讀者能夠理解和應用這些 OOP 技術來編寫更具結構性和可維護性的 PHP 代碼。
Thumbnail
本章節的目的是介紹 Kotlin 中的物件導向概念。這包括了類別、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型以及反射等概念。每一個概念都會透過範例程式碼來解釋其功能和用法。
Thumbnail
這個章節主要介紹了Swift程式語言中物件導向程式設計的基本概念,包括類別、建構子、公開、私有、受保護等等的概念。同時,也介紹了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射等進階特性。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
Thumbnail
本章節旨在介紹JavaScript中的物件導向編程。內容包括類別(Class)的定義和使用,建構子的作用,以及公開,私有,受保護(Protected)等不同訪問修飾符的概念。此外,還涵蓋了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型、反射等物件導向的主要觀念。
※ OPP第三大核心-多型 ※ 多型的基本定義: 多型是利用繼承的特性,讓不同的子類別可以實現相同的介面,但在呼叫這些介面的方法時會表現出不同的行為。這使得程式設計更具彈性和擴展性,避免了複雜的條件判斷式,同時促進了代碼的重用。 class Animal { makeSound() {
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
※ class類別 什麼是class? class是創造consturctor function時的語法糖,本質上與使用function創造物件(object)的行為沒有不同。 class的作用: 用來定義、描述要創造的物件(object)具有那些屬性、行為的一個表達式。就像是「車子的設計圖
Thumbnail
本文介紹了Python中的物件導向程式設計的重要概念,包括類別、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型和反射。每個概念都有對應的程式碼範例來說明其用法和功能。這些概念對於理解和使用Python進行物件導向程式設計至關重要。
Thumbnail
本文讓我們來淺談一下類別是什麼? 若想看詳細一點的python官方教學可點此連結 Python 的類別(Class)是一種面向物件導向程式設計的概念,讓你能夠創建具有屬性和方法的物件。類別是對現實世界中事物的抽象,它包含數據和操作這些數據的方法。它非常的抽象,想像一個類別就像是一個蛋糕模具,