從類別圖了解類別之間的依賴關係

物件導向程式設計,是在設計一個系統--用到多少類別,以及類別之間的關係。

類別圖(class diagram)可以用圖例的方法呈現類別之間的關係,正所謂一圖勝萬言,使用圖例表達類別的關係,比直接看程式碼來的清楚很多;因為類別圖很常用,所以學習物件導向程式設計,至少要懂得看類別圖。

以下使用 PlantUML 作為畫圖的工具

首先先看單一類別是如何呈現資訊:

@startuml
skinparam classAttributeIconSize 0
class Person {
.. public property ..
+ age : int
.. private property ..
- height : float
.. protected property ..
# name : string
.. package property ..
~ weight : float
==
.. public method ..
+ getAge() : int
.. private method ..
- getHeight() : float
.. protected method ..
# setName(String name) : void
.. package method ..
~ getWeight() : float
}
@enduml

一個類別可以分作四個部分:

  • 類型,比方說是介面(interface)或類別(class),上例為類別
  • 名稱,上例為 Person
  • 屬性(property)
  • 方法(method)

其中屬性和方法會有不同的能見度。而能見度的表示方法如下:

  • + public
  • - private
  • # protected
  • ~ package

了解單一類別的表示法,再來是了解類別之間關係。類別圖定義了六種關係,以下為簡單的說明。

依賴(Dependency)

類別間的依賴,表示了 A 類別使用了 B 類別,如下圖:

@startuml
scale 200 width
class A
class B
A .> B
@enduml

在程式上的意義,指的是 A 類別的方法定義裡,有 B 類別。比方說,Person 類別使用信用卡買 Switch,用 PHP 程式語言表達的方法如下:

class Person {
public function buy(CreditCard $card): Switch {}
}

對應的類別圖如下:

@startuml
class Person {
+ buy(CreditCard) : Switch
}
class CreditCard
class Switch
Person ..> CreditCard
Person ..> Switch
@enduml

這個程式寫法很常出現在靜態方法上。

關聯(Association)

關聯表示的是 A 類別擁有 B 類別,如下圖:

@startuml
scale 200 width
class A
class B
A -> B
@enduml

程式上的意義,指的就是類別的屬性,比方說 Person 類別有 cellphone 屬性是 Cellphone 類別。PHP 程式語言表達的方法如下:

class Person {
public $cellphone;
}

對應的類別圖如下:

@startuml
class Person {
+ cellphone : Cellphone
}
Person --> Cellphone
@enduml

聚合(Aggregation)

聚合表示的關係是 A 類別包含了 B 類別,如下圖:

@startuml
scale 200 width
class A
class B
A o-> B
@enduml

程式上的意義有下面幾個:

  1. A 類別的屬性有 B 類別
  2. A 類別的方法會用到 B 的屬性
  3. A 類別與 B 類別的生命週期是各自獨立的,比方說 B 類別產生的物件可同時被另一個 A 類別產生的物件聚合。

第三點可能有點抽象,舉個實例:如 Switch 與 JoyCon 的關係就是一個聚合最佳實例,JoyCon 不限定用在哪台主機,只要是 Switch,裝上去都能正常操作。而且兩者生命週期是各自獨立的,JoyCon 壞了可以買新的裝上;Switch 主機壞了,JoyCon 可以留到下一台主機繼續用。

畫成類別圖如下:

@startuml
class Switch {
+ joycon1 : JoyCon
+ joycon2 : JoyCon
}
class JoyCon

Switch o--> JoyCon
@enduml

組合(Composition)

組合代表的意義為,B 類別為 A 類別的一部分,如下圖

@startuml
scale 200 width
class A
class B
A *-> B
@enduml

與聚合的條件類似,唯獨第三點不同:A 類別與 B 類別的生命週期是一致的,也就是要嘛一起產生,要嘛一起死。

實務上來說,Macbook 與記憶體(Memory)的關係即為組合:如果一個壞,另一個也難逃一死。而過去較舊的機型則是聚合,因為可以抽換。

畫成類別圖如下:

@startuml
class Macbook {
- memory : Memory
}
class Memory
Macbook *--> Memory
@enduml

實作(Realization)與繼承(Generalization)就不特別說明,因為跟物件導向討論的大同小異,對應的圖如下:

實作:

@startuml
scale 200 width
class A
class B
A .|> B
@enduml

繼承:

@startuml
scale 200 width
class A
class B
A -|> B
@enduml

小結

一共有六種關係,下面是一個關係由弱到強的表格

關係 描述
依賴 A use a B
關聯 A has a B
聚合 A owns a B
組合 B is part of A
實作 B implement A
繼承 B extends A

越弱的關係,在修改 B 的程式碼,影響 A 的風險就越小。

了解類別圖與這個比較表的目的有兩個:

  1. 這是共同語言(UML)。
  2. 知道有不同的關係後,才知道如何比較不同的程式碼,進而達到改善的目的。