我們在閱讀開源專案時,總是希望能比較高效的整理清楚專案中的各個類之間的關係,那麼有沒有相應的工具能高效、簡潔的表示清楚類關係呢?UML類圖就是一個可以幫我們解決此類問題的工具或者方法。
1 什麼是UML
統一建模語言(Unified Modeling Language,縮寫UML)是非專利的第三代建模和規約語言。
UML是一種開放的方法,用於說明、視覺化、構建和編寫一個正在開發的、物件導向的、軟體密集系統的製品的開放方法。
UML模型和圖形
UML分為模型和圖形兩大類。區分UML模型和UML圖是非常重要的,UML圖(包括用例圖、協作圖、活動圖、序列圖、部署圖、構件圖、類圖、狀態圖)是模型中資訊的圖表表達形式,但是UML模型獨立於UML圖存在。
在UML系統開發中有三個主要的模型:
- 功能模型:從使用者的角度展示系統的功能,包括用例圖。
- 物件模型:採用物件,屬性,操作,關聯等概念展示系統的結構和基礎,包括類別圖、物件圖。
- 動態模型:展現系統的內部行為。包括序列圖,活動圖,狀態圖。
UML2.2中一共定義了14種圖示。
結構性圖形(Structure diagrams)強調的是系統式的建模:
- 靜態圖(static diagram):包括類圖、物件圖、包圖
- 實現圖(implementation diagram):包括元件圖、部署圖
- 剖面圖
- 複合結構圖
行為式圖形(Behavior diagrams)強調系統模型中觸發的事件
- 活動圖
- 狀態圖
- 用例圖
互動性圖形(Interaction diagrams),屬於行為圖形的子集合,強調系統模型中的資料流程
- 通訊圖
- 互動概述圖
- 時序圖
- 時間圖
2 UML類圖作用
UML展現了一系列最佳工程實踐,這些最佳實踐在對大規模,複雜系統進行建模方面,特別是軟體架構層次方面已經被驗證有效。
我們這次介紹的主要是類圖,為了解析專案的系統結構和架構層次,可以簡潔明瞭的幫助我們理解專案中類之間的關係。
類圖的作用:
(1):在軟體工程中,類圖是一種靜態的結構圖,描述了系統的類的集合,類的屬性和類之間的關係,可以簡化了人們對系統的理解;
(2):類圖是系統分析和設計階段的重要產物,是系統編碼和測試的重要模型。
3 類圖格式
在UML類圖中,類使用包含類名、屬性(field) 和方法(method) 且帶有分割線的矩形來表示,
舉個例子。一個Animal類,它包含name,age,state,isPet這4個屬性,以及name相關方法。
class Animal: NSObject {
public var name: String?
internal var isPet: Bool?
fileprivate var state: String?
private var age: Int? = 0
override init() {
self.name = "no name"
self.age = 0
self.isPet = true
self.state = "dead"
}
public func getName() -> String {
return self.name!
}
internal func setName(name: String?) {
self.name = name
}
}
複製程式碼
對應UML類圖:
- 類名:粗體,如果是類是抽象類則類名顯示為斜體!
- 屬性:
可見性 名稱:型別[=預設值]
可見性一般為public、private和protected,在類圖分別用+、-和#表示,在Swift中沒有與protected完全對應的可見控制,因此選用的是internal對應為#;名稱為屬性的名稱;型別為資料型別;預設值如變數 age預設值為0。
- 方法:
可見性 名稱(引數列表 引數1,引數2) :返回型別
可見性如上名稱表示式的介紹,名稱就是方法名,引數列表是可選的項,多引數的話引數直接用英文逗號隔開;返回值也是個可選項,返回值型別可以說基本的資料型別、使用者自定義型別和void。如果是構造方法,則無返回型別!
4類與類之間的關係表達
類圖中類與類之間的關係主要由:繼承、實現、依賴、關聯、聚合、組合這六大型別。表示方式如下圖:
(1)繼承關係(Generalization/extends)
繼承關係也叫泛化關係,指的是一個類(稱為子類、子介面)繼承另外的一個類(稱為父類、父介面)的功能,並可以增加它自己的新功能的能力,繼承是類與類或者介面與介面之間最常見的關係。
繼承用實線空心箭頭表示,由子類指向父類。
下面寫兩個子類,Fish和Cat分別繼承自Animal。
class Fish: Animal {
public var fishType: String?
func swim() {
}
}
class Cat: Animal {
public var hasFeet: Bool?
func playToy(doll:Doll) {
doll.toyMoved()
}
}
複製程式碼
(2)實現關係(implements)
指的是一個class類實現interface介面(可以是多個)的功能;實現是類與介面之間最常見的關係;在Java中此類關係通過關鍵字implements明確標識,在iOS中我將其理解成代理的實現。
寫一個洋娃娃類Doll,該類遵循了ToyAction協議,實現了玩具移動的方法。
protocol ToyAction {
func toyMoved() -> Void
}
class Doll: NSObject,ToyAction {
public var body: Body?
public var cloth: Cloth?
func toyMoved() {
//洋娃娃玩具動作具體實現
}
}
複製程式碼
(3)依賴關係(Dependency)
可以簡單的理解,就是一個類A使用到了另一個類B,而這種使用關係是具有偶然性的、、臨時性的、非常弱的,但是B類的變化會影響到A;比如某人要過河,需要借用一條船,此時人與船之間的關係就是依賴;表現在程式碼層面,為類B作為引數被類A在某個method方法中使用。
在我們的上述程式碼中Cat的playToy方法中引數引用了Doll,因此他們是依賴關係。
(4)關聯關係(Association)
他體現的是兩個類、或者類與介面之間語義級別的一種強依賴關係,比如我和我的朋友;這種關係比依賴更強、不存在依賴關係的偶然性、關係也不是臨時性的,一般是長期性的,而且雙方的關係一般是平等的、關聯可以是單向、雙向的;表現在程式碼層面,為被關聯類B以類屬性的形式出現在關聯類A中,也可能是關聯類A引用了一個型別為被關聯類B的全域性變數;
寫一個Person類,他擁有一個寵物貓,他們之間的關係是關聯。
class Head: NSObject {
}
class Person: NSObject {
public var pet: Cat?
public var head: Head?
}
複製程式碼
](association.png)
(5)聚合關係(Aggregation)
聚合是關聯關係的一種特例,他體現的是整體與部分、擁有的關係,即has-a的關係,此時整體與部分之間是可分離的,他們可以具有各自的生命週期,部分可以屬於多個整體物件,也可以為多個整體物件共享;比如計算機與CPU、公司與員工的關係等;表現在程式碼層面,和關聯關係是一致的,只能從語義級別來區分;
class Cloth: NSObject {
}
class Body: NSObject {
}
複製程式碼
在上述程式碼中Doll由Body和Cloth組成,且即使失去Cloth,Doll也可以正常存在。
(6)組合關係(Composition)
組合也是關聯關係的一種特例,他體現的是一種contains-a的關係,這種關係比聚合更強,也稱為強聚合;他同樣體現整體與部分間的關係,但此時整體與部分是不可分的,整體的生命週期結束也就意味著部分的生命週期結束;比如你和你的大腦;表現在程式碼層面,和關聯關係是一致的,只能從語義級別來區分;
上述程式碼中的Person擁有Head,並且這個整體和部分是不可分割的。
最後來看看這個例子中的整體關係:
其實理解了之後我們發現還是很簡單的,學會了之後就可以投入實踐中了,舉一個簡單第三方庫的類圖例子,下圖是Masonry的類圖整理,可以看到專案結構很清晰的展示了出來。