UML (統一建模語言),對於所有學過 OOP 語言的人是一個耳熟能詳的圖表,UML, SysML, IDEF 都是建模語言,他們要做的事情其實都是在描述整個功能、系統的關聯性。
前面一些文章也或多或少介紹了 UML 概念下產生的表達圖,所以這篇文章就會專注在 Code 上。
UML 的圖形元素被分成四大種,裡面都有不同的表達方式:
由於項目太多,可自行到 [3] 去參考,這裡介紹類別、介面、封裝、以及關聯性就好:
類別的表示法:
範例是一個學生類別,這個類別被 new 出來後只有姓名屬性可以直接被存取,其他的都不行,方法也只有 EnrollClass() 可以直接被呼叫。
物件的表示法,就是把名稱劃上底線,假設要描述一個傳遞物件到某個參數的行為,在此先假設學生 B 已經被 new 完後傳遞,則可以直接使用上圖,排除掉非公開的屬性跟方法,畫成圖:
介面,則是在名稱上加上 <<interface>>
:
封裝的畫法如下圖:
不過介面裡面可裝的元素包含屬性、類別、各種類別放在一起的 Library、或是 Interface,像是下圖這樣。
關聯性的線則是有以下圖表可以對照 [5]:
註記: 組合、聚合、依賴、繼承、關聯相關減少混淆解釋範例,可以參考 [7] [10]。
UML 圖表類型總共有 14 種:
分別是:
Structure Diagrams 結構圖 (Structural Modeling):
Static Diagram 靜態圖
Implementation Diagram 實現圖
剖面圖 Profile Diagram (內部構造圖)
Behaviour Diagrams 行為圖 (Behavioral Modeling):
Interaction Diagrams 互動圖 (Architectural Modeling):
本文章使用的是 UML 2.0 版,包含互動摘要、時序、時間圖都是新增進去的功能。
模型與圖形是不同的,這些獨立的 UML 模型可以用來呈現不同情境:
【功能模型】,最直接的例子就是使用案例圖,,主要是從使用者角度(包含工程師、終端使用者)展示系統功能,包括但不限於這個案例圖來製作 [8]。
【物件模型】,就是上述介紹的類別圖、物件圖…ETC 等。
【動態模型】,最直接的例子就是狀態圖、有限狀態機,這些圖可以用來描述併發 (Concurrent) 狀態及系統事件。
比方說,使用動態圖來描述一個併發內的子狀態,可以參考下圖 [9]:
設計模式的書其實也會使用 UML 圖來表達,而且大多都不是用複雜的 UML 圖來呈現的,最主要需要了解的關聯性有以下六種:
泛化 (Generalization)、實現 (Realization)、聚合 (Aggregation)、組合 (Composition)、依賴 (Dependency)、關聯 (Association) 。
最後可以透過這六種圖的搭配組合出不同的 UML 模式,本篇文章想要使用程式碼搭配圖形來解釋,請見下一節。
OOP 的 IDE 像是 Visual Studio, Net Beans 都有 UML 圖設計的工具,可以按照程式碼產生類別圖後作畫, Visual Studio 裡面,可以透過 add item -> diagram 就可以產生這些圖了。
現在下面要試著混用 Visual Studio 介紹關聯性的六種表達線。
實心箭頭 + 實線表示繼承,他的寫法是 (C#):
abstract class 動物
{
public abstract void 行走();
public abstract void 跳();
public abstract void 叫();
}
class 貓: 動物
{
public Program many_program;
public override void 叫()
{
Console.WriteLine("Meow");
}
public override void 行走()
{
throw new NotImplementedException();
}
public override void 跳()
{
throw new NotImplementedException();
}
}
讓動物變成一個抽象類別,然後讓貓咪繼承他之後實作。
Visual Studio 表示法有點奇特,不過空心箭頭 + 虛線是指實作 Interface 的意思。
public interface I人貓通訊介面
{
void 握手();
void 吃罐罐();
}
public class 貓通訊 : I人貓通訊介面
{
void I人貓通訊介面.吃罐罐()
{
throw new NotImplementedException();
}
void I人貓通訊介面.握手()
{
throw new NotImplementedException();
}
}
貓通訊本身就是一個介面,讓人類把貓通訊介面實現出來之後,透過其他神秘的方法,吃下這個介面的實現值再去執行功能,讓貓貓知道你要讓他吃罐罐跟握手。
依賴是虛線箭頭 + 虛線構成,由於在 Visual Studio 工具箱找不到那條虛線,所以直接畫圖出來。
**class 股票
{
}
class 彩券
{
}
class Me
{
public void 致富(股票 股, 彩券 券)
{
// 瘋狂買股買券
}
}
我看幾個範例都喜歡寫新陳代謝,不過我改成 Me,我離不開致富,而致富需要使用彩券 + 股票,這就形成依賴關係了 (見笑)。
關聯是實現箭 + 頭線現表示。
class 辣椒作物
{
// 我需要知道天氣
天氣 天氣;
// 建構子 (請在實作時讓我知道天氣)
辣椒作物(天氣 天氣) {
this.天氣 = 天氣;
}
public void 成熟採收()
{
if(this.天氣.目前季節() == "夏天")
{
Console.WriteLine("成熟準備採收");
}
else
{
Console.WriteLine("還不到採收季節");
}
}
}
class 天氣
{
public string 目前季節 ()
{
return "冬天";
}
}
辣椒作物需要知道天氣,才能讓成熟採收功能運作,所謂知道的意思,也可以想成是 Class 中的屬性是否有這項資料,或是從建構子帶入進來哪些資料 (不一定)。
聚合是使用空心菱形 + 實線表示。
class 團體
{
public List<人> 成員; // 成員列表
internal 人 人
{
get => default;
set
{
}
}
public void 加入團體(人 某人) {
成員.Add(某人);
}
public void 退出團體() { }
}
class 人
{
public void 工作() {
}
}
人和團體是不同的概念,團體在成員屬性中需要蒐集各式各樣的人,這些人也因此聚合到團體中了。
組合是使用實心菱形 + 實線表示。
class 墨西哥帽子
{
}
class 棒球衣服
{
}
class 演員
{
private 墨西哥帽子 墨西哥帽;
private 棒球衣服 棒球衣;
//建構子
演員()
{
this.墨西哥帽 = new 墨西哥帽子();
this.棒球衣 = new 棒球衣服();
}
}
演員本身可以透過陳列多個屬性,最後在建構子建立時,幫演員裝上特定的衣服跟帽子,組合出裝備。
References:
[1] https://www.tutorialspoint.com/uml/uml_basic_notations.htm
[2] https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E5%BB%BA%E6%A8%A1%E8%AF%AD%E8%A8%80
[3] https://www.tutorialspoint.com/uml/uml_architecture.htm
[4] https://www.conceptdraw.com/How-To-Guide/uml-class
[5] https://creately.com/blog/diagrams/class-diagram-tutorial/
[6] http://www-sop.inria.fr/axis/cbrtools/usermanual-eng/Print/UMLNotationPrint.html
[7] https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition/
[8] https://creately.com/diagram/example/i9z1wc852/Functional%20model
[9] https://www.tutorialspoint.com/object_oriented_analysis_design/ooad_dynamic_modeling.htm
[10] https://juejin.cn/post/6844904006221824013