什么是設(shè)計(jì)模式?詳解設(shè)計(jì)模式概念及幾大原則
第一節(jié)、前言
今天我們來講講設(shè)計(jì)模式,在我們學(xué)習(xí)Java的時(shí)候,時(shí)常聽到單例模式,多例模式,還有使用Spring的時(shí)候,默認(rèn)采用的單例模式,你所聽到的“飽漢式”、“餓漢式”,都是對設(shè)計(jì)模式的形容。那么什么是設(shè)計(jì)模式呢?它又是什么概念呢。
第二節(jié)、設(shè)計(jì)模式概念與GOF設(shè)計(jì)模式
設(shè)計(jì)模式(Design pattern)代表了最佳的實(shí)踐,通常被有經(jīng)驗(yàn)的面向?qū)ο蟮能浖_發(fā)人員所采用。設(shè)計(jì)模式是軟件開發(fā)人員在軟件開發(fā)過程中面臨的一般問題的解決方案。這些解決方案是眾多軟件開發(fā)人員經(jīng)過相當(dāng)長的一段時(shí)間的試驗(yàn)和錯(cuò)誤總結(jié)出來的。
個(gè)人理解:
設(shè)計(jì)模式并非是一種技術(shù),而是在項(xiàng)目迭代的過程中,為了實(shí)現(xiàn)一些功能,設(shè)計(jì)了一些解決方案,將這些經(jīng)驗(yàn)進(jìn)行總結(jié)出來的一個(gè)模式體系,這個(gè)體系是在被反復(fù)使用的、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
為什么要使用設(shè)計(jì)模式?
用設(shè)計(jì)模式是為了復(fù)用代碼、讓代碼更容易被他人理解、保證代碼可靠性。在目前的軟件設(shè)計(jì)潮流中,設(shè)計(jì)模式于己經(jīng)應(yīng)用與每個(gè)公司的各個(gè)項(xiàng)目中,設(shè)計(jì)模式使代碼編寫過程真正實(shí)現(xiàn)工程化,設(shè)計(jì)模式是軟件工程的基石,如同大廈的一塊塊磚一樣。項(xiàng)目中合理地運(yùn)用設(shè)計(jì)模式可以完美地解決很多問題,每種模式在現(xiàn)實(shí)中都有相應(yīng)的原理來與之對應(yīng),每種模式都描述了一個(gè)在我們在開發(fā)過程中、各個(gè)業(yè)務(wù)中相似的且又不斷重復(fù)發(fā)生的問題,以及提供了該問題的核心解決方案,這也是設(shè)計(jì)模式能被廣泛應(yīng)用的原因。
GOF設(shè)計(jì)模式
可能有的朋友聽說過“GOF設(shè)計(jì)模式”,千萬不能認(rèn)為GOF是一個(gè)人,更不要認(rèn)為這些模式是他創(chuàng)寫的。GOF是一個(gè)四個(gè)人的小團(tuán)隊(duì),他們的名字全稱叫做“Gang of Four”,GOF設(shè)計(jì)模式是1994年這個(gè)團(tuán)隊(duì)的4個(gè)人將在軟件開發(fā)中使用頻率較高的設(shè)計(jì)模式進(jìn)行歸納,編寫成Design Patterns: Elements of Reusable Object-Oriented Software【《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖? 基礎(chǔ)》】一書,該書的出版也標(biāo)志著設(shè)計(jì)模式正式成為面向?qū)ο?Object Oriented)軟件工程的 一個(gè)重要研究分支。
第三節(jié)、設(shè)計(jì)模式六大原則及分類
講解GOF設(shè)計(jì)模式之前,我們先來了解下設(shè)計(jì)模式的六大原則:
- 開閉原則(Open Close Principle)
- 有開有閉原則,對擴(kuò)展開放,但是對修改關(guān)閉??梢栽诔绦蛐枰卣沟臅r(shí)候使用,但是不能修改原有的代碼來實(shí)現(xiàn)擴(kuò)展。
- 里氏代換原則(Liskov Substitution Principle)
任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。只有當(dāng)派生類可以替換掉基類,且軟件單位的功能不受到影響時(shí),基類才能真正被復(fù)用,而派生類也能夠在基類的基礎(chǔ)上增加新的行為。里氏代換原則是對開閉原則的補(bǔ)充。實(shí)現(xiàn)開閉原則的關(guān)鍵步驟就是抽象化,而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn)。
可以理解為:子類可以擴(kuò)展父類的功能,但不能改變原有父類的功能,例如:子類能夠替換父類在代碼中已出現(xiàn)的地方,且代碼替換以后,代碼還能正常工作。
- 依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
針對接口編程,依賴于抽象而不依賴于具體。簡單解釋就是面向接口編程,“抽象”就是接口或者抽象類,“具體”值得就是實(shí)現(xiàn)類;含義:上層模塊不應(yīng)該依賴下層模塊,兩者應(yīng)依賴其抽象;抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象;傳變量或者傳參數(shù),盡量使用抽象類,或者接口。
例如:接口負(fù)責(zé)定義public的構(gòu)造方法,抽象類負(fù)責(zé)公共構(gòu)造部分的實(shí)現(xiàn),實(shí)現(xiàn)類準(zhǔn)確的實(shí)現(xiàn)業(yè)務(wù)邏輯。
- 接口隔離原則(Interface Segregation Principle)
使用多個(gè)相互隔離的接口開發(fā),比使用單個(gè)接口要好。目的是為降低類之間的耦合度。
- 迪米特法則Demeter Principle)
最少知道原則,盡量降低類與類之間的耦合,一個(gè)實(shí)體應(yīng)當(dāng)盡量少的與其他實(shí)體之間發(fā)生相互作用,使得系統(tǒng)功能模塊相對獨(dú)立,防止高耦合度。
- 合成復(fù)用原則(Composite Reuse Principle)
它要求在軟件復(fù)用時(shí),要盡量先使用組合或者聚合等關(guān)聯(lián)關(guān)系來實(shí)現(xiàn),其次才考慮使用繼承關(guān)系來實(shí)現(xiàn)。如果要使用繼承關(guān)系,則必須嚴(yán)格遵循里氏代換原則。合成復(fù)用原則同里氏代換原則相輔相成的,兩者都是開閉原則的具體實(shí)現(xiàn)規(guī)范。合成復(fù)用原則是通過將已有的對象納入新對象中,作為新對象的成員對象來實(shí)現(xiàn)的,新對象可以調(diào)用已有對象的功能,從而達(dá)到復(fù)用。
程序設(shè)計(jì)目標(biāo):高內(nèi)聚,低耦合
根據(jù)目的可以將設(shè)計(jì)模式被分為如下三種類型:
- 創(chuàng)建型模式:用于描述“怎樣創(chuàng)建對象”。
- 結(jié)構(gòu)型模式:用于描述如何將類或?qū)ο蟀茨撤N布局組成更大的結(jié)構(gòu)。
- 行為型模式:用于描述類或?qū)ο笾g怎樣相互協(xié)作共同完成某些單個(gè)對象都無法單獨(dú)完成的任務(wù),以及怎樣分配職責(zé)。
根據(jù)作用范圍可以將設(shè)計(jì)模式分為兩類:
- 類模式:用于處理類與子類之間的關(guān)系,這些關(guān)系通過繼承來建立,是靜態(tài)的,在編譯時(shí)刻便確定下來了。
- 對象模式:用于處理對象之間的關(guān)系,這些關(guān)系可以通過組合或聚合來實(shí)現(xiàn),在運(yùn)行時(shí)刻是可以變化的,更具動態(tài)性。
第四節(jié)、學(xué)習(xí)認(rèn)識類圖
認(rèn)識UML類圖能更好的的學(xué)習(xí)設(shè)計(jì)模式,主流的設(shè)計(jì)模式都能夠通過UML類圖更加明亮清晰的展示類與類之間的關(guān)系;學(xué)會UML類圖也可以更快速的看懂類與類之間的關(guān)系。
什么是UML?
1) UML,全稱Unified modeling language UML (統(tǒng)一建模語言), 是一種用于軟件系統(tǒng)分析和設(shè)計(jì)的語言工具,它用于幫助軟件開發(fā)人員進(jìn)行思考和記錄思路的結(jié)果。
2) UML 類似流程圖,本身有一套符號的規(guī)定,就像數(shù)學(xué)符號和化學(xué)符號一樣,這些符號用于描述軟件模型中的各個(gè)元素和他們之間的關(guān)系,比如類、接口、實(shí)現(xiàn)、泛化、依賴、組合、聚合等
常見的集中UML圖
你可能見過下面幾種UML圖:
用例圖
類圖
時(shí)序圖
UML圖可以簡單分為三類:
- 用例圖(use case)。
- 靜態(tài)結(jié)構(gòu)圖:類圖、對象圖、包圖、組件圖、部署圖。
- 動態(tài)行為圖:交互圖(時(shí)序圖與協(xié)作圖)、狀態(tài)圖、活動圖。
我們主要學(xué)習(xí)類圖,那類圖是什么?
類圖(Class diagram)由許多(靜態(tài))說明性的模型元素(例如類、包和它們之間的關(guān)系,這些元素和它們的內(nèi)容互相連接)組成。類圖可以組織在(并且屬于)包中,僅顯示特定包中的相關(guān)內(nèi)容。類圖(Class diagram)也是最常用的UML圖,顯示出類、接口以及它們之間的靜態(tài)結(jié)構(gòu)和關(guān)系;它用于描述系統(tǒng)的結(jié)構(gòu)化設(shè)計(jì),類圖(Class diagram)最基本的元素是類或者接口。
類圖
類圖主要模型元素
- 類(Class)。
- 接口(Interface)。
- 類之間的關(guān)系。
一、類
類(Class)是指具有相同屬性、方法和關(guān)系的對象的抽象,它封裝了數(shù)據(jù)和行為,是面向?qū)ο蟪绦蛟O(shè)計(jì)(OOP)的基礎(chǔ),具有封裝性、繼承性和多態(tài)性等三大特性。在 UML 中,類使用包含類名、屬性和操作且?guī)в蟹指艟€的矩形來表示。
(1) 類名(Name):是一個(gè)字符串,例如,Student。
(2) 屬性(Attribute):是指類的特性,即類的成員變量。
UML 按以下格式表示:
- [可見性]屬性名:類型[=默認(rèn)值]
- 例如:-name:String
注意:“可見性”表示該屬性對類外的元素是否可見,包括公有(Public)、私有(Private)、受保護(hù)(Protected)和朋友(Friendly,Java類圖中不適用此類型,其他語言可用)4 種,在類圖中分別用符號+、-、#、~表示
(3) 操作(Operations):是類的任意一個(gè)實(shí)例對象都可以使用的行為,是類的成員方法。UML 按以下格式表示:
- [可見性]名稱(參數(shù)列表)[:返回類型]
- 例如:+display():void
下圖所示是學(xué)生類的 UML 表示:
二、 接口
接口(Interface)是一種特殊的類,它具有類的結(jié)構(gòu),但不可被實(shí)例化,只可以被實(shí)現(xiàn)類實(shí)現(xiàn)。它包含抽象操作(抽象方法),但不包含屬性(成員變量),它描述了類或組件對外可見的動作。在 UML 中,接口使用一個(gè)帶有橫線小圓圈來進(jìn)行表示。如下圖,類名左上方帶有橫線小圈的就是接口。
三、類之間的關(guān)系
類之間的關(guān)系有6種關(guān)系:依賴關(guān)系、關(guān)聯(lián)關(guān)系、聚合關(guān)系、組合關(guān)系、泛化關(guān)系、實(shí)現(xiàn)關(guān)系
每個(gè)關(guān)系之間的形狀配置如下:
1.依賴關(guān)系
依賴(Dependency)關(guān)系是一種使用關(guān)系,它是對象之間耦合度最弱的一種關(guān)聯(lián)方式,例如在Java中某個(gè)類的方法通過局部變量、方法的參數(shù)或者對靜態(tài)方法的調(diào)用來訪問另一個(gè)類(被依賴類)中的某些方法來完成一些業(yè)務(wù)開發(fā)。
依賴關(guān)系使用帶箭頭的虛線來表示,箭頭從使用類指向被依賴的類(被使用者),如下圖,所示是人與汽車的的關(guān)系圖,人通過汽車的run方法來完成駕駛。
2.關(guān)聯(lián)關(guān)系
關(guān)聯(lián)(Association)關(guān)系是對象之間的一種引用關(guān)系,用于表示一類對象與另一類對象之間的聯(lián)系;關(guān)聯(lián)可以是雙向的,也可以是單向的。在 UML 類圖中,雙向的關(guān)聯(lián)可以用帶兩個(gè)箭頭或者沒有箭頭的實(shí)線來表示,單向的關(guān)聯(lián)用帶一個(gè)箭頭的實(shí)線來表示,箭頭從使用類指向被關(guān)聯(lián)的類。也可以在關(guān)聯(lián)線的兩端標(biāo)注角色名,代表兩種不同的角色。
(1)單向關(guān)聯(lián),人擁有車的屬性對象。
(2)雙向關(guān)聯(lián),人擁有汽車,汽車有歸屬人信息屬性
(3)自關(guān)聯(lián),水由無數(shù)個(gè)水分子組成,但是水分子依然是水
3. 聚合關(guān)系
聚合(Aggregation)關(guān)系是關(guān)聯(lián)關(guān)系的一種,是強(qiáng)關(guān)聯(lián)關(guān)系,是整體和部分之間的關(guān)系,是 has-a 的關(guān)系;如果整體和部分單獨(dú)拿出來,也能變?yōu)楠?dú)立的模塊,但是整體需要依賴部分才能保證整體,在 UML 類圖中,聚合關(guān)系可以用帶空心菱形的實(shí)線來表示,菱形指向整體。
例如下圖:公司和員工每一個(gè)都是一個(gè)獨(dú)立的關(guān)系,如果公司沒有了,員工依然還是員工,但是所有員工都沒了,這個(gè)公司也就不復(fù)存在了。
4.組合關(guān)系
組合(Composition)關(guān)系也是關(guān)聯(lián)關(guān)系的一種,也表示類之間的整體與部分的關(guān)系,但它是一種更強(qiáng)烈的聚合關(guān)系,是 cxmtains-a 關(guān)系;在組合關(guān)系中,整體對象可以控制部分對象的生命周期,一旦整體對象不存在,部分對象也將不存在,部分對象不能脫離整體對象而存在;在 UML 類圖中,組合關(guān)系用帶實(shí)心菱形的實(shí)線來表示,菱形指向整體。
例如下圖:腳是人的身體的重要組成部分,如果身體不存在了,那么腳也就不存在了,且部分不能脫離整體。
5.泛化關(guān)系
泛化(Generalization)關(guān)系是對象之間耦合度最大的一種關(guān)系,是父類與子類之間的關(guān)系,是一種繼承關(guān)系,是 is-a 的關(guān)系。用Java的理念可以認(rèn)為:一個(gè)父類只要被子類繼承,那這個(gè)父類與子類的關(guān)系就叫“泛化關(guān)系”。在 UML 類圖中,泛化關(guān)系用帶空心三角箭頭的實(shí)線來表示,箭頭從子類指向父類。在代碼實(shí)現(xiàn)時(shí),使用面向?qū)ο蟮睦^承機(jī)制來實(shí)現(xiàn)泛化關(guān)系
例如下圖:人作為一個(gè)父類,可能有多個(gè)子類,作者有人的屬性且有自己特殊的“寫作”技能,讀者擁有“評論書籍”的技能,這樣就構(gòu)成了泛化關(guān)系(人類與作者、人類與讀者都是泛化關(guān)系)。
6.實(shí)現(xiàn)關(guān)系
實(shí)現(xiàn)(Realization)關(guān)系是接口與實(shí)現(xiàn)類之間的關(guān)系。在這種關(guān)系中,實(shí)現(xiàn)類實(shí)現(xiàn)了接口中的抽象方法,類中的操作實(shí)現(xiàn)了接口中所聲明的所有的抽象操作。在 UML 類圖中,實(shí)現(xiàn)關(guān)系使用帶空心三角箭頭的虛線來表示,箭頭從實(shí)現(xiàn)類指向接口。
例如下圖:車是一個(gè)接口是一個(gè)規(guī)范,滿足能跑起來就可以,汽車和自行車都實(shí)現(xiàn)了“跑”這個(gè)方法,而汽車現(xiàn)在可以實(shí)現(xiàn)自動駕駛,就是在按照接口的規(guī)范實(shí)現(xiàn)了“run”方法,那么接口與實(shí)現(xiàn)類的關(guān)系就是實(shí)現(xiàn)關(guān)系。
第五節(jié)、結(jié)束語
GOF常見23種設(shè)計(jì)模式總結(jié)了目前主流的設(shè)計(jì)模式,其中由于簡單工廠模式比較容易實(shí)現(xiàn),所以并未被收錄;接下來的一段時(shí)間,小編會帶領(lǐng)大家學(xué)習(xí)24種設(shè)計(jì)模式,講述每種設(shè)計(jì)模式的概念、畫類圖、應(yīng)用場景,大家一起共同學(xué)習(xí)、共同成長。
本文轉(zhuǎn)載自微信公眾號「IT小白架構(gòu)師之路」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系IT小白架構(gòu)師之路公眾號。