概述

  • UML (Unified Modeling Language) 是用於說明、視覺化、構建和編寫物件導向軟體系統的統一建模語言
  • 類別圖是用來描述系統中物件的類型及其之間的各種靜態關係

物件導向基本觀念

物件 (Object)

  • 物件導向程式設計的主體是物件,它們合作完成系統運作
  • 物件導向程式可視為多個具互動關係之物件的集合

類別 (Class) 與物件 (Object)

  • 類別:抽象的模板,定義相同特徵的一群物件

    • 不具備實際的值
    • 定義屬性和方法
  • 物件:由類別產生的具象實體

    • 又稱實例 (instance)
    • 具有實際的值
    • 相同類別的物件具有相同特性,但屬性內容可能不同

屬性 (Attribute) 與方法 (Method)

  • 屬性:定義類別的特徵,物件會有不同的屬性內容
  • 方法:定義類別的行為,雖然演算法相同,但執行結果可能不同

UML 類別圖

類別表示法

基本類別

┌─────────────────┐
│   ClassName     │
├─────────────────┤
│ -attribute1     │
│ #attribute2     │
│ +attribute3     │
├─────────────────┤
│ -method1()      │
│ #method2()      │
│ +method3()      │
└─────────────────┘

抽象類別

抽象類別的名稱使用斜體表示,或在名稱上方加上 {abstract} 標記

┌─────────────────┐
│  «abstract»     │
│   ClassName     │  或  斜體表示: ClassName
├─────────────────┤
│ -attribute1     │
│ #attribute2     │
├─────────────────┤
│ +method1()      │
│ +abstract1()    │  抽象方法也用斜體表示
└─────────────────┘

介面 (Interface)

介面可以用兩種方式表示:

  1. 使用標準類別符號但加上 «interface» 標籤
┌─────────────────┐
│  «interface»    │
│   InterfaceName │
├─────────────────┤
│                 │
├─────────────────┤
│ +method1()      │
│ +method2()      │
└─────────────────┘
  1. 使用圓圈表示法 (Lollipop Notation),特別用於表示一個類別實作多個介面時
     ○ InterfaceName
     │
┌────┼────────────┐
│   ClassName     │
├─────────────────┤
│                 │
└─────────────────┘

可見性符號

  • + : public
  • # : protected
  • - : private
  • ~ : package (僅限於同一package)

靜態成員

  • 靜態屬性或方法用底線標示,例如:+staticMethod()

從 Use Cases 找出物件/類別

  • 名詞/動詞分析:文字描述中的名詞可找出類別與屬性,動詞可找出操作
  • 將屬性與操作放入合適類別中
  • 設定類別間的關係

類別間的關係 (Relationship)

1. 相依 (Dependency) - “uses a”

  • X使用Y:X呼叫Y的method
  • 短期關係
  • 表示法:虛線箭頭 X ---> Y
  • 範例程式碼:
class X {
    void f1(Y y) {
        // X 使用 Y 作為參數
        y.foo();
    }
    
    void f2() {
        // X 在方法中建立並使用 Y
        Y y = new Y();
        y.foo();
    }
}

另一個範例:

class Car {
    private double weight;
    
    public void addBaggageWeight(Baggage baggage) {
        // Car 呼叫 Baggage 的方法,表示相依關係
        weight += baggage.getWeight();
    }
}

2. 關聯 (Association) - “knows a”

  • X熟識Y:X包含Y的reference
  • 長期關係
  • 表示法:實線箭頭 X ──→ Y
  • 範例程式碼:
class X {
    private Y y; // X 保持對 Y 的引用
    
    public X(Y y) {
        this.y = y;
    }
}

另一個範例 (Book 和 Author 的關聯):

class Book {
    private String name;
    private Author[] authors;  // 一對多的關聯
    
    public Book(String name) {
        this.name = name;
        this.authors = new Author[10];
    }
    
    void addAuthor(Author author) {
        // 實作新增作者的邏輯
    }
    
    void listAuthors() {
        for(int i = 0; i < authors.length; i++) {
            System.out.println(authors[i]);
        }
    }
}

3. 組合 (Composition) - “has a”

  • X包含Y,Y不可被其他物件包含
  • X與Y生命週期一致:若刪除X,則Y隨之被刪除
  • 表示法:實線+實心菱形 X ◆──→ Y
  • 範例程式碼:
class X {
    private Y[] y; // 組合關係,Y由X建立和管理
    
    public X() {
        y = new Y[10]; // 在建構子中建立Y對象
        // 當X被回收時,Y對象也會被回收
    }
}

另一個範例:

class Airplane {
    private Bitmap bitmap;  // Airplane和Bitmap形成組合關係
    private int x, y;
    
    void move() {
        x += 10;
        y += 20;
    }
    
    void draw() {
        bitmap.draw(x, y);
    }
}

4. 聚合 (Aggregation) - “has a”

  • X包含Y,但Y可被其他物件包含
  • 當X消失,Y仍然存在
  • 表示法:實線+空心菱形 X ◇──→ Y
  • 範例程式碼:
class Department {
    private ArrayList<Employee> employees; // 聚合關係
    
    // 部門通過引用Employee來建立關聯
    public void addEmployee(Employee employee) {
        employees.add(employee);
    }
    
    // 部門解散不會導致員工消失
    public void disband() {
        employees.clear();
    }
}

5. 繼承 (Inheritance) - “is a”

  • X繼承自Y
  • 表示法:實線+空心三角形 X ───▷ Y
  • 範例程式碼:
class Y {
    // 父類別
}
 
class X extends Y {
    // 子類別,繼承Y的屬性和方法
}

6. 實作 (Implementation) - “can do”

  • X實作Y的介面
  • 表示法:虛線+空心三角形 X ---▷ Y
  • 範例程式碼:
interface Comparable {
    int compareTo(Object obj);
}
 
class MyDate implements Comparable {
    // 實作Comparable介面
    public int compareTo(Object obj) {
        // 實作比較邏輯
        return 0;
    }
}
 
class MyTime implements Comparable {
    // 也實作Comparable介面
    public int compareTo(Object obj) {
        // 實作比較邏輯
        return 0;
    }
}

其他重要概念

抽象類別 (Abstract Class)

  • 無法產生物件,但可被繼承
  • 通常用於表示概念性的分類
  • 範例程式碼:
abstract class Vehicle {
    // 抽象類別無法直接實例化
    public abstract void turnLeft();
    public abstract void turnRight();
}
 
class Bike extends Vehicle {
    public void turnLeft() {
        // 腳踏車的左轉實作
    }
    
    public void turnRight() {
        // 腳踏車的右轉實作
    }
}

多型 (Polymorphism)

  • 不同的物件可以有相同的方法名稱,但呈現出不同行為
  • 是繼承可達成的重要效益
  • 範例程式碼:
class Client {
    double op1(Shape s) {
        // 多型:根據實際的Shape類型調用不同的getArea()實作
        double area = s.getArea();
        return area;
    }
}
 
abstract class Shape {
    public abstract double getArea();
}
 
class Rectangle extends Shape {
    private double length;
    private double width;
    
    public double getArea() {
        return length * width;
    }
}
 
class Circle extends Shape {
    private double radius;
    
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

實際建模步驟

  1. 識別所有類別
  2. 建立類別間的關係
  3. 設定屬性
  4. 設定方法

領域模型範例

以客戶訂單系統為例:

  • 客戶有公司客戶與個人客戶兩種 (繼承關係)
  • 客戶會下訂單買產品 (關聯關係)
  • 訂單包含產品資訊 (組合關係)
Customer ────→ Order ◇────→ ProductInfo
    △
    │
    ├─── CorporateCustomer
    │
    └─── PersonalCustomer

類別圖與軟體設計的定位

  • 從需求分析到程式碼的過程中,類別圖提供了從概念模型到實作的橋樑
  • 在領域驅動設計 (DDD) 中扮演重要角色
  • 可與其他UML圖形搭配使用,如使用案例圖、活動圖、循序圖等