Java程序員的角度闡述UML和對象建模問題,是一個深入淺出的實用性介紹。 雖然從歷史和基本理念方面來探討UML非常吸引人,但我們還是直接從Java代碼開 始,看看UML如何描述Java類,再在敘述過程中插入一些歷史和基本理念方面的知 識。
UML類圖
在Java中,我們用下面的代碼聲明兩個公用類,每一個Java類放入一個文件, 文件的名字就是Java類的名字加上擴展名.java:
public class Person{}
public class Organization{}
UML是Unified Modeling Language的縮寫,即“統一建模語言”。與Java不同 ,UML是一種圖形化的建模“語言”,它用一個矩形來表示一個類,在矩形的內部 寫上類的名稱,一個類圖可以放入多個類。用矩形表示類,是UML中U(Unified) 起的作用。在UML的第一個版本出現,每一個對象建模專家都有自己的一套符號, 一些人用點表示類,一些人用圓圈表示類,還有一些人用圓角矩形表示類。顯然 ,這很容易引起混亂。後來,Rational公司的三個專家——Grady Booch、James Raumbaugh、Ivar Jacobson達成了一致意見,同意“統一”他們各自使用的符號 ,UML終於創立,符號之爭也終於落下了帷幕。圖一就是上面兩個Java類的UML類 圖:
圖一 有二個類的簡單類圖
如果要描述一系列類的內部結構以及它們相互之間的關系,UML類圖是非常有 用的。例如,在許多書籍中,我們可以看到作者用類圖來描述各個類之間的關系 。
顯然,空的類沒有什麼實際意義。我們要為Person類加上一些實例變量和簡單 的方法。下面是Person類的代碼,已經過簡化處理,不含任何注釋:
public class Person {
private String name;
private String socialSecurityNumber;
private Date dateOfBirth;
private String emailAddress;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSocialSecurityNumber()
{ return socialSecurityNumber; }
public void setSocialSecurityNumber(String socialSecurityNumber)
{ this.socialSecurityNumber = socialSecurityNumber; }
public Date getDateOfBirth() { return dateOfBirth; }
public void setDateOfBirth(Date dateOfBirth)
{ this.dateOfBirth = dateOfBirth; }
public int calcAgeInYears()
{/*not implemented yet*/return 0;}
}
圖二顯示了Person類的UML圖。可以看到,UML用“+”和“-”符號分別表示 public和private修飾符。UML只顯示操作和屬性類型之類的特征信息,操作的結 果在行未的冒號之後聲明。
圖二 在UML類圖中描述屬性和方法
由於UML類圖不包含方法的具體實現,所以在UML類圖中查看屬性和方法等基本 信息要比直接查看Java源代碼更方便一些。在創建UML圖時,人們常常忽略或隱藏 各種細節信息,以便查看和掌握類的整體結構。例如,UML類圖常常只顯示出屬性 和操作的名稱,簡單的訪問器方法(諸如getXXX()、setXXX()之類的方法)也常 常不顯示出來。圖三就是簡化圖二得到的結果。
圖三 經過簡化的Person類UML圖
圖三清楚地顯示出了Person類主要的屬性和方法。但是,單個類的UML圖還不 是很有用。只有包含多個類且描述了多個類之間關系的UML圖,才具有實用意義。 UML用兩個類之間的連線來表示兩者之間的關系,不同的線型表示不同的關系,在 UML類圖中最常見的關系是關聯關系。
關聯關系
前面Person類的屬性都是簡單類型(Primitive Type),或者說是Java直接提 供的標准類型。現在來考慮下面的代碼片斷,它增加了一個對Organization實例 的引用:
public class Person {...
private Organization employer;
...
}
引用的名稱是employer,意味著這裡的Organization代表著Person的雇主。圖 四顯示了如何在UML中描述這種關系:
圖四 兩個類之間的關聯關系
兩個類之間的連線表示Person類對Organization類有一種依賴關系。這條線是 一條實線(而不是虛線),表示這種依賴關系是一種關聯。
如有必要,關聯關系可顯示出角色、多重性、關聯方向等屬性。圖四的關聯關 系顯示出Organization對象在該關系中是雇主的角色,“0..1”表示每一個 Person類的對象最多和一個Organization類的對象有關系,也可能和0個 Organization對象有關系(即Person對Organization的引用可設置為null)。開 叉的箭頭表示Person類擁有對Organization的引用,而不是Organization擁有對 Person的引用。
多重性
多重性回答這樣一個問題:在一個關系中,每個類各有多少對象參與其中?常 見的多重性如圖五所示。
圖五 常見的多重性及其含義
前面我們已經看到了Java代碼中0..1多重性的實例。不難猜測,多重性為“1 ”意味著一個類對另一個類的引用不能為null(一般是這樣一種情形:引用的值 在構造函數中初始化,且在所有相關的set方法中禁止把該引用設置為null)。
值為“1..*”和“0..*”的多重性稍微復雜一點。在Java中,實現這類多重性 的途徑之一是使用某種集合類,例如Vector,來保存可能需要用到的多個引用:
多重性回答這樣一個問題:在一個關系中,每個類各有多少對象參與其中?常 見的多重性如圖五所示。前面我們已經看到了Java代碼中0..1多重性的實例。不 難猜測,多重性為“1”意味著一個類對另一個類的引用不能為null(一般是這樣 一種情形:引用的值在構造函數中初始化,且在所有相關的set方法中禁止把該引 用設置為null)。值為“1..*”和“0..*”的多重性稍微復雜一點。在Java中, 實現這類多重性的途徑之一是使用某種集合類,例如Vector,來保存可能需要用 到的多個引用:
ClassA{
...
Vector classB; // 保存B類對象引用的Vector
...
}
對於“1..*”多重性,Java程序必須確保Vector至少包含一項內容。
在有些關系中,多重性的值可以是某個精確的范圍或數字。例如,一個小孩最 多有兩個生物學意義上的健在雙親,即它的多重性可表示為“0..2”。用Java代 碼描述這種關系時,程序必須帶有確保Parent對象實例少於或等於2個的約束。
一個UML關聯加上多重性、角色、關聯方向之後,能夠描述出大量信息,遠比 一大堆Java源代碼簡潔和直觀。雖然UML圖沒有說明關系的具體實現方式,但它能 夠充分地說明關系的意義和作用。圖六顯示了標注多重性、角色名稱之後的雇傭 關系,它表示一個Person可以為多個Organization工作,一個Organization可以 雇傭多個Person。
圖六 雙向關聯關系
聚合與合成
關聯只是UML中的關系之一。下面我們來看看UML中的其他兩種關系——聚合( Aggregation)和合成(Composition),它們實際上是關聯關系的不同變種。聚合 是這樣一種關聯關系,在這種關系中一個類的對象代表著另一個類的對象的一部 分,有的人因此也把聚合關系叫做“全體-部分”關系。聚合關系用實線空心菱形 箭頭表示,箭頭由表示Part的類指向表示Whole的類,參見圖七。
圖七 聚合與合成
那麼,在Java程序中聚合關系又是什麼樣的呢?答案是:這要看你在問誰。聚 合是一個有爭議的概念,表達的是一種生命周期依賴關系。有人根據習慣認為, 聚合意味著Whole類必須負責創建和拆除Part類的對象;但也有人為聚合關系下了 更寬松的定義。到底應該怎麼理解,你最好能夠在合作者之間取得一致意見和約 定,避免混淆。
合成是一種較強的聚合關系。這兩種關系基本相似,不同之處在於,在合成關 系中,Part的對象任何時候只能從屬於一個Whole對象,也就是說,必須用Java代 碼確保這種唯一的從屬關系。
前面我們已經看到,類的屬性、操作以及各個類之間的關系可以用UML類圖來 描述。然而,對於Java類裡面的對象引用,什麼時候應該把它當作關聯關系、什 麼時候把它當作屬性,這一點還沒有搞清楚。答案是:要在哪一個層次交流信息 ,UML圖就應該具體到哪一個層次。有些時候,即使是簡單的對象,也最好畫出它 的類圖,把其他類對它的引用描述成關聯關系;另一些時候,可能需要把對象引 用表示成屬性,甚至從類圖完全省略對該對象的引用,以便在類圖中突出顯示其 他更重要的類和關系。大多數的UML工具軟件都提供了隱藏UML類圖各種細節信息 的機制。
獲得UML圖一般有兩種辦法,手工設計UML圖(在此基礎上可由UML工具生成 Java應用的骨架代碼),或者用工具分析Java源代碼(甚至字節碼)獲得UML圖。 一些優秀的UML工具能夠在你繪制UML圖的同時生成Java代碼,在你編輯Java代碼 的同時更新UML圖。例如TogetherSoft的Together ControlCentre,本文的UML圖 就是用這個工具繪制的,有免費版Together Community Edition可供試用。