關係

物件透過訊息(message)的傳遞至另一個物件,救代表了兩個物件之間,建立一種連線(link)的關係。觀察一段敘述:韓劇中的長今,因為當醫女要練習針灸的緣故,所以施針在一隻大頭吉娃娃身上。以 UML 物件圖(Object Diagram) 來表示如下圖:

範例—物件之間的連結關係
圖1、範例—物件之間的連結關係

上圖 1 的箭頭表示物件的可視性(visibility),說明長今(UML 物件圖內的物件名稱係加上底線表示之))物件傳送了 “針灸” 訊息給大頭吉娃娃物件。箭頭是否有可能為雙向? 當然有可能!例如,大頭吉娃娃因為被扎針後很痛而反過來咬了長今一口,如此兩個物件就構成了雙向關係,參考如下圖:

範例—物件之間的雙向連結
圖2、範例—物件之間的雙向連結

觀察物件之間的關係,再予以抽象化(abstract),也就是說再進而一步地來觀察這些物件所屬類別(class)之間的關係,依據其關係的性質與其結構性,可以發掘出類別之間的關係,分為三種:

  • 關連(Association)
  • 組成關係(整體—局部,Whole-parts)
  • 一般化—特殊化(Generalization—Specialization)

針對上述三種類別之間的關係,有必要個別來作詳細說明,並列舉範例、UML 語法與範例程式碼等。

 

結合關係(Association)

說明

「連結(link)」表示物件之間的關係;而「結合(association,或翻譯為關連)」就是將物件之間的「連線」關係抽象化,用來表示類別與類別之間的抽象關係。

結合代表著我們需要記憶類別之間的關係,通常是在某個情境當中,我們需要保存其相關資訊一段時間(視領域與需求而定,一般而言,可能保持幾秒到數十年的情況都有可能)。例如,以 「人(Persion)」與「狗(Dog)」這兩個類別來看,兩個類別是否有關係?若是我們塑模(Modeling)的對象為寵物店(Pet Shop)管理系統,那麼顯然這兩個類別就有值得保存兩者之間關係的必要性;但若開發的對象為人事管理系統,那麼,你又何必將「狗」這個類別與「人」類別關連起來,甚至,根本就沒有必要將「狗」類別給塑模包含至人事管理系統來的。

關於結合關係的 UML 表示法如下圖:

範例—結合關係的UML表示語法
圖3、範例—結合關係的UML表示語法

在結合關係內的每一個類別,都有其各自所擔任的角色(role),參考上圖 3,「Person」類別在與「Company」類別的結合關係中,是擔任了 “雇員” 的角色;反之,「Company」類別則是擔任著在這個結合關係中的 ”雇主” 角色,類別所擔任的角色,是表示在這個關連靠近類別的端點旁邊。

關連也可以為其命名,來凸顯兩個類別之間的一種行為。上圖 3的關連名稱被命名為 “雇用契約”,代表著「Person」類別與「Company」類別是一種 “雇用” 的結合關係。

範例

範例—駕駛員與汽車的結合關係

觀察周遭生活面面向,可以舉出太多一般性的類別與類別之間的結合關係。例如,”人開著汽車”,以這麼簡短的一句話,將其抽象化,就可以發現到,有兩個主要的類別:「人」與「汽車」,兩者必然存在著關係,否則,車子如何有可能會發動、行進呢?

UML 表示法

範例—Driver與Car 類別的關連UML表示法
圖4、範例—Driver與Car 類別的關連UML表示法

上圖 4表達了「Driver」類別與「Car」類別的 UML 表示法,其中,1 對 1…* 為多重性(multiplicity)的表示法,多重性定義了類別 A 會有多少個個體(instance)與類別 B 多少個個體會關連在一起。,上圖的多重性定義即表示為:一個駕駛員可以擁有1到多台車子。

Hint: 如何找出 “Car" 類別的屬性與操作?

從車子的角度來思考,車子應該知道什麼資訊(Knowing),以及應該負責那些行為(Doing)。請仔細的觀察,當你在開車時,你所看到的儀表板數據就是車子所應該知道的資訊,這些資訊就會成為「Car」類別的屬性;你所操作車子的動作,包括啟動、排檔、踩油門、轉動方向盤、煞車…等,就是車子所提供給駕駛的服務(service),也就是「Car」類別的操作(在程式語言的術語則稱為 “method”)。

Java 程式範例碼

原始碼下載

#001 public class Driver {
#002 
#003 	private String name;
#004 	private String 行照號碼;
#005 	public Car m_Car;
#006 
#007 	public String getName(){
#008 		return "駕駛員名稱:" + name;
#009 	}
#010 
#011 	public String get行照號碼(){
#012 		行照號碼 = "XXXX-XXXX-00128";
#013 		return 行照號碼;
#014 	}
#015 }
#001 public class Car {
#002 
#003 	private String 型號;
#004 	private String 車號;
#005 	private float 里程數;
#006 	private int 油表數;
#007 
#008 	public float get里程數(){
#009 		//需至里程記錄器取得資料
#010 		里程數 = 58000; //先給個測試數值
#011 		return 里程數;
#012 	}
#013 
#014 	public int get油表數(){
#015 		油表數 = 12; //剩餘 12 公升
#016 		return 油表數;
#017 	}
#018 	
#019 	public String get車況資訊(){
#020 		型號 = "蛋龜二號";
#021 		車號 = "GY-8228";
#022 		String 車況 = "型號:" + 型號
#023 		  + ";車號:" + 車號;
#024 		return 車況;
#025 	}
#026 
#027 	public String 踩油門(){
#028 		return "動作: 踩油門";
#029 	}
#030 
#031 	public String 排檔(String 排檔別){
#032 		return "動作: 排檔-> " + 排檔別;
#033 	}
#034 
#035 	public String 開啟(){
#036 		return "動作: 開啟";
#037 	}
#038 
#039 	public String 煞車(){
#040 		return "動作: 煞車";
#041 	}
#042 
#043 	public String 關閉(){
#044 		return "動作: 關閉";
#045 	}
#046 
#047 	public String 轉動方向盤(){
#048 		return "動作: 轉動方向盤";
#049 	}
#050 }

為了測試 “Car” 類別所提供的 method,我們可以先設計一個測試用的 Java stand-alone 的程式,以站在 “Driver” 的角度,來看車子的操作:

#001 public class TestCar {
#002 	public static void main(String[] args) {
#003 		Car m_car = new Car();
#004 		
#005 		System.out.println(m_car.開啟());
#006 		System.out.println(m_car.排檔("D"));
#007 		System.out.println(m_car.踩油門());
#008 		System.out.println(m_car.get油表數());
#009 		System.out.println(m_car.get里程數());
#010 		System.out.println(m_car.關閉());
#011 	}
#012 }

執行結果如下:

動作: 開啟
動作: 排檔-> D
動作: 踩油門
12
58000.0
動作: 關閉