---
title: '設計模式基礎 - UML'
disqus: kyleAlien
---
設計模式基礎 - UML
===
[TOC]
## OverView of Content
設計模式 推薦書
> [**設計模式之禪**](https://www.tenlong.com.tw/products/9787111437871?list_name=srh)
>
> [Android 設計模式](https://www.tenlong.com.tw/products/9789864340941?list_name=srh)
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)、[**軟體設計特色、物件導向核心 – 1 入門**](https://devtechascendancy.com/object-oriented-core-concepts/)
:::
## 軟體設計
### 軟體生命週期
* 一個軟體的開發一般會經歷以下這幾個階段
1. **軟體分析**:
這一步的重點在 **分析問題領域**,了解並頗析需求方,並且預估部份可拓展性
> 「問題領域」後面小節有說明
<!-- > TODO: 引用本章的 ⚓️ 錨點 -->
2. **軟體設計**:
確定軟體架構(或者是要使用的架構),並將整個 **專案劃分成大大小小的多個子系統**,**設計每個子系統的具體細節**
3. **軟體開發**:
可以到這一步再決定開發的程式語言,並實現設計時的藍圖
4. **軟體測試**:
實做單元測試、整合測試、系統測試、性能測試... 等等
5. **軟體佈署**:
可以使用 CICD 上架各種不同平台
6. **軟體維護**(維運):
修復軟體中的 Bug,當使用者有新需求、修改需求時可以不影響整體架構來達成需求
> 
### 優性的軟體特性
* 而優良的軟體開發應該具備以下這幾個特性(以下提出幾個重點,一個優秀的軟體,它們的特性都會互交)
* **高內聚**:每個子系統完成特定功能,不同子系統之間 **不會有重複功能**
> 在設計高內句的淚的同時,也要注意到子系統粒度不要過大,否則不易於維護
* **可重用性**:減少軟體程式的重複開發
* **可維護性**:當軟體需求發生變更(**並不是指新需求,而是業務邏輯上的調整**),可以修改局部代碼來達成需求(不會牽一髮動全身)
* **多子系統**:
* **穩定性**:系統的粒度要控制,粒度越小,可被重用的機率域高,當有需要修改時,也不會影響到過多的類
* **組合性**:透過小系統的組合來達成一個大系統,這種可組合性同時帶來了軟體的重用、維護性
* **可擴充性**:當接收到新需求時,應該透過 **拓展的方式** 來達成新需求,而不是修改已有程式碼(除了本身已有的商業邏輯錯誤)
* **低耦合**:子系統之間相互獨立,並切透過抽象、界面來達成業務需求
> 
### 軟體發展 - 結構化 vs. 物件導向
* **軟體發展 - 結構化語言**:
以往非「物件導向」的程式語言,多是採用 **命令式設計**(像是 C 語言),會透過 SA(`Structure Analysis`), SD(`Structure Desgin`), SP(`Structure Programming`) 相輔相成… 它(結構化)的特點在於最小單元是 **方法**,透過 **Input, Output** 來運算得到結果
> 對應的圖是資料流(`Data Flow`)
>
> 
這種結構化命令式設計,**往往導致緊耦合**(並非一定)、並且不易修改
:::warning
* 這裡的結構化結論並非一定,如果命令式設計優良,仍可達到類似物件導向的效果(可重用性高、可拓展)
> e.g: 像是 C 語言的 struct 繼承、C 語言的函數指標
:::
* **軟體發展 - 物件導向語言**:將每個需求分化,由底層開始建立(**最小單元為物件**),逐漸聚合為符合業務需求的軟體
其中的物件模型,就是 **問題領域的物件模型**(用電腦模擬現實事件的問題),它的特性如下
1. **由下至上的抽象**:
> 就像是先畫樹葉再畫出樹幹
* 先解釋 **由下**:物件導向設計會分析需求然後建立一個符合需求的模型,並建立相關類別
* 再說明 **至上的抽象**:創建完相關類別後,將相同特性抽取出來建立一個父類抽象,這就完成了上層的抽象建立
2. **由上至下的分解**:
* 建立物件模型的過程中,也同時包括了由上至下的分解
## 物件導向 - 核心、觀念
### 建立物件 - 問題領域
* **問題領域**:用軟體來 **模擬真實世界的領域**(包括公司、銀行、學校... 等等領域)
* 我們在建立物件時需要注意到的是啥?內容物是啥?應該封裝哪些資訊?
這**決定於你目前遇到的問題領域**(問題的接觸到哪些不同的層面),你要解決哪些問題,那你應該封裝哪些資訊;
對於同一件事物的「**觀點不同,所建立的物件就不同**」
> eg. 對於 Program Library,A 的看法是封裝的方法數量,B 的看法是大小,那就會建立兩個完全不同的物件
```kotlin=
// 以 Library 方法的數量來封裝
class ProgramLibraryA {
int methods = 0
}
// 以 Library 的大小來封裝
class ProgramLibraryB {
int size = 0
}
```
:::success
* 那應該選擇哪個呢?
同樣!這取決於你遇到的問題領域阿!你要去判斷你目前所遇到的情況再來封裝物件
:::
* **物件導向程式的特性如下**
1. **萬物皆為物件**:(當然~ 每個程式語言會有些許的不同)
問題領域中的實體、概念都可以化為軟體中的物件
> 而如何切分大小才能保證可重用性、擴充性,則須透過經驗來判斷
2. **每個物件都是唯一**:
在 JVM 中提供的運行環境中,不會存在兩個相同的物件,它們各在記憶體中的唯一位置
3. **物件有屬性、行為**:
屬性會在一個物件的範疇內做管理,行為則是對外暴露的方法
```kotlin=
class MySchool {
private int schoolStudentsCount = 0;
public void addStudents(int count) {
schoolStudentsCount += count;
}
}
```
4. **物件可以有狀態**:
既然物件有司有的屬性,那就可以控制該物件的狀態
```kotlin=
class MySchool {
public static final int MAX_COUNT = 1_000;
private int schoolStudentsCount = 0;
public void addStudents(int count) {
// 控制狀態
if(schoolStudentsCount + count > MAX_COUNT) {
throw Exception();
}
schoolStudentsCount += count;
}
}
```
> 
:::success
* **物件、類別**的差異?
類別就像是一個模板,在 JVM 內只會存在一個(在方法區),而 **物件是一個類別的實體(instance)**;所有的物件一定都會屬於某個類型
:::
### 物件服務 - 系統邊界
* 每個物件都會有特定的功能(為了解決某個問題),並對外提供解決問題的方法,並且每個子系統相互服務
* **服務邊界**:就是 **物件對外服務時可觸及的領域**,超過這個邊界就不在服務範圍內
**在設計軟體架構時,要清楚知道每個子系統的系統邊界在哪**
> 
### 物件封裝、透通
* **封裝**:**將大型實做拆分為一個個類**,其好處如下
1. 可以隱藏系統細節(就算你裡面用了 100 個私有方法,對外仍是提供 1 個方法),使用者不必知道細節
> 隱藏私有屬性、方法
2. 提供系統的獨立性,內部修改並不影響外部的界面合約
> 隱藏大多東西,對外提供乾淨的界面
3. 適當的封裝有利於重用性
4. 降低大型系統的風險(修復一個崩潰,結果不小心製造了 10 個崩況...之類的)
* **透通**:透通得意思其實是 **看不見**,**透通與封裝具有相同的概念**… 而以下這兩句話是相同的意思
* 物件封裝實現細節
* **物件的實現細節對使用者是通透的**
:::success
* 可以把透通記憶程玻璃,你看得見結果,但是看不見過程(那個過程就是玻璃)
:::
### 物件界面、抽象 - 多型
* **物件界面 `interface`**(純抽象方法):
**在現實世界中界面是一個實體**(像是插座、遙控、音響... 等等),但在 **軟體中界面是一種抽象服務**,它並 **不包括實做細節**;其特性如下
1. 物件導向中,界面 `interface` 是設計類與類之間鬆耦合的一個有效手段
2. 介面設計同時並且也保證個系統的 **可拓展性、穩定性**
:::info
* 在 Java 中,界面有兩種意義
* 意指系統的對外提供的服務(因為 interface 中只能有 public 方法)
* 能清晰的將系統的實現細節、界面宣告分離
:::
* **抽象 `abstract` 類、方法**:
**抽像是一種由具體到抽象、由複雜到簡潔的觀念**;抽象也意味著分析事物的功能(但並不包括實現)
> 與界面一樣,可以用 **劃分軟體設計、程式撰寫的界線**
:::warning
* **多型 & 抽象機制 & JVM**
以 JVM 語言抽相機制的開發人員在開發過程中,它是 JVM 運行時提供的 **動態連結機制**
:::
### 物件組合
* 組合就是將 **多個簡單的子系統組裝**,來達到業務邏輯需求的手段,它有以下優點;這裡我們回顧由上至下的分析:在建立物件模型時我們會…
1. 先分析、**識別問題領域**(宏觀)
2. **拆分細節**、決定物件粗粒度(微觀)
> 
* 組裝的好處如下
* 隔離子系統個別維護
* 隱藏系統的複雜性(封裝、透通)
* 提高重用性
## UML 簡介
UML (`Unified Modeling Language`) 是一種定義良好、不分語言、功能強大的 **視覺化建模語言**,其目標是為開發團隊題標準通用的物件導向設計通用語
而 UML 並不只有一種圖示,我們可以 **依照不同角色、狀況選用不同圖示(框圖)** ,主要 UML 分類包含如下
| UML 分類 | 說明 |
| - | - |
| 用例圖(`Use Case Diagram`) | 從 **使用者角度** 描述系統功能 |
| 類別圖(`Class Diagram`) | 描述物件模型中,**類與類之間的關係** |
| 序列圖(`Sequence Diagram`)、協作圖(`Cooperation Diagram`) | 描述物件之間的 **互動關係**,並強調 **時間順序** |
| 狀態轉換圖(`State Transition Diagram`) | 描述一個物件在某些條件下的狀態切換 |
| 組件圖(`Component Diagram`) | 以較上層的角度描述軟體元件之間的關係,也可以描述軟體程式的 結構圖 |
| 佈署圖(`Deployment Diagram`) | 描述軟體 **與硬體的物理結構** |
### 用例圖 - Use Case Diagram
* 用例圖(`Use Case Diagram`)包含以下內容
| 內容 | 說明 | 補充 |
| - | - | - |
| 角色 | 角色乃是 **系統邊界**,就是使用系統的使用者 | 用 **人形符號** 表示 |
| 用例 | 角色使用系統的某個功能 | 用 **橢圓符號** 標示 |
角色使用功能時(用例),或是功能呼叫令一個功能時,使用 **實線的箭頭符號表示**
:::info
* 在軟體分析功能(用例)的細節、處理流程,每個用例可以用 例檔 來描述,主要可以分為以下幾種:
1. 前置條件
2. 主流事件
3. 其他事件流:以當前分析的角度來看,除了主流程的都是其他事件流
4. 後置條件(不一定會有)
:::
> 
### 類別圖 - Class Diagram
* 類別圖(`Class Diagram`)是我們最常見的圖,它代表類與類之間的 **靜態結構**;它的內容如下
| 內容 | 說明 | 補充 |
| - | - | - |
| 類別 | 類別圖的主要元素 | 使用矩形表示,如果是抽象類則使用 *斜體字* 表示(抽象方法也一樣) |
| 類之間的關係 | 類的關係有很多種(下面說明) | 依照不同關係使用不同的線條 |
> 
### 序列圖、協作圖
* 序列圖描述了一次會描述出兩個為度重點
1. **水平為度**:
表示物件之間的呼叫過程(發送訊息的過程)
2. **垂直為度**:
表示呼叫方法的順序
> 
* 協作圖重點在描述 **角色之間的互動**
> 
:::warning
* **序列圖 vs. 協作圖**
**序列圖展示了物件與角色之間的是透過時間推移進行互動,而協作圖則不能參照時間**
:::
### 狀態轉換圖
* 有時候物件會因為某行行為切換自身的狀態,而如果那 **狀態切換較為複雜,就可以使用狀態圖描述**;而該圖的內容如下
重點是什麼「**行為**」,導致「**狀態的改變**」
| 內容 | 代表圖示 |
| - | - |
| 初始點 | 用實心圓表示 |
| 狀態轉換 | 用箭頭表示 |
| 狀態 | 用元腳矩形表示 |
| 中止點 | 用內部包含實心圓的圓表示 |
> 
### 組件圖
* 組件圖,主要是顯示 **軟體系統中元件(或是子系統)之間的關係**(用較高層級的角度分析軟體),常用在顯示與其他第三方元件(SDK, Library)的關係
> 
### 佈署圖
* 佈署圖表示的是,**軟體系統如何佈署到硬體環境中**,能夠展示 **軟體系統中的元件在硬體環境中的物理布局**,如下圖
| 內容 | 代表圖示 |
| - | - |
| 節點 | 三維立方體 |
> 
## UML 類別之間的關係
OOP 物件導向設計我們時常會使用到 UML 去描述類與類之間的關係,從類之間的關係去解偶合;所以先了解 UML 的表達方式,基礎有分為以下幾種
| 類關係 | 概述 |
| - | - |
| 關聯 | 兩個類之間存在一定的關係 |
| 依賴 | 為某類提供服務 |
| 聚集 | 相當於組合 |
| 泛化 | 繼承 |
| 實做 | 實做界面 |
### 類別 - 可見性的代表符號
* 這個符號可以用在 `Class`、`Method`、`Field`,它可以描述用來描述該類 (模型),可以描述對象目標的透明度 (使用類的 Scope、Visibility)
| 權限 | 符號 |
| -------- | -------- |
| public | + |
| protected | # |
| private | - |
| package | ~ |
```java=
// 符號範例
class StudentInfo {
public String name;
protected String nickName;
private int age;
int number;
}
```
> 
### 類別 Association - Field 關係
* `Association` 聯合 (單、雙向):就像 Class 中的 member(也就是 Field);關聯更多時候是指「**參照語意**」
1. **雙向 `Association`**
```java=
public class Teacher {
private Student student = new Student();
}
public class Student {
private Teacher teacher = new Teacher();
}
```
> 
2. **單向 `Association`**:有方向性
```java=
private class Info { }
public class Teacher {
private Info info = new Info();
}
public class Student {
private Teacher teacher = new Teacher();
}
```
> 
3. **自身 `Association`**:無箭頭
```java=
public class Student {
private Student next = new Student();
}
```
> 
4. **多維關聯 `Association`**:無箭頭
```java=
private class Info {
private Teacher t = new Teacher();
private Student s = new Student();
}
public class Teacher {
private Info info = new Info();
private Student s = new Student();
}
public class Student {
private Info info = new Info();
private Teacher t = new Teacher();
}
```
> 
### 類別 Dependency - 返回、呼叫、創建
* **`Dependency` 依賴 (使用關係)**:依賴關係有兩個大方向(它很容易跟關聯稿混,簡單記的話,只要記住關聯是 Class 內的 Field,其餘都是依賴)
:::info
* `Dependency` 的含義,在類別設計中使用依賴更有程式語法中的「**參照類型**」之意(Reference)
:::
1. 實體化某個類型(返回某個類型)
```java=
public class Engine { }
public class Car {
// Car 依賴 Engine(創建)
public Engine getEngine() {
return Engine();
}
}
```
2. 返回類型 & 呼叫某個類型的方法
```java=
public class Engine {
public int getPower() {
return 300; // 返回 int 類型
}
}
public class Car {
// Car 依賴 Engine(使用)
public String getEngineInfo(Engine e) {
return "Car power: " + e.getPower(); // 使用 Engine 類型
}
}
```
依賴者指向,依賴對象,**使用虛線實體箭頭**
> 
:::info
* 當然除了返回、呼叫、創建 之外還有像是「全局變數」、「靜態方法」、「局部變數」、「操作參數」都算是依賴
:::
### 泛化 Generalization
* **繼承類**:使用帶實線的 **空三角形箭頭** 表示繼承
```java=
abstract class Engine {
protected abstract int calPower();
}
class SmallEngine extends Engine {
protected int calPower() {
return 10;
}
}
```
> 
### 實現 Realization
* **實作介面**:使用 **線實體箭頭** 代表實做
```java=
interface IBook {
void addBook(Book book);
Book getBook(int index);
}
class ImplBook implement IBook {
public void addBook(Book book) {
// TODO:
}
public Book getBook(int index) {
// TODO:
}
}
```
> 
### 聚集 Aggregation
* **`Aggreation` 聚合 (`ByRef`、`ByValue`)**:
1. **`ByRef` 空菱形**:稱為 **聚合**,用來表示 `集體` & `個體` 之間的關係;有「參照語意」之意… 但是這種表示法在 UML 表達中屬於非必須的表示,可以依照情況適度展示
> **類拆開後仍各有存在意義**,類與類之間 **有獨立的生命週期,但是又會互相制衡**;
>
> eg. 檯燈、燈泡
```java=
public class Student { }
public class MyClass {
private Student[] students = new Student()[];
}
```
> 
2. **`ByValue` 實菱形**:又稱為 **複合 (又稱為組合、強聚集)**,用來表示 `個體` 與部分部件的關係 (該部件不可輕易分離,關係較緊密);有「值語意」之意
> **類拆卸後其存在意義不大**(以商業邏輯的角度去看)、有 **生命週期的牽連**(共存亡)
>
> e.g
> 1. 電路板、電阻、電容
> 2. 人類、心臟、頭、眼睛
```java=
public class Student {
private Heart heart;
private Head head;
private Eyes eyes;
}
```
> 
:::success
* 何時該用 `ByRef` 或是 `ByValue`?
這可以須根據 **場景、語意、商業邏輯** 的角度去分析
:::
## 其他延伸概念 Other
### 類 - 多重性 Multiplicity
* **多重性 (`Multiplicity`)**:
| 權限 | 符號 |
| -------- | -------- |
| 0 | 0 個 |
| 1 | 1 個 |
| * | 多個 |
| 0...* | 0 ~ 多個 |
| 1...* | 1 ~ 多個 |
1. **聚合的多重性表達**:在 UML 中,聚合表示整體對象可以包含部分對象,但它們不是強耦合的。以下是在 Java 中使用聚合關係表示多重性的範例程式碼
```java=
import java.util.List;
class WholeObject {
// 其他成員
// 聚合關係
private List<PartObject> parts;
public List<PartObject> getParts() {
return parts;
}
public void setParts(List<PartObject> parts) {
this.parts = parts;
}
}
class PartObject {
// 其他成員
}
public class AggregationExample {
public static void main(String[] args) {
WholeObject whole = new WholeObject();
// 創建部分對象
PartObject part1 = new PartObject();
PartObject part2 = new PartObject();
// 設定聚合關係
whole.setParts(List.of(part1, part2));
// 其他操作...
}
}
```
> 
2. **鏈結關係**:在 UML 中,鏈結關係表示類別之間的鏈接(可以是連接其他類、或是連接自身)。以下是在 Java 中使用鏈結關係表示多重性的範例程式碼
```java=
class Information {
private Information privateInformation;
}
```
> 
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)
* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)
* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::
### 創建模式 - Creation Patterns
* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)
* **創建模式 - `Creation Patterns`**:
創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節,並提供創建對象的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
:::success
* [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)
* [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)
* [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)
* [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
* [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)
* [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)
* [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
:::
### 行為模式 - Behavioral Patterns
* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)
* **行為模式 - `Behavioral Patterns`**:
行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
:::warning
* [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)
* [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)
* [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)
* [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)
* [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)
* [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)
* [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)
* [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)
* [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)
* [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)
* [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
:::
### 結構模式 - Structural Patterns
* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)
* **結構模式 - `Structural Patterns`**:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
:::danger
* [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)
* [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)
* [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)
* [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)
* [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)
* [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
:::
###### tags: `Java 設計模式` `基礎進階`