設計模式學習筆記(二):UML與面向對象設計原則


1 UML

1.1 UML

UML(Unified Modeling Language)是統一建模語言,1997年11月UML1.1版本提交給OMG並正式通過,成為建模語言的個那個也標准。2003年6月UML2.0獲得正式通過。

1.2 UML特性

  • U(Unified):統一,UML融合了多種優秀的面向對象建模方法以及多種得到認可的軟件工程方法,消除了因方法林立且相互獨立而帶來的種種不便,集眾家之長,股名“統一”。通過統一的表示方法可以讓不同知識背景的領域專家,系統分析設計人員以及開發人員可以方便地交流
  • M(Modeling):UML是一種通用的可視化建模語言,不同與編程語言,UML通過一些標准的圖形符號和文字來對系統進行建模,用於對軟件進行描述,可視化處理,構造系統制品的文檔。UML適用於各種軟件開發方法,軟件生命周期的各個階段,各種應用領域以及各種開發工具
  • L(Language):UML是一種語言,也就意味着它有屬於自己的標准表達規則,不是一種類似Java,C++的編程語言,而是一種分析設計語言,一種建模語言

1.3 UML結構

UML結構通常包括以下4個部分:視圖,圖,模型元素以及通用機制。

1.3.1 視圖

UML視圖用於從不同的角度來表示待建模的系統。視圖是由許多圖形組成的一個抽象集合,在建立一個系統模型時,只有通過定義多個視圖,每個視圖顯示該系統的一個特定方面,才能構造出該系統的完整藍圖。

UML視圖包括:

  • 用戶視圖:以用戶的觀點表示系統的目標,是所有視圖的核心,用於描述系統的需求
  • 結構視圖:系統的靜態行為,描述系統的靜態元素,比如包,類,對象以及它們之間的關系
  • 行為視圖:系統的動態行為,描述系統的組成元素在系統運行時的交互關系
  • 實現視圖:系統中邏輯元素的分布,描述系統中物理文件以及它們之間的關系
  • 環境視圖:系統中物理元素的分布,描述系統中硬件設備以及他們之間的關系

1.3.2 圖

UML圖是描述UML視圖內容的圖形,UML2.0提供了13種圖,分別是用例圖,類圖,對象圖,包圖,組合結構圖,狀態圖,活動圖,順序圖,通信圖,定時圖,交互概覽圖,組件圖和部署圖。其中:

  • 用例圖對應用戶視圖
  • 類圖,對象圖,包圖和組合結構圖對應結構視圖
  • 狀態圖,活動圖,順序圖,通信圖,定時圖和交互概覽圖對應行為視圖
  • 組件圖對應實現視圖
  • 部署圖對應環境視圖

1.3.3 模型元素

模型元素是指UML圖中所使用的一些概念,對應於普通的面向對象概念,如類,對象,消息以及這些概念之間的關系,如關聯關系,泛化關系等。

1.3.4 通用機制

UML提供的通用機制為模型元素提供額外的注釋,信息和語義,這些通用機制也提供了擴展機制,允許用戶對UML進行擴展,如定義新的建模元素,擴展原有的語義,添加新的特殊信息來擴展模型元素的規則說明等,以便適用於特定的方法或過程,組織和用戶。

2 UML類圖

2.1 類圖

類封裝了數據和行為,是具有相同屬性,操作,關系的對象集合的總稱。類圖是用出現在系統中不同類來描述系統的靜態結構,主要描述不同的類以及它們之間的關系。

在UML中,類圖包含類名,屬性以及操作。如下面的Employee類:

在這里插入圖片描述
類一般由三部分組成:

  • 類名
  • 屬性
  • 操作

2.1.1 類名

類名就是類的名字,一個字符串。

2.1.2 屬性

類的成員變量,一般的格式為

可見性 名稱 : 類型 [ = 默認值]

可見性表示該屬性對於類外的元素是否可見,包括:

  • 公有:+
  • 私有:-
  • 受保護:#
  • 包:~

2.1.3 操作

UML規定操作的定義方式為:

可見性 名稱(參數列表)[ : 返回類型]
  • 可見性與屬性可見性的定義一致
  • 參數列表表示方法的參數,語法與屬性定義類似,用,分隔

2.2 類之間的關系

UML提供了四種不同的方式表示類與類之間的關系:

  • 關聯關系
  • 依賴關系
  • 泛化關系
  • 接口與實現關系

下面逐個看一下。

3 關聯關系

關聯關系是一種結構化關系,用於表示一類對象與另一類對象之間有聯系。在UML中用實線連接有關聯關系的類。可以在關聯線上標注角色名,關系的兩端代表兩種不同的角色,因此在一個關聯關系中可以包含兩個角色名,角色名不是必須的,但可以使類之間的關系更加明確。

例如在一個登錄界面類LoginForm包含一個JButton

在這里插入圖片描述

UML中關聯通常包括以下六種形式:

  • 雙向關聯
  • 單向關聯
  • 自關聯
  • 多重性關聯
  • 聚合關系
  • 組合關系

3.1 雙向關聯

默認情況下關聯是雙向的,例如顧客購買商品並擁有商品,反之賣出的商品總是某個顧客與之相關聯:

在這里插入圖片描述

3.2 單向關聯

關聯也可以是單向的,在UML中關聯用帶箭頭的實線表示,比如顧客擁有地址:

在這里插入圖片描述

3.3 自關聯

系統中可能會存在一些類的屬性對象類型為該類本身,這種特殊的關聯關系為自關聯,常見於鏈表:

在這里插入圖片描述

3.4 多重性關聯

多重性關聯又稱為多重性關聯聯系,表示兩個關聯對象在數量上的對應關系。在UML中,對象之間的多重性可以直接在關聯直線上用一個數字或者一個數字范圍來表示。常見的表示方式如下:

在這里插入圖片描述

例如一個界面可以具有0個或多個按鈕,但是一個按鈕只能從屬於一個界面:

在這里插入圖片描述

3.5 聚合關系

聚合關系表示整體與部分的關系,使用空心菱形表示。聚合關系中部分是整體的一部分,但是部分可以脫離整體獨立存在,比如引擎是汽車的一部分,但是引擎可以獨立於汽車存在:

在這里插入圖片描述

3.6 組合關系

組合關系也表示整體與部分之間的關系,但是部分不能脫離整體存在。組合關系使用實心菱形表示。比如人的頭和嘴巴是組合關系:

在這里插入圖片描述

4 依賴關系

依賴關系是一種使用關系,在需要表示“一個事物使用另一個事物”時使用依賴關系。UML中依賴關系用帶箭頭的虛線表示,由依賴的一方指向被依賴的一方。例如駕駛員開車,開車需要車,也就是駕駛員依賴於車:

在這里插入圖片描述

5 泛化關系

泛化關系也就是繼承關系,用於描述父類與之類之間的關系,父類又叫基類或者超類,子類又稱作派生類。UML中泛化關系用帶空心三角形的直線表示,箭頭指向基類:

在這里插入圖片描述

6 接口與實現關系

很多語言比如Java,C#都有接口的概念,接口通常沒有屬性,所有是操作都是抽象的,只有操作的聲明沒有操作的實現。UML中使用<<Interface>>表示接口:

在這里插入圖片描述

類與接口之間的實現關系使用空心三角形+虛線表示:

在這里插入圖片描述

7 面向對象設計原則

7.1 概述

面向對象設計的目標之一是支持可維護性復用,一方面需要實現設計方案或者源代碼的重用,一方面要確保系統能夠易於擴展和修改,具有較好的靈活性。面向對象設計原則由此誕生,它們蘊含於很多設計模式中,是從許多方案總結出來的指導性原則。常見的7種面向對象設計原則如下:

  • 單一權責原則
  • 開閉原則
  • 里氏代換原則
  • 依賴倒轉原則
  • 接口隔離原則
  • 合成復用原則
  • 迪米特法則

7.2 單一職責原則SRP

單一權責原則(Single Responsibility Principal):一個類只負責一個功能領域中的相應職責。
或者可以定義為:就一個類而言,應該只有一個引起它變化的原因。

單一權責原則的核心思想是:一個類不能太“累”。一個類(大到模塊,小到方法)承擔的職責越多,被復用的可能性越小,而且一個類承擔的職責過多,就相當於將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作,因此需要將職責分離,封裝在不同的類中,即將不同的變化原因封裝在不同的類中。單一權責原則是實現高內聚,低耦合的指導方針。

7.3 開閉原則OCP

開閉原則(Open-Closed Principle):一個軟件實體應當對擴展開放,對修改關閉。即軟件實體應盡量不修改原有代碼的情況下進行擴展。

其中軟件實體可以是一個軟件模塊,一個由多個類組成的局部結構或者一個獨立的類。

一個軟件設計符合開閉原則,則可以非常方便地對其進行擴展,而且在擴展時無須修改現有代碼,使得軟件系統在擁有適應性和靈活性的同時具備較好的穩定性和延續性。

為了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。可以通過接口,抽象類等定義抽象層,通過具體類進行擴展,修改系統的行為時無需修改抽象層,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有代碼的基礎上擴展系統的功能,達到開閉原則的要求。

7.4 里氏代換原則LSP

里氏代換原則(Liskov Substitution Principle):所有引用基類/父類的地方必須能透明地使用其子類的對象。

簡單地說就是父類出現的地方可以用子類代替,程序不會產生任何的錯誤和異常。使用里氏代換原則時,應該將父類設計為抽象類或者接口,讓子類繼承父類或實現父類接口,並實現父類中聲明的方法,運行時,子類實例代替父類實例,可以很方便地擴展系統的功能,無須修改原有子類的代碼,增加新的功能可以通過增加一個新的子類來實現。

7.5 依賴倒轉原則DIP

依賴倒轉原則(Dependency Inversion Principal):抽象不應該依賴具體細節,細節應當依賴於抽象,換言之要針對接口編程,而不是針對實現編程。

依賴倒轉原則要求程序在源代碼中傳遞參數時或者在關聯關系中,盡量引用高層次的抽象層類,即使有接口和抽象類進行變量類型聲明,參數類型聲明,方法返回類型聲明以及數據類型的轉換等,而不是用具體類來做。一個具體類應該只實現接口或者抽象類中聲明過的方法,而不要給出多余的方法,否則將無法調用在子類中新增的方法。

在實現依賴倒轉原則時,需要針對抽象層進行編程,而將具體類的對象通過依賴注入(Dependency Injection)的方式注入到其他對象中。依賴注入是指當一個對象要與其他對象發生依賴關系時,通過抽象來注入所依賴的對象。常用的注入方式包括:

  • 構造注入:通過構造函數來傳入具體類的對象
  • 設值注入(setter注入):通過setter來傳入具體類對象
  • 接口注入:通過實現在接口中聲明的方法來傳入具體類對象

上面的方法在定義時使用抽象類型,在運行時傳入具體類型的對象,由子類對象來覆蓋父類對象 。

7.6 接口隔離原則ISP

接口隔離原則(Interface Segregation Principal):使用多個專門的接口,而不使用單一的總接口,即客戶端不應該依賴於那些它不需要的接口。

也就是說,當一個接口太大時需要划分為更小的接口,使用該接口的客戶端僅需知道與之相關的方法。每一個接口應該承擔一種相對獨立的角色,這里的接口有兩層意思:

  • 一種是指一個類型所具有的方法特征的集合,僅僅是一種邏輯上的抽象
  • 另一種是指某個語言具體接口的定義,有嚴格的定義和結構,比如Java中的interface

ISP對兩種不同含義的表達方式有所不同:

  • 當接口理解成一個類型所提供的所有方法特征的集合時,這就是一種邏輯上的概念,接口的划分將直接帶來類型的划分,可以把接口理解成角色,一個接口只能代表一個角色,每個角色都有它特定的一個接口,此時這個原則可以叫“角色隔離原則”
  • 把接口理解成狹義的特定語言的接口,ISP表達的意思是接口僅僅提供客戶端需要的行為,客戶端不需要的行為則隱藏起來,應當為客戶端提供盡可能小的接口,而不提供大的總接口。接口應盡量細化,同時接口中的方法應該盡量少,每個接口中只包含一個客戶端(如子模塊或者業務邏輯類)所需的方法即可,這種機制也叫“定制服務”

使用接口隔離原則時,注意控制接口的粒度:

  • 接口太小導致接口泛濫,不利於維護
  • 接口太大將違背ISP,靈活性差,使用不方便

一般而言接口中僅包含為某一類用戶定制的方法即可。

7.7 合成復用原則CRP

合成復用原則(Composite Reuse Principal):盡量使用對象組合而不是繼承來達成復用目的。

合成復用原則又叫組合/聚合復用原則(Composition/Aggregate Reuse Principal),就是在一個新對象中通過關聯關系(組合/聚合)對對象進行重用而不是使用繼承。

面向對象設計中,可以通過兩種方法在不同環境中復用已有的設計和實現:

  • 繼承
  • 組合/聚合

7.7.1 繼承

繼承需要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,降低復雜度,而濫用繼承反而會增加系統構建和維護的難度以及系統的復雜度。繼承主要帶來的問題是會破環系統的封裝性,因為繼承會將基類實現細節暴露給子類,由於基類內部細節對子類可見,因此叫“白箱復用”。

一般而言兩個類之間的關系是“Is-A”關系就可以使用繼承。

7.7.2 組合/聚合

盡管可以通過繼承來對代碼進行復用,一般來說優先考慮組合/聚合。組合/聚合可以使系統更加靈活,降低類與類之間的耦合度。由於新對象可以直接調用已有對象的功能,這樣做可以使成員對象的內部實現細節對新對象不可見,所以這種復用叫“黑箱復用”。

一般而言兩個類之間的關系是“Has-A”關系就可以使用組合/聚合。

7.8 迪米特法則LoD

迪米特法則(Law of Demeter):一個軟件實體應當盡可能少地與其他實體發生相互作用。

迪米特法則又叫最少知識原則(Least Knowledge Principal,LKP),迪米特法則會對軟件實體之間通信的寬度與深度進行限制,可以降低系統的耦合度,使類與類之間保持松耦合。

迪米特法則還有幾種定義形式:不要和“陌生人”說話,只與直接朋友通信。對於一個對象“朋友”可以是以下幾類:

  • 對象本身(this)
  • 以參數形式傳入的對象
  • 成員對象
  • 如果成員對象是一個集合,那么集合中的元素也是“朋友”
  • 當前對象所創建的對象

滿足上述條件之一即是“朋友”,否則就是“陌生人”,不能和“陌生人”發生直接交互。

迪米特法則要求設計系統時盡量減少對象之間的交互,通過引入一個合理的中間類來降低現有對象之間的耦合度。應用迪米特法則時需要注意幾點:

  • 優先將類設計為不變類
  • 類划分上盡量創建松耦合的類
  • 類結構設計上盡量降低成員變量和成員函數的訪問權限
  • 在對其他類的引用上,一個對象對其他對象的引用應當降到最低

8 總結

在這里插入圖片描述

在這里插入圖片描述

如果覺得文章好看,歡迎點贊。

同時歡迎關注微信公眾號:氷泠之路。

在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM