天天看點

深入淺出UML類圖---寫得不錯,備忘

九種關系總結,EA圖中會用到:

關聯關系(Association):雙向關聯,單向關聯,自關聯、多重性關聯Multiplicity、

聚合(Aggregation):整體與部分的關系,整體對象銷毀時成員對象不銷毀,一般是構造函數或Set方法傳入成員對象。

組合(Composition):整體與部分的關系,整體對象銷毀時成員對象一并銷毀,一般在構造函數中建立成員對象。

依賴關系(Dependency):Driver類依賴Car類的move方法,Driver--->Car

泛化關系(Generalization):父類與子類之間,由子類指向父類

實作關系(Realization):接口與實作類之間,由實作類指向接口

作者:劉偉 ,釋出于:2012-11-23,來源:CSDN

http://www.uml.org.cn/oobject/201211231.asp

在UML 2.0的13種圖形中,類圖是使用頻率最高的UML圖之一。Martin Fowler在其著作《UML Distilled: A Brief Guide to the Standard Object Modeling Language, Third Edition》(《UML精粹:标準對象模組化語言簡明指南(第3版)》)中有這麼一段:“If someone were to come up to you in a dark alley and say, 'Psst, wanna

see a UML diagram?' that diagram would probably be a class diagram. The majority of UML diagrams I see are class diagrams.”(“如果有人在黑暗的小巷中向你走來并對你說:‘嘿,想不想看一張UML圖?’那麼這張圖很有可能就是一張類圖,我所見過的大部分的UML圖都是類圖”),由此可見類圖的重要性。

類圖用于描述系統中所包含的類以及它們之間的互相關系,幫助人們簡化對系統的了解,它是系統分析和設計階段的重要産物,也是系統編碼和測試的重要模型依據。

1. 類

類(Class)封裝了資料和行為,是面向對象的重要組成部分,它是具有相同屬性、操作、關系的對象集合的總稱。在系統中,每個類都具有一定的職責,職責指的是類要完成什麼樣的功能,要承擔什麼樣的義務。一個類可以有多種職責,設計得好的類一般隻有一種職責。在定義類的時候,将類的職責分解成為類的屬性和操作(即方法)。類的屬性即類的資料職責,類的操作即類的行為職責。設計類是面向對象設計中最重要的組成部分,也是最複雜和最耗時的部分。

在軟體系統運作時,類将被執行個體化成對象(Object),對象對應于某個具體的事物,是類的執行個體(Instance)。

類圖(Class Diagram)使用出現在系統中的不同類來描述系統的靜态結構,它用來描述不同的類以及它們之間的關系。

在系統分析與設計階段,類通常可以分為三種,分别是實體類(Entity Class)、控制類(Control Class)和邊界類(Boundary Class),下面對這三種類加以簡要說明:

(1) 實體類:實體類對應系統需求中的每個實體,它們通常需要儲存在永久存儲體中,一般使用資料庫表或檔案來記錄,實體類既包括存儲和傳遞資料的類,還包括操作資料的類。實體類來源于需求說明中的名詞,如學生、商品等。

(2) 控制類:控制類用于展現應用程式的執行邏輯,提供相應的業務操作,将控制類抽象出來可以降低界面和資料庫之間的耦合度。控制類一般是由動賓結構的短語(動詞+名詞)轉化來的名詞,如增加商品對應有一個商品增加類,注冊對應有一個使用者注冊類等

(3) 邊界類:邊界類用于對外部使用者與系統之間的互動對象進行抽象,主要包括界面類,如對話框、視窗、菜單等。

在面向對象分析和設計的初級階段,通常首先識别出實體類,繪制初始類圖,此時的類圖也可稱為領域模型,包括實體類及其它們之間的互相關系。

2. 類的UML圖示

在UML中,類使用包含類名、屬性和操作且帶有分隔線的長方形來表示,如定義一個Employee類,它包含屬性name、age和email,以及操作modifyInfo(),在UML類圖中該類如圖1所示:

深入淺出UML類圖---寫得不錯,備忘

圖1 類的UML圖示

圖1對應的Java代碼片段如下:

在UML類圖中,類一般由三部分組成:

(1) 第一部分是類名:每個類都必須有一個名字,類名是一個字元串。

(2) 第二部分是類的屬性(Attributes):屬性是指類的性質,即類的成員變量。一個類可以有任意多個屬性,也可以沒有屬性

UML規定屬性的表示方式為:

可見性 名稱:類型 [ = 預設值 ]

其中:

“可見性”表示該屬性對于類外的元素而言是否可見,包括公有(public)、私有(private)和受保護(protected)三種,在類圖中分别用符号+、-和#表示。

“名稱”表示屬性名,用一個字元串表示。

“類型”表示屬性的資料類型,可以是基本資料類型,也可以是使用者自定義類型。

“預設值”是一個可選項,即屬性的初始值。

(3) 第三部分是類的操作(Operations):操作是類的任意一個執行個體對象都可以使用的行為,是類的成員方法。

UML規定操作的表示方式為:

可見性 名稱(參數清單) [ : 傳回類型]

“可見性”的定義與屬性的可見性定義相同。

“名稱”即方法名,用一個字元串表示。

“參數清單”表示方法的參數,其文法與屬性的定義相似,參數個數是任意的,多個參數之間用逗号“,”隔開。

“傳回類型”是一個可選項,表示方法的傳回值類型,依賴于具體的程式設計語言,可以是基本資料類型,也可以是使用者自定義類型,還可以是空類型(void),如果是構造方法,則無傳回類型。

在類圖2中,操作method1的可見性為public(+),帶入了一個Object類型的參數par,傳回值為空(void);操作method2的可見性為protected(#),無參數,傳回值為String類型;操作method3的可見性為private(-),包含兩個參數,其中一個參數為int類型,另一個為int[]類型,傳回值為int類型。

深入淺出UML類圖---寫得不錯,備忘

圖2 類圖操作說明示意圖

由于在Java語言中允許出現内部類,是以可能會出現包含四個部分的類圖,如圖3所示:

深入淺出UML類圖---寫得不錯,備忘

圖3 包含内部類的類圖

類與類之間的關系(1)

在軟體系統中,類并不是孤立存在的,類與類之間存在各種關系,對于不同類型的關系,UML提供了不同的表示方式。

關聯(Association)關系是類與類之間最常用的一種關系,它是一種結構化關系,用于表示一類對象與另一類對象之間有聯系,如汽車和輪胎、師傅和徒弟、班級和學生等等。在UML類圖中,用實線連接配接有關聯關系的對象所對應的類,在使用Java、C#和C++等程式設計語言實作關聯關系時,通常将一個類的對象作為另一個類的成員變量。在使用類圖表示關聯關系時可以在關聯線上标注角色名,一般使用一個表示兩者之間關系的動詞或者名詞表示角色名(有時該名詞為執行個體對象名),關系的兩端代表兩種不同的角色,是以在一個關聯關系中可以包含兩個角色名,角色名不是必須的,可以根據需要增加,其目的是使類之間的關系更加明确。

如在一個登入界面類LoginForm中包含一個JButton類型的注冊按鈕loginButton,它們之間可以表示為關聯關系,代碼實作時可以在LoginForm中定義一個名為loginButton的屬性對象,其類型為JButton。如圖1所示:

深入淺出UML類圖---寫得不錯,備忘

圖1 關聯關系執行個體

在UML中,關聯關系通常又包含如下幾種形式:

(1) 雙向關聯

預設情況下,關聯是雙向的。例如:顧客(Customer)購買商品(Product)并擁有商品,反之,賣出的商品總有某個顧客與之相關聯。是以,Customer類和Product類之間具有雙向關聯關系,如圖2所示:

深入淺出UML類圖---寫得不錯,備忘

圖2 雙向關聯執行個體

圖2對應的Java代碼片段如下:

(2) 單向關聯

類的關聯關系也可以是單向的,單向關聯用帶箭頭的實線表示。例如:顧客(Customer)擁有位址(Address),則Customer類與Address類具有單向關聯關系,如圖3所示:

深入淺出UML類圖---寫得不錯,備忘

圖3 單向關聯執行個體

圖3對應的Java代碼片段如下:

(3) 自關聯

在系統中可能會存在一些類的屬性對象類型為該類本身,這種特殊的關聯關系稱為自關聯。例如:一個節點類(Node)的成員又是節點Node類型的對象,如圖4所示:

深入淺出UML類圖---寫得不錯,備忘

圖4 自關聯執行個體

圖4對應的Java代碼片段如下:

(4) 多重性關聯

多重性關聯關系又稱為重數性(Multiplicity)關聯關系,表示兩個關聯對象在數量上的對應關系。在UML中,對象之間的多重性可以直接在關聯直線上用一個數字或一個數字範圍表示。

對象之間可以存在多種多重性關聯關系,常見的多重性表示方式如表1所示:

表1 多重性表示方式清單

表示方式

多重性說明

1..1

表示另一個類的一個對象隻與該類的一個對象有關系

0..*

表示另一個類的一個對象與該類的零個或多個對象有關系

1..*

表示另一個類的一個對象與該類的一個或多個對象有關系

0..1

表示另一個類的一個對象沒有或隻與該類的一個對象有關系

m..n

表示另一個類的一個對象與該類最少m,最多n個對象有關系 (m≤n)

例如:一個界面(Form)可以擁有零個或多個按鈕(Button),但是一個按鈕隻能屬于一個界面,是以,一個Form類的對象可以與零個或多個Button類的對象相關聯,但一個Button類的對象隻能與一個Form類的對象關聯,如圖5所示:

深入淺出UML類圖---寫得不錯,備忘

圖5 多重性關聯執行個體

圖5對應的Java代碼片段如下:

(5) 聚合關系

聚合(Aggregation)關系表示整體與部分的關系。在聚合關系中,成員對象是整體對象的一部分,但是成員對象可以脫離整體對象獨立存在。在UML中,聚合關系用帶空心菱形的直線表示。例如:汽車發動機(Engine)是汽車(Car)的組成部分,但是汽車發動機可以獨立存在,是以,汽車和發動機是聚合關系,如圖6所示:

深入淺出UML類圖---寫得不錯,備忘

圖6 聚合關系執行個體

在代碼實作聚合關系時,成員對象通常作為構造方法、Setter方法或業務方法的參數注入到整體對象中,圖6對應的Java代碼片段如下:

(6) 組合關系

組合(Composition)關系也表示類之間整體和部分的關系,但是在組合關系中整體對象可以控制成員對象的生命周期,一旦整體對象不存在,成員對象也将不存在,成員對象與整體對象之間具有同生共死的關系。在UML中,組合關系用帶實心菱形的直線表示。例如:人的頭(Head)與嘴巴(Mouth),嘴巴是頭的組成部分之一,而且如果頭沒了,嘴巴也就沒了,是以頭和嘴巴是組合關系,如圖7所示:

深入淺出UML類圖---寫得不錯,備忘

圖7 組合關系執行個體

在代碼實作組合關系時,通常在整體類的構造方法中直接執行個體化成員類,圖7對應的Java代碼片段如下:

類與類之間的關系(2)

依賴(Dependency)關系是一種使用關系,特定事物的改變有可能會影響到使用該事物的其他事物,在需要表示一個事物使用另一個事物時使用依賴關系。大多數情況下,依賴關系展現在某個類的方法使用另一個類的對象作為參數。在UML中,依賴關系用帶箭頭的虛線表示,由依賴的一方指向被依賴的一方。例如:駕駛員開車,在Driver類的drive()方法中将Car類型的對象car作為一個參數傳遞,以便在drive()方法中能夠調用car的move()方法,且駕駛員的drive()方法依賴車的move()方法,是以類Driver依賴類Car,如圖1所示:

深入淺出UML類圖---寫得不錯,備忘

圖1 依賴關系執行個體

在系統實施階段,依賴關系通常通過三種方式來實作,第一種也是最常用的一種方式是如圖1所示的将一個類的對象作為另一個類中方法的參數,第二種方式是在一個類的方法中将另一個類的對象作為其局部變量,第三種方式是在一個類的方法中調用另一個類的靜态方法。圖1對應的Java代碼片段如下:

泛化(Generalization)關系也就是繼承關系,用于描述父類與子類之間的關系,父類又稱作基類或超類,子類又稱作派生類。在UML中,泛化關系用帶空心三角形的直線來表示。在代碼實作時,我們使用面向對象的繼承機制來實作泛化關系,如在Java語言中使用extends關鍵字、在C++/C#中使用冒号“:”來實作。例如:Student類和Teacher類都是Person類的子類,Student類和Teacher類繼承了Person類的屬性和方法,Person類的屬性包含姓名(name)和年齡(age),每一個Student和Teacher也都具有這兩個屬性,另外Student類增加了屬性學号(studentNo),Teacher類增加了屬性教師編号(teacherNo),Person類的方法包括行走move()和說話say(),Student類和Teacher類繼承了這兩個方法,而且Student類還新增方法study(),Teacher類還新增方法teach()。如圖2所示:

深入淺出UML類圖---寫得不錯,備忘

圖2 泛化關系執行個體

4. 接口與實作關系

在很多面向對象語言中都引入了接口的概念,如Java、C#等,在接口中,通常沒有屬性,而且所有的操作都是抽象的,隻有操作的聲明,沒有操作的實作。UML中用與類的表示法類似的方式表示接口,如圖3所示:

深入淺出UML類圖---寫得不錯,備忘

圖3 接口的UML圖示

接口之間也可以有與類之間關系類似的繼承關系和依賴關系,但是接口和類之間還存在一種實作(Realization)關系,在這種關系中,類實作了接口,類中的操作實作了接口中所聲明的操作。在UML中,類與接口之間的實作關系用帶空心三角形的虛線來表示。例如:定義了一個交通工具接口Vehicle,包含一個抽象操作move(),在類Ship和類Car中都實作了該move()操作,不過具體的實作細節将會不一樣,如圖4所示:

深入淺出UML類圖---寫得不錯,備忘

圖4 實作關系執行個體

實作關系在程式設計實作時,不同的面向對象語言也提供了不同的文法,如在Java語言中使用implements關鍵字,而在C++/C#中使用冒号“:”來實作。圖4對應的Java代碼片段如下:

執行個體分析1——登入子產品

某基于C/S的即時聊天系統登入子產品功能描述如下:

使用者通過登入界面(LoginForm)輸入賬号和密碼,系統将輸入的賬号和密碼與存儲在資料庫(User)表中的使用者資訊進行比較,驗證使用者輸入是否正确,如果輸入正确則進入主界面(MainForm),否則提示“輸入錯誤”。

根據以上描述繪制初始類圖。

參考解決方案:

參考類圖如下:

深入淺出UML類圖---寫得不錯,備忘

考慮到系統擴充性,在本執行個體中引入了抽象資料通路接口IUserDAO,再将具體資料通路對象注入到業務邏輯對象中,可通過配置檔案(如XML檔案)等方式來實作,将具體的資料通路類類名存儲在配置檔案中,如果需要更換新的具體資料通路對象,隻需修改配置檔案即可,原有程式代碼無須做任何修改。

類說明:

類 名

說 明

LoginForm

登入視窗,省略界面元件和按鈕事件處理方法(邊界類)

LoginBO

登入業務邏輯類,封裝實作登入功能的業務邏輯(控制類)

IUserDAO

抽象資料通路類接口,聲明對User表的資料操作方法,省略除查詢外的其他方法(實體類)

UserDAO

具體資料通路類,實作對User表的資料操作方法,省略除查詢外的其他方法(實體類)

MainForm

主視窗(邊界類)

方法說明:

方法名

LoginForm類的LoginForm()方法

LoginForm構造函數,初始化執行個體成員

LoginForm類的validate()方法

界面類的驗證方法,通過調用業務邏輯類LoginBO的validate()方法實作對使用者輸入資訊的驗證

LoginBO類的validate()方法

業務邏輯類的驗證方法,通過調用資料通路類的findUserByAccAndPwd()方法驗證使用者輸入資訊的合法性

LoginBO類的setIUserDAO()方法

Setter方法,在業務邏輯對象中注入資料通路對象(注意:此處針對抽象資料通路類程式設計

IUserDAO接口的findUserByAccAndPwd()方法

業務方法聲明,通過使用者賬号和密碼在資料庫中查詢使用者資訊,判斷該使用者身份的合法性

UserDAO類的findUserByAccAndPwd()方法

業務方法實作,實作在IUserDAO接口中聲明的資料通路方法

執行個體分析2——注冊子產品

某基于Java語言的C/S軟體需要提供注冊功能,該功能簡要描述如下:

使用者通過注冊界面(RegisterForm)輸入個人資訊,使用者點選“注冊”按鈕後将輸入的資訊通過一個封裝使用者輸入資料的對象(UserDTO)傳遞給操作資料庫的資料通路類,為了提高系統的擴充性,針對不同的資料庫可能需要提供不同的資料通路類,是以提供了資料通路類接口,如IUserDAO,每一個具體資料通路類都是某一個資料通路類接口的實作類,如OracleUserDAO就是一個專門用于通路Oracle資料庫的資料通路類。

根據以上描述繪制類圖。為了簡化類圖,個人資訊僅包括賬号(userAccount)和密碼(userPassword),且界面類無需涉及界面細節元素。

在以上功能說明中,可以分析出該系統包括三個類和一個接口,這三個類分别是注冊界面類RegisterForm、使用者資料傳輸類UserDTO、Oracle使用者資料通路類OracleUserDAO,接口是抽象使用者資料通路接口IUserDAO。它們之間的關系如下:

(1) 在RegisterForm中需要使用UserDTO類傳輸資料且需要使用資料通路類來操作資料庫,是以RegisterForm與UserDTO和IUserDAO之間存在關聯關系,在RegisterForm中可以直接執行個體化UserDTO,是以它們之間可以使用組合關聯。

(2) 由于資料庫類型需要靈活更換,是以在RegisterForm中不能直接執行個體化IUserDAO的子類,可以針對接口IUserDAO程式設計,再通過注入的方式傳入一個IUserDAO接口的子類對象(在本書後續章節中将學習如何具體實作),是以RegisterForm和IUserDAO之間具有聚合關聯關系。

(3) OracleUserDAO是實作了IUserDAO接口的子類,是以它們之間具有類與接口的實作關系。

(4) 在聲明IUserDAO接口的增加使用者資訊方法addUser()時,需要将在界面類中執行個體化的UserDTO對象作為參數傳遞進來,然後取出封裝在UserDTO對象中的資料插入資料庫,是以addUser()方法的函數原型可以定義為:public boolean addUser(UserDTO user),在IUserDAO的方法addUser()中将UserDTO類型的對象作為參數,故IUserDAO與UserDTO存在依賴關系。

通過以上分析,該執行個體參考類圖如圖1所示:

深入淺出UML類圖---寫得不錯,備忘

圖1 注冊功能參考類圖

注意:在繪制類圖或其他UML圖形時,可以通過注釋(Comment)來對圖中的符号或元素進行一些附加說明,如果需要詳細說明類圖中的某一方法的功能或者實作過程,可以使用如圖2所示表示方式:

深入淺出UML類圖---寫得不錯,備忘

圖2 類圖注釋執行個體

執行個體分析3——售票機控制程式

某運輸公司決定為新的售票機開發車票銷售的控制軟體。圖I給出了售票機的面闆示意圖以及相關的控制部件。

深入淺出UML類圖---寫得不錯,備忘

圖I 售票機面闆示意圖

售票機相關部件的作用如下所述:

(1) 目的地鍵盤用來輸入行程目的地的代碼(例如,200表示總站)。

(2) 乘客可以通過車票鍵盤選擇車票種類(單程票、多次往返票和座席種類)。

(3) 繼續/取消鍵盤上的取消按鈕用于取消購票過程,繼續按鈕允許乘客連續購買多張票。

(4) 顯示屏顯示所有的系統輸出和使用者提示資訊。

(5) 插卡口接受MCard(現金卡),硬币口和紙币槽接受現金。

(6) 列印機用于輸出車票。

(7) 所有部件均可實作自檢并恢複到初始狀态。

現采用面向對象方法開發該系統,使用UML進行模組化,繪制該系統的初始類圖。

深入淺出UML類圖---寫得不錯,備忘

Component

抽象部件類,所有部件類的父類

Keyboard

抽象鍵盤類

ActionKeyboard

繼續/取消鍵盤類

TicketKindKeyboard

車票種類鍵盤類

DestinationKeyboard

目的地鍵盤類

Screen

顯示屏類

CardDriver

卡驅動器類

CashSlot

現金(硬币/紙币)槽類

Printer

列印機類

TicketSoldSystem

售票系統類

Component 的init()方法

初始化部件

Component 的doSeltTest()方法

自檢

Keyboard的getSelectedKey()方法

擷取按鍵值

ActionKeyboard的getAction()方法

繼續/取消鍵盤事件處理

TicketKindKeyboard的getTicketKind()方法

車票種類鍵盤事件處理

DestinationKeyboard的getDestinationCode()方法

目的地鍵盤事件處理

Screen的showText()方法

顯示資訊

CardDriver的getCredit()方法

擷取金額

CardDriver的debitFare()方法

更新卡餘額

CardDriver的ejectMCard()方法

退卡

CashSlot的getCredit()方法

Printer的printTicket()方法

列印車票

Printer的ejectTicket()方法

出票

TicketSoldSystem的verifyCredit()方法

驗證金額

TicketSoldSystem的calculateFare()方法

計算費用