學(xué)習(xí)并理解 23 種設(shè)計(jì)模式
設(shè)計(jì)模式 Design Pattern 是一套被反復(fù)使用、多數(shù)人知曉的匙头、經(jīng)過(guò)分類(lèi)編目的漫谷、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié),使用設(shè)計(jì)模式是為了可重用代碼蹂析、讓代碼更容易被他人理解并且保證代碼可靠性舔示。。
在《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書(shū)中所介紹的 23 種經(jīng)典設(shè)計(jì)模式电抚,不過(guò)設(shè)計(jì)模式并不僅僅只有這 23 種惕稻,隨著軟件開(kāi)發(fā)行業(yè)的發(fā)展,越來(lái)越多的新模式不斷誕生并得以應(yīng)用蝙叛。有經(jīng)驗(yàn)的開(kāi)發(fā)者在學(xué)習(xí)設(shè)計(jì)模式可以和過(guò)往的經(jīng)驗(yàn)互相印證俺祠,更容易理解這些設(shè)計(jì)模式。
設(shè)計(jì)模式一般包含模式名稱、問(wèn)題蜘渣、目的淌铐、解決方案、效果等組成要素蔫缸。問(wèn)題描述了應(yīng)該在何時(shí)使用模式腿准,它包含了設(shè)計(jì)中存在的問(wèn)題以及問(wèn)題存在的原因。解決方案描述了一個(gè)設(shè)計(jì)模式的組成成分拾碌,以及這些組成成分之間的相互關(guān)系吐葱,各自的職責(zé)和協(xié)作方式,通常解決方案通過(guò) UML 類(lèi)圖和核心代碼來(lái)進(jìn)行描述校翔。效果描述了模式的優(yōu)缺點(diǎn)以及在使用模式時(shí)應(yīng)權(quán)衡的問(wèn)題弟跑。
為什么要學(xué)習(xí)設(shè)計(jì)模式:
設(shè)計(jì)模式來(lái)源眾多專家的經(jīng)驗(yàn)和智慧,它們是從許多優(yōu)秀的軟件系統(tǒng)中總結(jié)出的成功的防症、能夠?qū)崿F(xiàn)可維護(hù)性復(fù)用的設(shè)計(jì)方案孟辑,使用這些方案將可以讓我們避免做一些重復(fù)性的工作
設(shè)計(jì)模式提供了一套通用的設(shè)計(jì)詞匯和一種通用的形式來(lái)方便開(kāi)發(fā)人員之間溝通和交流,使得設(shè)計(jì)方案更加通俗易懂
大部分設(shè)計(jì)模式都兼顧了系統(tǒng)的可重用性和可擴(kuò)展性蔫敲,這使得我們可以更好地重用一些已有的設(shè)計(jì)方案扑浸、功能模塊甚至一個(gè)完整的軟件系統(tǒng),避免我們經(jīng)常做一些重復(fù)的設(shè)計(jì)燕偶、編寫(xiě)一些重復(fù)的代碼
合理使用設(shè)計(jì)模式并對(duì)設(shè)計(jì)模式的使用情況進(jìn)行文檔化,將有助于別人更快地理解系統(tǒng)
學(xué)習(xí)設(shè)計(jì)模式將有助于初學(xué)者更加深入地理解面向?qū)ο笏枷?/p>
儲(chǔ)備知識(shí):
抽象類(lèi):一般抽象類(lèi)都是作為基類(lèi)础嫡,比如說(shuō)「電腦」就可以作為一個(gè)抽象類(lèi)指么,根據(jù)抽象類(lèi)派生出「臺(tái)式電腦」和「筆記本電腦」2種具體類(lèi)。一般不對(duì)抽象類(lèi)進(jìn)行實(shí)例化榴鼎。
組合優(yōu)于繼承:不能濫用繼承來(lái)拓展功能伯诬,配合組合會(huì)更靈活。同樣拿「電腦」抽象類(lèi)來(lái)舉例巫财,如果使用繼承盗似,區(qū)分不同類(lèi)型的「電腦」我們可以派生出「臺(tái)式電腦」和「筆記本電腦」,如果再增加一個(gè)維度平项,根據(jù)品牌又能繼續(xù)細(xì)分出「聯(lián)想臺(tái)式電腦」赫舒、「聯(lián)想筆記本電腦」、「蘋(píng)果臺(tái)式電腦」和「蘋(píng)果筆記本電腦」等等闽瓢,如果再增加一個(gè)維度繼續(xù)細(xì)分下去接癌,顯然繼承是無(wú)法勝任的。這個(gè)時(shí)候可以使用繼承加組合方式扣讼,組合的對(duì)象也可以進(jìn)行抽象化設(shè)計(jì):
一缺猛、UML 類(lèi)圖
每個(gè)模式都有相應(yīng)的對(duì)象結(jié)構(gòu)圖,同時(shí)為了展示對(duì)象間的交互細(xì)節(jié), 有些時(shí)候會(huì)用到 UML 圖來(lái)介紹其如何運(yùn)行荔燎。這里不會(huì)將 UML 的各種元素都提到耻姥,只想講講類(lèi)圖中各個(gè)類(lèi)之間的關(guān)系, 能看懂類(lèi)圖中各個(gè)類(lèi)之間的線條有咨、箭頭代表什么意思后琐簇,也就足夠應(yīng)對(duì)日常的工作和交流。同時(shí),我們應(yīng)該能將類(lèi)圖所表達(dá)的含義和最終的代碼對(duì)應(yīng)起來(lái)灿巧。有了這些知識(shí)浮定,看后面章節(jié)的設(shè)計(jì)模式結(jié)構(gòu)圖就沒(méi)有什么問(wèn)題了。
本文中大部分是 UML 類(lèi)圖据某,也有個(gè)別簡(jiǎn)易流程圖。由于文中部分模式并未配圖诗箍,你可以在這里查看我在網(wǎng)絡(luò)上收集的完整 23 種設(shè)計(jì)模式 UML 類(lèi)圖癣籽。
1.1 繼承
繼承用一條帶空心箭頭的直接表示。
1.2 實(shí)現(xiàn)
實(shí)現(xiàn)關(guān)系用一條帶空心箭頭的虛線表示滤祖。
1.3 組合
與聚合關(guān)系一樣筷狼,組合關(guān)系同樣表示整體由部分構(gòu)成的語(yǔ)義。比如公司由多個(gè)部門(mén)組成匠童,但組合關(guān)系是一種強(qiáng)依賴的特殊聚合關(guān)系埂材,如果整體不存在了,則部分也不存在了汤求。例如俏险,公司不存在了,部門(mén)也將不存在了扬绪。
1.4 聚合
聚合關(guān)系用于表示實(shí)體對(duì)象之間的關(guān)系竖独,表示整體由部分構(gòu)成的語(yǔ)義,例如一個(gè)部門(mén)由多個(gè)員工組成挤牛。與組合關(guān)系不同的是莹痢,整體和部分不是強(qiáng)依賴的,即使整體不存在了墓赴,部分仍然存在竞膳。例如,部門(mén)撤銷(xiāo)了竣蹦,人員不會(huì)消失顶猜,他們依然存在。
1.5 關(guān)聯(lián)
關(guān)聯(lián)關(guān)系是用一條直線表示的痘括,它描述不同類(lèi)的對(duì)象之間的結(jié)構(gòu)關(guān)系长窄,它是一種靜態(tài)關(guān)系滔吠, 通常與運(yùn)行狀態(tài)無(wú)關(guān),一般由常識(shí)等因素決定的挠日。它一般用來(lái)定義對(duì)象之間靜態(tài)的疮绷、天然的結(jié)構(gòu), 所以嚣潜,關(guān)聯(lián)關(guān)系是一種“強(qiáng)關(guān)聯(lián)”的關(guān)系冬骚。
比如,乘車(chē)人和車(chē)票之間就是一種關(guān)聯(lián)關(guān)系懂算,學(xué)生和學(xué)校就是一種關(guān)聯(lián)關(guān)系只冻,關(guān)聯(lián)關(guān)系默認(rèn)不強(qiáng)調(diào)方向,表示對(duì)象間相互知道计技。如果特別強(qiáng)調(diào)方向喜德,如下圖,表示 A 知道 B 垮媒,但 B 不知道 A 舍悯。
1.6 依賴
依賴關(guān)系是用一套帶箭頭的虛線表示的,如A依賴于B睡雇,他描述一個(gè)對(duì)象在運(yùn)行期間會(huì)用到另一個(gè)對(duì)象的關(guān)系萌衬。
與關(guān)聯(lián)關(guān)系不同的是,它是一種臨時(shí)性的關(guān)系它抱,通常在運(yùn)行期間產(chǎn)生秕豫,并且隨著運(yùn)行時(shí)的變化,依賴關(guān)系也可能發(fā)生變化观蓄。顯然馁蒂,依賴也有方向,雙向依賴是一種非常糟糕的結(jié)構(gòu)蜘腌,我們總是應(yīng)該保持單向依賴,杜絕雙向依賴的產(chǎn)生饵隙。
二撮珠、六大原則
2.1 開(kāi)閉原則
一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉金矛。即軟件實(shí)體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展芯急。
任何軟件都需要面臨一個(gè)很重要的問(wèn)題,即它們的需求會(huì)隨時(shí)間的推移而發(fā)生變化驶俊。當(dāng)軟件系統(tǒng)需要面對(duì)新的需求時(shí)娶耍,我們應(yīng)該盡量保證系統(tǒng)的設(shè)計(jì)框架是穩(wěn)定的。如果一個(gè)軟件設(shè)計(jì)符合開(kāi)閉原則饼酿,那么可以非常方便地對(duì)系統(tǒng)進(jìn)行擴(kuò)展榕酒,而且在擴(kuò)展時(shí)無(wú)須修改現(xiàn)有代碼胚膊,使得軟件系統(tǒng)在擁有適應(yīng)性和靈活性的同時(shí)具備較好的穩(wěn)定性和延續(xù)性。隨著軟件規(guī)模越來(lái)越大想鹰,軟件壽命越來(lái)越長(zhǎng)紊婉,軟件維護(hù)成本越來(lái)越高,設(shè)計(jì)滿足開(kāi)閉原則的軟件系統(tǒng)也變得越來(lái)越重要辑舷。
為了滿足開(kāi)閉原則喻犁,需要對(duì)系統(tǒng)進(jìn)行抽象化設(shè)計(jì),抽象化是開(kāi)閉原則的關(guān)鍵何缓。在Java肢础、C#等編程語(yǔ)言中,可以為系統(tǒng)定義一個(gè)相對(duì)穩(wěn)定的抽象層碌廓,而將不同的實(shí)現(xiàn)行為移至具體的實(shí)現(xiàn)層中完成传轰。在很多面向?qū)ο缶幊陶Z(yǔ)言中都提供了接口、抽象類(lèi)等機(jī)制氓皱,可以通過(guò)它們定義系統(tǒng)的抽象層路召,再通過(guò)具體類(lèi)來(lái)進(jìn)行擴(kuò)展。如果需要修改系統(tǒng)的行為波材,無(wú)須對(duì)抽象層進(jìn)行任何改動(dòng)股淡,只需要增加新的具體類(lèi)來(lái)實(shí)現(xiàn)新的業(yè)務(wù)功能即可,實(shí)現(xiàn)在不修改已有代碼的基礎(chǔ)上擴(kuò)展系統(tǒng)的功能廷区,達(dá)到開(kāi)閉原則的要求唯灵。
優(yōu)點(diǎn):實(shí)踐開(kāi)閉原則的優(yōu)點(diǎn)在于可以在不改動(dòng)原有代碼的前提下給程序擴(kuò)展功能。增加了程序的可擴(kuò)展性隙轻,同時(shí)也降低了程序的維護(hù)成本埠帕。
2.2 里氏替換原則
所有引用基類(lèi)對(duì)象的地方能夠透明地使用其子類(lèi)的對(duì)象
里氏代換原則告訴我們,在軟件中將一個(gè)基類(lèi)對(duì)象替換成它的子類(lèi)對(duì)象玖绿,程序?qū)⒉粫?huì)產(chǎn)生任何錯(cuò)誤和異常敛瓷,反過(guò)來(lái)則不成立,如果一個(gè)軟件實(shí)體使用的是一個(gè)子類(lèi)對(duì)象的話斑匪,那么它不一定能夠使用基類(lèi)對(duì)象呐籽。例如:我喜歡動(dòng)物,那我一定喜歡狗蚀瘸,因?yàn)楣肥莿?dòng)物的子類(lèi)狡蝶。但是我喜歡狗,不能據(jù)此斷定我喜歡動(dòng)物贮勃,因?yàn)槲也⒉幌矚g老鼠贪惹,雖然它也是動(dòng)物。
例如有兩個(gè)類(lèi)寂嘉,一個(gè)類(lèi)為BaseClass奏瞬,另一個(gè)是SubClass類(lèi)枫绅,并且SubClass類(lèi)是BaseClass類(lèi)的子類(lèi),那么一個(gè)方法如果可以接受一個(gè)BaseClass類(lèi)型的基類(lèi)對(duì)象base的話丝格,如:method1(base)撑瞧,那么它必然可以接受一個(gè)BaseClass類(lèi)型的子類(lèi)對(duì)象sub,method1(sub)能夠正常運(yùn)行显蝌。反過(guò)來(lái)的代換不成立预伺,如一個(gè)方法method2接受BaseClass類(lèi)型的子類(lèi)對(duì)象sub為參數(shù):method2(sub),那么一般而言不可以有method2(base)曼尊,除非是重載方法酬诀。
里氏代換原則是實(shí)現(xiàn)開(kāi)閉原則的重要方式之一,由于使用基類(lèi)對(duì)象的地方都可以使用子類(lèi)對(duì)象骆撇,因此在程序中盡量使用基類(lèi)類(lèi)型來(lái)對(duì)對(duì)象進(jìn)行定義瞒御,而在運(yùn)行時(shí)再確定其子類(lèi)類(lèi)型,用子類(lèi)對(duì)象來(lái)替換父類(lèi)對(duì)象神郊。
優(yōu)點(diǎn):可以檢驗(yàn)繼承使用的正確性肴裙,約束繼承在使用上的泛濫。
2.3 依賴倒置原則
抽象不應(yīng)該依賴于具體類(lèi)涌乳,具體類(lèi)應(yīng)當(dāng)依賴于抽象蜻懦。換言之,要針對(duì)接口編程夕晓,而不是針對(duì)實(shí)現(xiàn)編程宛乃。
依賴倒轉(zhuǎn)原則要求我們?cè)诔绦虼a中傳遞參數(shù)時(shí)或在關(guān)聯(lián)關(guān)系中,盡量引用層次高的抽象層類(lèi)蒸辆,即使用接口和抽象類(lèi)進(jìn)行變量類(lèi)型聲明征炼、參數(shù)類(lèi)型聲明、方法返回類(lèi)型聲明躬贡,以及數(shù)據(jù)類(lèi)型的轉(zhuǎn)換等谆奥,而不要用具體類(lèi)來(lái)做這些事情。為了確保該原則的應(yīng)用拂玻,一個(gè)具體類(lèi)應(yīng)當(dāng)只實(shí)現(xiàn)接口或抽象類(lèi)中聲明過(guò)的方法雄右,而不要給出多余的方法,否則將無(wú)法調(diào)用到在子類(lèi)中增加的新方法纺讲。
在引入抽象層后,系統(tǒng)將具有很好的靈活性囤屹,在程序中盡量使用抽象層進(jìn)行編程熬甚,而將具體類(lèi)寫(xiě)在配置文件中,這樣一來(lái)肋坚,如果系統(tǒng)行為發(fā)生變化乡括,只需要對(duì)抽象層進(jìn)行擴(kuò)展肃廓,并修改配置文件,而無(wú)須修改原有系統(tǒng)的源代碼诲泌,在不修改的情況下來(lái)擴(kuò)展系統(tǒng)的功能盲赊,滿足開(kāi)閉原則的要求。
優(yōu)點(diǎn):通過(guò)抽象來(lái)搭建框架敷扫,建立類(lèi)和類(lèi)的關(guān)聯(lián)哀蘑,以減少類(lèi)間的耦合性。而且以抽象搭建的系統(tǒng)要比以具體實(shí)現(xiàn)搭建的系統(tǒng)更加穩(wěn)定葵第,擴(kuò)展性更高绘迁,同時(shí)也便于維護(hù)。
2.4 單一職責(zé)原則
一個(gè)類(lèi)只負(fù)責(zé)一個(gè)功能領(lǐng)域中的相應(yīng)職責(zé)卒密,或者可以定義為:就一個(gè)類(lèi)而言缀台,應(yīng)該只有一個(gè)引起它變化的原因。
單一職責(zé)原則告訴我們:一個(gè)類(lèi)不能太“累”哮奇!在軟件系統(tǒng)中膛腐,一個(gè)類(lèi)(大到模塊,小到方法)承擔(dān)的職責(zé)越多鼎俘,它被復(fù)用的可能性就越小哲身,而且一個(gè)類(lèi)承擔(dān)的職責(zé)過(guò)多,就相當(dāng)于將這些職責(zé)耦合在一起而芥,當(dāng)其中一個(gè)職責(zé)變化時(shí)律罢,可能會(huì)影響其他職責(zé)的運(yùn)作,因此要將這些職責(zé)進(jìn)行分離棍丐,將不同的職責(zé)封裝在不同的類(lèi)中误辑,即將不同的變化原因封裝在不同的類(lèi)中,如果多個(gè)職責(zé)總是同時(shí)發(fā)生改變則可將它們封裝在同一類(lèi)中歌逢。
單一職責(zé)原則是實(shí)現(xiàn)高內(nèi)聚巾钉、低耦合的指導(dǎo)方針,它是最簡(jiǎn)單但又最難運(yùn)用的原則秘案,需要設(shè)計(jì)人員發(fā)現(xiàn)類(lèi)的不同職責(zé)并將其分離砰苍,而發(fā)現(xiàn)類(lèi)的多重職責(zé)需要設(shè)計(jì)人員具有較強(qiáng)的分析設(shè)計(jì)能力和相關(guān)實(shí)踐經(jīng)驗(yàn)。
優(yōu)點(diǎn):如果類(lèi)與方法的職責(zé)劃分得很清晰阱高,不但可以提高代碼的可讀性赚导,更實(shí)際性地更降低了程序出錯(cuò)的風(fēng)險(xiǎn),因?yàn)榍逦拇a會(huì)讓 bug 無(wú)處藏身赤惊,也有利于 bug 的追蹤吼旧,也就是降低了程序的維護(hù)成本。
2.5 迪米特法則(最少知道原則)
一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用
如果一個(gè)系統(tǒng)符合迪米特法則未舟,那么當(dāng)其中某一個(gè)模塊發(fā)生修改時(shí)圈暗,就會(huì)盡量少地影響其他模塊掂为,擴(kuò)展會(huì)相對(duì)容易,這是對(duì)軟件實(shí)體之間通信的限制员串,迪米特法則要求限制軟件實(shí)體之間通信的寬度和深度勇哗。迪米特法則可降低系統(tǒng)的耦合度,使類(lèi)與類(lèi)之間保持松散的耦合關(guān)系寸齐。
迪米特法則要求我們?cè)谠O(shè)計(jì)系統(tǒng)時(shí)欲诺,應(yīng)該盡量減少對(duì)象之間的交互,如果兩個(gè)對(duì)象之間不必彼此直接通信访忿,那么這兩個(gè)對(duì)象就不應(yīng)當(dāng)發(fā)生任何直接的相互作用瞧栗,如果其中的一個(gè)對(duì)象需要調(diào)用另一個(gè)對(duì)象的某一個(gè)方法的話,可以通過(guò)第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用海铆。簡(jiǎn)言之迹恐,就是通過(guò)引入一個(gè)合理的第三者來(lái)降低現(xiàn)有對(duì)象之間的耦合度。
在將迪米特法則運(yùn)用到系統(tǒng)設(shè)計(jì)中時(shí)卧斟,要注意下面的幾點(diǎn):在類(lèi)的劃分上殴边,應(yīng)當(dāng)盡量創(chuàng)建松耦合的類(lèi),類(lèi)之間的耦合度越低珍语,就越有利于復(fù)用锤岸,一個(gè)處在松耦合中的類(lèi)一旦被修改,不會(huì)對(duì)關(guān)聯(lián)的類(lèi)造成太大波及板乙。在類(lèi)的結(jié)構(gòu)設(shè)計(jì)上是偷,每一個(gè)類(lèi)都應(yīng)當(dāng)盡量降低其成員變量和成員函數(shù)的訪問(wèn)權(quán)限。在類(lèi)的設(shè)計(jì)上募逞,只要有可能蛋铆,一個(gè)類(lèi)型應(yīng)當(dāng)設(shè)計(jì)成不變類(lèi)。在對(duì)其他類(lèi)的引用上放接,一個(gè)對(duì)象對(duì)其他對(duì)象的引用應(yīng)當(dāng)降到最低刺啦。
優(yōu)點(diǎn):實(shí)踐迪米特法則可以良好地降低類(lèi)與類(lèi)之間的耦合,減少類(lèi)與類(lèi)之間的關(guān)聯(lián)程度纠脾,讓類(lèi)與類(lèi)之間的協(xié)作更加直接玛瘸。
2.6 接口分離原則
使用多個(gè)專門(mén)的接口,而不使用單一的總接口苟蹈,即客戶端不應(yīng)該依賴那些它不需要的接口糊渊。
根據(jù)接口隔離原則,當(dāng)一個(gè)接口太大時(shí)慧脱,我們需要將它分割成一些更細(xì)小的接口渺绒,使用該接口的客戶端僅需知道與之相關(guān)的方法即可。每一個(gè)接口應(yīng)該承擔(dān)一種相對(duì)獨(dú)立的角色,不干不該干的事芒篷,該干的事都要干。
在使用接口隔離原則時(shí)采缚,我們需要注意控制接口的粒度针炉,接口不能太小,如果太小會(huì)導(dǎo)致系統(tǒng)中接口泛濫扳抽,不利于維護(hù)篡帕。接口也不能太大,太大的接口將違背接口隔離原則贸呢,靈活性較差镰烧,使用起來(lái)很不方便。
優(yōu)點(diǎn):避免同一個(gè)接口里面包含不同類(lèi)職責(zé)的方法楞陷,接口責(zé)任劃分更加明確怔鳖,符合高內(nèi)聚低耦合的思想。
2.7 合成復(fù)用原則(六大之外的)
盡量使用對(duì)象組合固蛾,而不是繼承來(lái)達(dá)到復(fù)用的目的
合成復(fù)用原則就是在一個(gè)新的對(duì)象里通過(guò)關(guān)聯(lián)關(guān)系(包括組合關(guān)系和聚合關(guān)系)來(lái)使用一些已有的對(duì)象结执,使之成為新對(duì)象的一部分,新對(duì)象通過(guò)委派調(diào)用已有對(duì)象的方法達(dá)到復(fù)用功能的目的艾凯。簡(jiǎn)而言之献幔,復(fù)用時(shí)要盡量使用組合/聚合關(guān)系(關(guān)聯(lián)關(guān)系),少用繼承趾诗。
在面向?qū)ο笤O(shè)計(jì)中蜡感,可以通過(guò)兩種方法在不同的環(huán)境中復(fù)用已有的設(shè)計(jì)和實(shí)現(xiàn),即通過(guò)組合/聚合關(guān)系或通過(guò)繼承恃泪,但首先應(yīng)該考慮使用組合/聚合郑兴,組合/聚合可以使系統(tǒng)更加靈活,降低類(lèi)與類(lèi)之間的耦合度悟泵。一個(gè)類(lèi)的變化對(duì)其他類(lèi)造成的影響相對(duì)較少杈笔,其次才考慮繼承,在使用繼承時(shí)糕非,需要嚴(yán)格遵循里氏代換原則蒙具,有效使用繼承會(huì)有助于對(duì)問(wèn)題的理解,降低復(fù)雜度朽肥,而濫用繼承反而會(huì)增加系統(tǒng)構(gòu)建和維護(hù)的難度以及系統(tǒng)的復(fù)雜度禁筏,因此需要慎重使用繼承復(fù)用。
優(yōu)點(diǎn):避免復(fù)用時(shí)濫用繼承衡招,合理使用組合關(guān)系篱昔,增加靈活性。
2.8 六大原則 - 學(xué)習(xí)心得
六大原則中,開(kāi)閉原則州刽、里氏替換原則空执、依賴倒置原則聯(lián)系比較緊密给涕,后兩者是實(shí)現(xiàn)開(kāi)閉原則重要前提沮稚,使用中通過(guò)抽象化設(shè)計(jì)具有很好的可拓展性和可維護(hù)性。
知道最少原則可以降低耦合塞蹭,減少不必要的交互匹表,主張?jiān)O(shè)計(jì)接口和類(lèi)要簡(jiǎn)單易使用门坷,將復(fù)雜的邏輯封裝并提供簡(jiǎn)單易用的接口。
單一職責(zé)原則使項(xiàng)目中的類(lèi)和方法根據(jù)職責(zé)細(xì)分袍镀,避免單個(gè)類(lèi)負(fù)擔(dān)過(guò)重默蚌。職責(zé)越多,被復(fù)用的可能性就越小或使用起來(lái)越麻煩苇羡。
接口分離原則將功能復(fù)雜的接口細(xì)分成多個(gè)特定功能的接口绸吸,只做該做的事情,降低耦合宣虾,但是細(xì)化粒度不能太細(xì)惯裕,容易導(dǎo)致接口過(guò)多。單一職責(zé)原則強(qiáng)調(diào)單個(gè)類(lèi)內(nèi)部根據(jù)職責(zé)細(xì)分的設(shè)計(jì)绣硝,接口分離原則強(qiáng)調(diào)類(lèi)之間的耦合蜻势,盡量建立最小的依賴關(guān)系。
三鹉胖、模式分類(lèi)
《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書(shū)中設(shè)計(jì)模式有23個(gè)握玛,它們各具特色,每個(gè)模式都為某一個(gè)可重復(fù)的設(shè)計(jì)問(wèn)題提供了一套解決方案甫菠。根據(jù)它們的用途挠铲,設(shè)計(jì)模式可分為創(chuàng)建型(Creational),結(jié)構(gòu)型(Structural)和行為型(Behavioral)三種寂诱,其中創(chuàng)建型模式主要用于描述如何創(chuàng)建對(duì)象拂苹,結(jié)構(gòu)型模式主要用于描述如何實(shí)現(xiàn)類(lèi)或?qū)ο蟮慕M合,行為型模式主要用于描述類(lèi)或?qū)ο笤鯓咏换ヒ约霸鯓臃峙渎氊?zé)痰洒。
此外瓢棒,根據(jù)某個(gè)模式主要是用于處理類(lèi)之間的關(guān)系還是對(duì)象之間的關(guān)系,設(shè)計(jì)模式還可以分為類(lèi)模式和對(duì)象模式丘喻。我們經(jīng)常將兩種分類(lèi)方式結(jié)合使用脯宿,如單例模式是對(duì)象創(chuàng)建型模式,模板方法模式是類(lèi)行為型模式泉粉。
3.1 創(chuàng)建型
創(chuàng)建型模式(Creational Pattern)對(duì)類(lèi)的實(shí)例化過(guò)程進(jìn)行了抽象连霉,能夠?qū)⒛K中對(duì)象的創(chuàng)建和對(duì)象的使用分離榴芳。為了使結(jié)構(gòu)更加清晰,外界對(duì)于這些對(duì)象只需要知道它們共同的接口跺撼,而不清楚其具體的實(shí)現(xiàn)細(xì)節(jié)窟感,使整個(gè)系統(tǒng)的設(shè)計(jì)更加符合單一職責(zé)原則。
簡(jiǎn)單工廠模式(Simple Factory Pattern)
工廠方法模式(Factory Method Pattern)
抽象工廠模式(Abstract Factory Pattern)
單例模式(Singleton Pattern)
生成器模式(Builder Pattern)
原型模式(Prototype Pattern)
3.2 結(jié)構(gòu)型
結(jié)構(gòu)型模式(Structural Pattern)描述如何將類(lèi)或者對(duì) 象結(jié)合在一起形成更大的結(jié)構(gòu)歉井,就像搭積木肌括,可以通過(guò) 簡(jiǎn)單積木的組合形成復(fù)雜的、功能更為強(qiáng)大的結(jié)構(gòu)酣难。結(jié)構(gòu)型模式可以分為類(lèi)結(jié)構(gòu)型模式和對(duì)象結(jié)構(gòu)型模式:
類(lèi)結(jié)構(gòu)型模式關(guān)心類(lèi)的組合,由多個(gè)類(lèi)可以組合成一個(gè)更大的系統(tǒng)黑滴,在類(lèi)結(jié)構(gòu)型模式中一般只存在繼承關(guān)系和實(shí)現(xiàn)關(guān)系憨募。
對(duì)象結(jié)構(gòu)型模式關(guān)心類(lèi)與對(duì)象的組合,通過(guò)關(guān)聯(lián)關(guān)系使得在一 個(gè)類(lèi)中定義另一個(gè)類(lèi)的實(shí)例對(duì)象袁辈,然后通過(guò)該對(duì)象調(diào)用其方法菜谣。 根據(jù)“合成復(fù)用原則”,在系統(tǒng)中盡量使用關(guān)聯(lián)關(guān)系來(lái)替代繼 承關(guān)系晚缩,因此大部分結(jié)構(gòu)型模式都是對(duì)象結(jié)構(gòu)型模式尾膊。
外觀模式
適配器模式
橋接模式
代理模式
裝飾者模式
享元模式
3.3 行為型
行為型模式(Behavioral Pattern)是對(duì)在不同的對(duì)象之間劃分責(zé)任和算法的抽象化。行為型模式不僅僅關(guān)注類(lèi)和對(duì)象的結(jié)構(gòu)荞彼,而且重點(diǎn)關(guān)注它們之間的相互作用冈敛。通過(guò)行為型模式,可以更加清晰地劃分類(lèi)與對(duì)象的職責(zé)鸣皂,并研究系統(tǒng)在運(yùn)行時(shí)實(shí)例對(duì)象之間的交互抓谴。
職責(zé)鏈模式
命令模式
解釋器模式
迭代器模式
中介者模式
備忘錄模式
觀察者模式
狀態(tài)模式
策略模式
模板方法模式
訪問(wèn)者模式
四、創(chuàng)建型 - 設(shè)計(jì)模式
4.1 簡(jiǎn)單工廠模式
簡(jiǎn)單工廠模式(Simple Factory Pattern):專門(mén)定義一個(gè)類(lèi)(工廠類(lèi))來(lái)負(fù)責(zé)創(chuàng)建其他類(lèi)的實(shí)例寞缝“┭梗可以根據(jù)創(chuàng)建方法的參數(shù)來(lái)返回不同類(lèi)的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類(lèi)荆陆。
舉例:
簡(jiǎn)單工廠模式像一個(gè)代工廠滩届,一個(gè)工廠可以生產(chǎn)多種產(chǎn)品。舉個(gè)例子被啼,一個(gè)飲料加工廠同時(shí)幫百事可樂(lè)和可口可樂(lè)生產(chǎn)帜消,加工廠根據(jù)輸入?yún)?shù)Type來(lái)生產(chǎn)不同的產(chǎn)品。
優(yōu)點(diǎn):
使用者只需要給工廠類(lèi)傳入一個(gè)正確的約定好的參數(shù)趟据,就可以獲取你所需要的對(duì)象券犁,而不需要知道其創(chuàng)建細(xì)節(jié),一定程度上減少系統(tǒng)的耦合汹碱。
客戶端無(wú)須知道所創(chuàng)建的具體產(chǎn)品類(lèi)的類(lèi)名粘衬,只需要知道具體產(chǎn)品類(lèi)所對(duì)應(yīng)的參數(shù)即可,減少開(kāi)發(fā)者的記憶成本。
缺點(diǎn):
如果業(yè)務(wù)上添加新產(chǎn)品的話稚新,就需要修改工廠類(lèi)原有的判斷邏輯勘伺,這其實(shí)是違背了開(kāi)閉原則的。
在產(chǎn)品類(lèi)型較多時(shí)褂删,有可能造成工廠邏輯過(guò)于復(fù)雜飞醉。所以簡(jiǎn)單工廠模式比較適合產(chǎn)品種類(lèi)比較少而且增多的概率很低的情況。
4.2 工廠方法模式
工廠方法模式(Factory Method Pattern)又稱為工廠模式屯阀,工廠父類(lèi)負(fù)責(zé)定義創(chuàng)建產(chǎn)品對(duì)象的公共接口缅帘,而工廠子類(lèi)則負(fù)責(zé)生成具體的產(chǎn)品對(duì)象,即通過(guò)不同的工廠子類(lèi)來(lái)創(chuàng)建不同的產(chǎn)品對(duì)象难衰。
舉例:
工廠方法和簡(jiǎn)單工廠有一些區(qū)別钦无,簡(jiǎn)單工廠是由一個(gè)代工廠生產(chǎn)不同的產(chǎn)品,而工廠方法是對(duì)工廠進(jìn)行抽象化盖袭,不同產(chǎn)品都由專門(mén)的具體工廠來(lái)生產(chǎn)失暂。可口可樂(lè)工廠專門(mén)生產(chǎn)可口可樂(lè)鳄虱,百事可樂(lè)工廠專門(mén)生產(chǎn)百事可樂(lè)弟塞。
優(yōu)點(diǎn):
用戶只需要關(guān)心其所需產(chǎn)品對(duì)應(yīng)的具體工廠是哪一個(gè)即可,不需要關(guān)心產(chǎn)品的創(chuàng)建細(xì)節(jié)拙已,也不需要知道具體產(chǎn)品類(lèi)的類(lèi)名决记。
當(dāng)系統(tǒng)中加入新產(chǎn)品時(shí),不需要修改抽象工廠和抽象產(chǎn)品提供的接口倍踪,也無(wú)須修改客戶端和其他的具體工廠和具體產(chǎn)品霉涨,而只要添加一個(gè)具體工廠和與其對(duì)應(yīng)的具體產(chǎn)品就可以了,符合了開(kāi)閉原則惭适。
缺點(diǎn):
當(dāng)系統(tǒng)中加入新產(chǎn)品時(shí)笙瑟,除了需要提供新的產(chǎn)品類(lèi)之外,還要提供與其對(duì)應(yīng)的具體工廠類(lèi)癞志。因此系統(tǒng)中類(lèi)的個(gè)數(shù)將成對(duì)增加往枷,增加了系統(tǒng)的復(fù)雜度。
4.3 抽象工廠模式
抽象工廠模式(Abstract Factory Pattern):提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口凄杯,而無(wú)須指定它們具體的類(lèi)错洁。
舉例:
抽象工廠和工廠方法不同的地方在于,生產(chǎn)產(chǎn)品的工廠是抽象的戒突。舉例屯碴,可口可樂(lè)公司生產(chǎn)可樂(lè)的同時(shí),也需要生產(chǎn)裝可樂(lè)的瓶子和箱子膊存,瓶子和箱子也是可口可樂(lè)專屬定制的导而,同樣百事可樂(lè)公司也會(huì)有這個(gè)需求忱叭。這個(gè)時(shí)候我們的工廠不僅僅是生產(chǎn)可樂(lè)飲料的工廠,還必須同時(shí)生產(chǎn)同一主題的瓶子和箱子今艺,所以它是一個(gè)抽象的主題工廠韵丑,專門(mén)生產(chǎn)同一主題的不同商品。
優(yōu)點(diǎn):
具體產(chǎn)品在應(yīng)用層代碼隔離虚缎,不需要關(guān)心產(chǎn)品細(xì)節(jié)撵彻。只需要知道自己需要的產(chǎn)品是屬于哪個(gè)工廠的即可 當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對(duì)象被設(shè)計(jì)成一起工作時(shí),它能夠保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對(duì)象实牡。這對(duì)一些需要根據(jù)當(dāng)前環(huán)境來(lái)決定其行為的軟件系統(tǒng)來(lái)說(shuō)陌僵,是一種非常實(shí)用的設(shè)計(jì)模式。
缺點(diǎn):
規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合创坞,產(chǎn)品族中擴(kuò)展新的產(chǎn)品困難拾弃,需要修改抽象工廠的接口。
4.4 單例模式
單例模式(Singleton Pattern):?jiǎn)卫J酱_保某一個(gè)類(lèi)只有一個(gè)實(shí)例摆霉,并提供一個(gè)訪問(wèn)它的全劇訪問(wèn)點(diǎn)。
舉例:
單例模式下奔坟,對(duì)應(yīng)類(lèi)只能生成一個(gè)實(shí)例携栋。就像一個(gè)王國(guó)只能有一個(gè)國(guó)王,一旦王國(guó)里的事務(wù)多起來(lái)咳秉,這唯一的國(guó)王也容易職責(zé)過(guò)重婉支。
優(yōu)點(diǎn):
提供了對(duì)唯一實(shí)例的受控訪問(wèn)。因?yàn)閱卫?lèi)封裝了它的唯一實(shí)例澜建,所以它可以嚴(yán)格控制客戶怎樣以及何時(shí)訪問(wèn)它向挖。
因?yàn)樵擃?lèi)在系統(tǒng)內(nèi)存中只存在一個(gè)對(duì)象,所以可以節(jié)約系統(tǒng)資源炕舵。
缺點(diǎn):
由于單例模式中沒(méi)有抽象層何之,因此單例類(lèi)很難進(jìn)行擴(kuò)展。
對(duì)于有垃圾回收系統(tǒng)的語(yǔ)言 Java咽筋,C# 來(lái)說(shuō)溶推,如果對(duì)象長(zhǎng)時(shí)間不被利用,則可能會(huì)被回收奸攻。那么如果這個(gè)單例持有一些數(shù)據(jù)的話蒜危,在回收后重新實(shí)例化時(shí)就不復(fù)存在了。
4.5 生成器模式
生成器模式(Builder Pattern):也叫創(chuàng)建者模式睹耐,它將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離辐赞,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。
舉例:
生成器模式將復(fù)雜的創(chuàng)建邏輯進(jìn)行分割硝训,例如生產(chǎn)汽車(chē)响委,分步驟創(chuàng)建安裝不同的零件新思。如果創(chuàng)建邏輯簡(jiǎn)單則沒(méi)有拆分的必要。
優(yōu)點(diǎn):
客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)晃酒,將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過(guò)程解耦表牢,使得相同的創(chuàng)建過(guò)程可以創(chuàng)建不同的產(chǎn)品對(duì)象。
每一個(gè)具體建造者都相對(duì)獨(dú)立贝次,而與其他的具體建造者無(wú)關(guān)崔兴,因此可以很方便地替換具體建造者或增加新的具體建造者, 用戶使用不同的具體建造者即可得到不同的產(chǎn)品對(duì)象 蛔翅。
增加新的具體建造者無(wú)須修改原有類(lèi)庫(kù)的代碼敲茄,指揮者類(lèi)針對(duì)抽象建造者類(lèi)編程,系統(tǒng)擴(kuò)展方便山析,符合“開(kāi)閉原則”堰燎。
可以更加精細(xì)地控制產(chǎn)品的創(chuàng)建過(guò)程 。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中笋轨,使得創(chuàng)建過(guò)程更加清晰秆剪,也更方便使用程序來(lái)控制創(chuàng)建過(guò)程。
缺點(diǎn):
建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn)爵政,其組成部分相似仅讽,如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式钾挟,因此其使用范圍受到一定的限制洁灵。
如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會(huì)導(dǎo)致需要定義很多具體建造者類(lèi)來(lái)實(shí)現(xiàn)這種變化掺出,導(dǎo)致系統(tǒng)變得很龐大徽千。
4.6 原型模式
原型模式(Prototype Pattern): 使用原型實(shí)例指定待創(chuàng)建對(duì)象的類(lèi)型,并且通過(guò)復(fù)制這個(gè)原型來(lái)創(chuàng)建新的對(duì)象汤锨。
舉例:
原型模式就像復(fù)印技術(shù)双抽,根據(jù)原對(duì)象復(fù)印出一個(gè)新對(duì)象,并根據(jù)需求對(duì)新對(duì)象進(jìn)行微調(diào)闲礼。
優(yōu)點(diǎn):
可以利用原型模式簡(jiǎn)化對(duì)象的創(chuàng)建過(guò)程荠诬,尤其是對(duì)一些創(chuàng)建過(guò)程繁瑣,包含對(duì)象層級(jí)比較多的對(duì)象來(lái)說(shuō)位仁,使用原型模式可以節(jié)約系統(tǒng)資源柑贞,提高對(duì)象生成的效率。
可以很方便得通過(guò)改變值來(lái)生成新的對(duì)象:有些對(duì)象之間的差別可能只在于某些值的不同聂抢;用原型模式可以快速?gòu)?fù)制出新的對(duì)象并手動(dòng)修改值即可钧嘶。
缺點(diǎn):
對(duì)象包含的所有對(duì)象都需要配備一個(gè)克隆的方法,這就使得在對(duì)象層級(jí)比較多的情況下琳疏,代碼量會(huì)很大有决,也更加復(fù)雜闸拿。
五、結(jié)構(gòu)型 - 設(shè)計(jì)模式
5.1 裝飾模式
裝飾模式(Decorator Pattern) :不改變?cè)袑?duì)象的前提下书幕,動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的功能新荤。
舉例:
裝飾模式貼合開(kāi)閉原則,在不改變?cè)蓄?lèi)的情況下台汇,對(duì)父類(lèi)進(jìn)行改造或新增功能苛骨。舉例,定一個(gè)抽象類(lèi)Tea苟呐,只能提供白開(kāi)水痒芝,但是通過(guò)裝飾類(lèi)BlackTea裝飾之后拓展了新功能,通過(guò)BlackTea類(lèi)可以用白開(kāi)水泡紅茶牵素,還可以選擇加檸檬严衬。
優(yōu)點(diǎn):
比繼承更加靈活:不同于在編譯期起作用的繼承;裝飾者模式可以在運(yùn)行時(shí)擴(kuò)展一個(gè)對(duì)象的功能笆呆。另外也可以通過(guò)配置文件在運(yùn)行時(shí)選擇不同的裝飾器请琳,從而實(shí)現(xiàn)不同的行為。也可以通過(guò)不同的組合赠幕,可以實(shí)現(xiàn)不同效果俄精。
符合“開(kāi)閉原則”:裝飾者和被裝飾者可以獨(dú)立變化。用戶可以根據(jù)需要增加新的裝飾類(lèi)劣坊,在使用時(shí)再對(duì)其進(jìn)行組合,原有代碼無(wú)須改變屈留。
缺點(diǎn):
裝飾者模式需要?jiǎng)?chuàng)建一些具體裝飾類(lèi)局冰,會(huì)增加系統(tǒng)的復(fù)雜度。
5.2 外觀模式
外觀模式(Facade Pattern):外觀模式定義了一個(gè)高層接口灌危,為子系統(tǒng)中的一組接口提供一個(gè)統(tǒng)一的接口康二。外觀模式又稱為門(mén)面模式,它是一種結(jié)構(gòu)型設(shè)計(jì)模式模式勇蝙。
舉例:
外觀模式提供了簡(jiǎn)單明確的接口沫勿,但是在內(nèi)部眾多子系統(tǒng)功能進(jìn)行整合。就像圖片緩存味混,內(nèi)部包含了涉及到其他子系統(tǒng)的如緩存产雹、下載等處理,外觀模式將這些復(fù)雜的邏輯都隱藏了翁锡。在UIImageView和UIButton調(diào)用的時(shí)候蔓挖,你只需要調(diào)一個(gè)setImageWithUrl:(NSString *)url接口就可以了,達(dá)到解耦合的目的馆衔。
優(yōu)點(diǎn):
實(shí)現(xiàn)了客戶端與子系統(tǒng)間的解耦:客戶端無(wú)需知道子系統(tǒng)的接口瘟判,簡(jiǎn)化了客戶端調(diào)用子系統(tǒng)的調(diào)用過(guò)程怨绣,使得子系統(tǒng)使用起來(lái)更加容易。同時(shí)便于子系統(tǒng)的擴(kuò)展和維護(hù)拷获。
符合迪米特法則(最少知道原則):子系統(tǒng)只需要將需要外部調(diào)用的接口暴露給外觀類(lèi)即可篮撑,而且他的接口則可以隱藏起來(lái)。
缺點(diǎn):
違背了開(kāi)閉原則:在不引入抽象外觀類(lèi)的情況下匆瓜,增加新的子系統(tǒng)可能需要修改外觀類(lèi)或客戶端的代碼赢笨。
5.3 代理模式
代理模式(Proxy Pattern) :為某個(gè)對(duì)象提供一個(gè)代理,并由這個(gè)代理對(duì)象控制對(duì)原對(duì)象的訪問(wèn)陕壹。
舉例:
代理模式像一個(gè)房屋中介质欲,買(mǎi)家只能通過(guò)中介來(lái)買(mǎi)房,代理具備被代理類(lèi)的所有功能糠馆,就像房東有賣(mài)房功能嘶伟,中介也具有賣(mài)房功能。此外代理實(shí)例還可以幫助被代理實(shí)例進(jìn)行一些額外處理又碌,比如中介可以幫助房東篩選優(yōu)質(zhì)買(mǎi)家的功能九昧,幫助房東pass掉一些不符合條件的買(mǎi)家。還有消息隊(duì)列也是該模式毕匀。
優(yōu)點(diǎn):
降低系統(tǒng)的耦合度:代理模式能夠協(xié)調(diào)調(diào)用者和被調(diào)用者铸鹰,在一定程度上降低了系 統(tǒng)的耦合度。
不同類(lèi)型的代理可以對(duì)客戶端對(duì)目標(biāo)對(duì)象的訪問(wèn)進(jìn)行不同的控制:
遠(yuǎn)程代理,使得客戶端可以訪問(wèn)在遠(yuǎn)程機(jī)器上的對(duì)象皂岔,遠(yuǎn)程機(jī)器 可能具有更好的計(jì)算性能與處理速度蹋笼,可以快速響應(yīng)并處理客戶端請(qǐng)求。
虛擬代理通過(guò)使用一個(gè)小對(duì)象來(lái)代表一個(gè)大對(duì)象躁垛,可以減少系統(tǒng)資源的消耗剖毯,對(duì)系統(tǒng)進(jìn)行優(yōu)化并提高運(yùn)行速度。
保護(hù)代理可以控制客戶端對(duì)真實(shí)對(duì)象的使用權(quán)限教馆。
缺點(diǎn):
由于在客戶端和被代理對(duì)象之間增加了代理對(duì)象逊谋,因此可能會(huì)讓客戶端請(qǐng)求的速度變慢。
5.4 享元模式
享元模式(Flyweight Pattern):運(yùn)用共享技術(shù)復(fù)用大量細(xì)粒度的對(duì)象,降低程序內(nèi)存的占用,提高程序的性能土铺。
舉例:
例如 UITableViewCell 的緩存機(jī)制胶滋,達(dá)到降低內(nèi)存消耗的目的。舉例悲敷,音樂(lè)服務(wù)根據(jù)收費(fèi)劃分出免費(fèi)用戶和會(huì)員用戶究恤,免費(fèi)用戶只能聽(tīng)部分免費(fèi)音樂(lè),會(huì)員用戶可以聽(tīng)全部的音樂(lè)后德,并且可以下載丁溅。雖然權(quán)限上二者間有一些區(qū)別,但是他們所享受的音樂(lè)來(lái)是自于同一個(gè)音樂(lè)庫(kù)探遵,這樣所有的音樂(lè)都只需要保存一份就可以了窟赏。另外如果出現(xiàn)音樂(lè)庫(kù)里沒(méi)有的音樂(lè)時(shí)妓柜,則需要新增該音樂(lè),然后其他服務(wù)也可以享受新增的音樂(lè)涯穷,相當(dāng)于享元池或緩存池的功能棍掐。
享元模式區(qū)保證共享內(nèi)部狀態(tài)如音樂(lè)庫(kù),而外部狀態(tài)根據(jù)不同需求定制如各種訪問(wèn)權(quán)限拷况,使用中不能去改變內(nèi)部狀態(tài)作煌,以達(dá)到共享的目的。
優(yōu)點(diǎn):
使用享元淖荩可以減少內(nèi)存中對(duì)象的數(shù)量粟誓,使得相同對(duì)象或相似對(duì)象在內(nèi)存中只保存一份,降低系統(tǒng)的使用內(nèi)存起意,也可以提性能鹰服。
享元模式的外部狀態(tài)相對(duì)獨(dú)立,而且不會(huì)影響其內(nèi)部狀態(tài)揽咕,從而使得享元對(duì)象可以在不同的環(huán)境中被共享悲酷。
缺點(diǎn):
使用享元模式需要分離出內(nèi)部狀態(tài)和外部狀態(tài),這使得程序的邏輯復(fù)雜化亲善。
對(duì)象在緩沖池中的復(fù)用需要考慮線程問(wèn)題设易。
5.5 橋接模式
橋接模式(Simple Factory Pattern):將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化啦吧。
舉例:
盡管手機(jī)都有各自的不同之處屡限,但是他們都有一個(gè)手機(jī)卡卡槽兢榨,卡槽里可以插不同運(yùn)營(yíng)商的卡湃密。不管手機(jī)和卡內(nèi)部如何改變,只要卡槽的行業(yè)標(biāo)準(zhǔn)沒(méi)有變芹彬,就都可以正常使用携添。橋接模式在于將復(fù)雜的類(lèi)進(jìn)行分割巩梢,優(yōu)先對(duì)象組合的方式袍睡,就像將手機(jī)里的手機(jī)卡抽離出去新建一個(gè)類(lèi)知染,實(shí)現(xiàn)手機(jī)實(shí)例持有一個(gè)手機(jī)卡實(shí)例的組合方式肋僧。而不是通過(guò)繼承來(lái)新建多個(gè)不同手機(jī)卡的手機(jī)子類(lèi)斑胜。
SIMCardProtocol?協(xié)議相當(dāng)于行業(yè)標(biāo)準(zhǔn),所以手機(jī)卡都要遵循該協(xié)議嫌吠。而各個(gè)手機(jī)生產(chǎn)商知道該協(xié)議止潘,就可以直接利用該協(xié)議獲得 SIM 卡內(nèi)部信息。
優(yōu)點(diǎn):
擴(kuò)展性好辫诅,符合開(kāi)閉原則:將抽象與實(shí)現(xiàn)分離凭戴,讓二者可以獨(dú)立變化
缺點(diǎn):
在設(shè)計(jì)之前,需要識(shí)別出兩個(gè)獨(dú)立變化的維度炕矮。
5.6 適配器模式
適配器模式(Adapter Pattern) :將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口么夫,使得原本由于接口不兼容而不能一起工作的那些類(lèi)可以一起工作者冤。適配器模式的別名是包裝器模式(Wrapper),是一種結(jié)構(gòu)型設(shè)計(jì)模式档痪。
舉例:
適配器模式顧名思義涉枫,比如內(nèi)地用像港版插頭需要一個(gè)轉(zhuǎn)接頭。再比如iPhone的手機(jī)卡是特別小的 Nano 卡腐螟,把 Nano 卡拿到其他手機(jī)上不能貼合卡槽尺寸愿汰,所以我們需要加一個(gè)符合卡槽尺寸的卡套。
優(yōu)點(diǎn):
符合開(kāi)閉原則:使用適配器而不需要改變現(xiàn)有類(lèi)乐纸,提高類(lèi)的復(fù)用性衬廷。
目標(biāo)類(lèi)和適配器類(lèi)解耦,提高程序擴(kuò)展性汽绢。
缺點(diǎn):
增加了系統(tǒng)的復(fù)雜性
六吗跋、行為型 - 設(shè)計(jì)模式
6.1 職責(zé)鏈模式
職責(zé)鏈模式(Chain of Responsibility Pattern):避免請(qǐng)求發(fā)送者與接收者耦合在一起,讓多個(gè)對(duì)象都有可能接收請(qǐng)求庶喜,將這些對(duì)象連接成一條鏈小腊,并且沿著這條鏈傳遞請(qǐng)求,直到有對(duì)象處理它為止久窟。職責(zé)鏈模式是一種對(duì)象行為型模式秩冈。
舉例:
職責(zé)鏈模式在 iOS 中有大量的應(yīng)用,比如事件響應(yīng)鏈斥扛,事件傳遞下來(lái)會(huì)先判斷該事件是不是應(yīng)該由自己處理入问,如果不是由自己處理則傳給下一位響應(yīng)者去處理,如此循環(huán)下去稀颁。需要注意的是要避免響應(yīng)鏈循環(huán)調(diào)用造成死循環(huán)芬失,還有當(dāng)所有的響應(yīng)者都無(wú)法處理時(shí)的情況。
優(yōu)點(diǎn):
職責(zé)鏈模式使得一個(gè)對(duì)象無(wú)須知道是其他哪一個(gè)對(duì)象處理其請(qǐng)求匾灶,對(duì)象僅需知道該請(qǐng)求會(huì)被處理即可棱烂,接收者和發(fā)送者都沒(méi)有對(duì)方的明確信息,且鏈中的對(duì)象不需要知道鏈的結(jié)構(gòu)阶女,由客戶端負(fù)責(zé)鏈的創(chuàng)建颊糜,降低了系統(tǒng)的耦合度。
請(qǐng)求處理對(duì)象僅需維持一個(gè)指向其后繼者的引用秃踩,而不需要維持它對(duì)所有的候選處理者的引用衬鱼,可簡(jiǎn)化對(duì)象的相互連接。
在給對(duì)象分派職責(zé)時(shí)憔杨,職責(zé)鏈可以給我們更多的靈活性鸟赫,可以通過(guò)在運(yùn)行時(shí)對(duì)該鏈進(jìn)行動(dòng)態(tài)的增加或修改來(lái)增加或改變處理一個(gè)請(qǐng)求的職責(zé)。
在系統(tǒng)中增加一個(gè)新的具體請(qǐng)求處理者時(shí)無(wú)須修改原有系統(tǒng)的代碼,只需要在客戶端重新建鏈即可抛蚤,從這一點(diǎn)來(lái)看是符合“開(kāi)閉原則”的台谢。
缺點(diǎn):
由于一個(gè)請(qǐng)求沒(méi)有明確的接收者,那么就不能保證它一定會(huì)被處理岁经,該請(qǐng)求可能一直到鏈的末端都得不到處理对碌;一個(gè)請(qǐng)求也可能因職責(zé)鏈沒(méi)有被正確配置而得不到處理。
對(duì)于比較長(zhǎng)的職責(zé)鏈蒿偎,請(qǐng)求的處理可能涉及到多個(gè)處理對(duì)象朽们,系統(tǒng)性能將受到一定影響,而且在進(jìn)行代碼調(diào)試時(shí)不太方便诉位。
如果建鏈不當(dāng)骑脱,可能會(huì)造成循環(huán)調(diào)用,將導(dǎo)致系統(tǒng)陷入死循環(huán)苍糠。
6.2 命令模式
命令模式(Command Pattern):將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象叁丧,從而讓我們可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志岳瞭,以及支持可撤銷(xiāo)的操作拥娄。命令模式是一種對(duì)象行為型模式,其別名為動(dòng)作(Action)模式或事務(wù)(Transaction)模式瞳筏。
舉例:
和之前代理模式中的舉例有些相似稚瘾,不過(guò)命令模式的本質(zhì)是對(duì)命令進(jìn)行封裝,將發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開(kāi)姚炕。例如遙控器是一個(gè)調(diào)用者摊欠,不同按鈕代表不同的命令,而電視是接收者柱宦。
優(yōu)點(diǎn):
降低系統(tǒng)的耦合度些椒。由于請(qǐng)求者與接收者之間不存在直接引用,因此請(qǐng)求者與接收者之間實(shí)現(xiàn)完全解耦掸刊,相同的請(qǐng)求者可以對(duì)應(yīng)不同的接收者免糕,同樣,相同的接收者也可以供不同的請(qǐng)求者使用忧侧,兩者之間具有良好的獨(dú)立性石窑。
新的命令可以很容易地加入到系統(tǒng)中。由于增加新的具體命令類(lèi)不會(huì)影響到其他類(lèi)苍柏,因此增加新的具體命令類(lèi)很容易尼斧,無(wú)須修改原有系統(tǒng)源代碼姜贡,甚至客戶類(lèi)代碼试吁,滿足“開(kāi)閉原則”的要求。
可以比較容易地設(shè)計(jì)一個(gè)命令隊(duì)列或宏命令(組合命令)。
為請(qǐng)求的撤銷(xiāo)(Undo)和恢復(fù)(Redo)操作提供了一種設(shè)計(jì)和實(shí)現(xiàn)方案熄捍。
缺點(diǎn):
使用命令模式可能會(huì)導(dǎo)致某些系統(tǒng)有過(guò)多的具體命令類(lèi)烛恤。因?yàn)獒槍?duì)每一個(gè)對(duì)請(qǐng)求接收者的調(diào)用操作都需要設(shè)計(jì)一個(gè)具體命令類(lèi),因此在某些系統(tǒng)中可能需要提供大量的具體命令類(lèi)余耽,這將影響命令模式的使用缚柏。
6.3 解釋器模式
解釋器模式(Interpreter Pattern):定義一個(gè)語(yǔ)言的文法,并且建立一個(gè)解釋器來(lái)解釋該語(yǔ)言中的句子碟贾,這里的“語(yǔ)言”是指使用規(guī)定格式和語(yǔ)法的代碼币喧。解釋器模式是一種類(lèi)行為型模式。
舉例:
說(shuō)到解釋器模式袱耽,我們的編譯器杀餐,在對(duì)代碼進(jìn)行編譯的時(shí)候也用到了該模式。我們可以直接來(lái)做一個(gè)簡(jiǎn)單的解釋器朱巨,一個(gè)給機(jī)器人下發(fā)指令的解釋器史翘。
命令參數(shù)
direction 移動(dòng)方向'up' 'down' 'left' 'right'
action 移動(dòng)方式'move' 'run'
distance 移動(dòng)距離an integer
表達(dá)式終結(jié)符號(hào)';'
通過(guò)建立一個(gè)映射關(guān)系可以很快將指令轉(zhuǎn)換成行為,例如up run 5;表示向上跑5米冀续,而left move 12;表示向左移動(dòng)12米琼讽。
優(yōu)點(diǎn):
易于改變和擴(kuò)展文法。由于在解釋器模式中使用類(lèi)來(lái)表示語(yǔ)言的文法規(guī)則洪唐,因此可以通過(guò)繼承等機(jī)制來(lái)改變或擴(kuò)展文法钻蹬。
每一條文法規(guī)則都可以表示為一個(gè)類(lèi),因此可以方便地實(shí)現(xiàn)一個(gè)簡(jiǎn)單的語(yǔ)言凭需。
實(shí)現(xiàn)文法較為容易脉让。在抽象語(yǔ)法樹(shù)中每一個(gè)表達(dá)式節(jié)點(diǎn)類(lèi)的實(shí)現(xiàn)方式都是相似的,這些類(lèi)的代碼編寫(xiě)都不會(huì)特別復(fù)雜功炮,還可以通過(guò)一些工具自動(dòng)生成節(jié)點(diǎn)類(lèi)代碼溅潜。
增加新的解釋表達(dá)式較為方便。如果用戶需要增加新的解釋表達(dá)式只需要對(duì)應(yīng)增加一個(gè)新的終結(jié)符表達(dá)式或非終結(jié)符表達(dá)式類(lèi)薪伏,原有表達(dá)式類(lèi)代碼無(wú)須修改滚澜,符合“開(kāi)閉原則”。
缺點(diǎn):
對(duì)于復(fù)雜文法難以維護(hù)嫁怀。在解釋器模式中设捐,每一條規(guī)則至少需要定義一個(gè)類(lèi),因此如果一個(gè)語(yǔ)言包含太多文法規(guī)則塘淑,類(lèi)的個(gè)數(shù)將會(huì)急劇增加萝招,導(dǎo)致系統(tǒng)難以管理和維護(hù),此時(shí)可以考慮使用語(yǔ)法分析程序等方式來(lái)取代解釋器模式存捺。
執(zhí)行效率較低槐沼。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用曙蒸,因此在解釋較為復(fù)雜的句子時(shí)其速度很慢,而且代碼的調(diào)試過(guò)程也比較麻煩岗钩。
6.4 迭代器模式
迭代器模式(Iterator Pattern):提供一種方法來(lái)訪問(wèn)聚合對(duì)象纽窟,而不用暴露這個(gè)對(duì)象的內(nèi)部表示,其別名為游標(biāo)(Cursor)兼吓。迭代器模式是一種對(duì)象行為型模式臂港。
舉例:
迭代器幫助請(qǐng)求方獲取數(shù)據(jù),避免直接操作數(shù)據(jù)聚合類(lèi)视搏,使數(shù)據(jù)聚合類(lèi)專注存儲(chǔ)數(shù)據(jù)审孽。具體應(yīng)用有分頁(yè)等功能,分頁(yè)功能的迭代器將專門(mén)負(fù)責(zé)操作分頁(yè)數(shù)據(jù)浑娜,將操作邏輯和數(shù)據(jù)源分離瓷胧。
優(yōu)點(diǎn):
它支持以不同的方式遍歷一個(gè)聚合對(duì)象,在同一個(gè)聚合對(duì)象上可以定義多種遍歷方式棚愤。在迭代器模式中只需要用一個(gè)不同的迭代器來(lái)替換原有迭代器即可改變遍歷算法搓萧,我們也可以自己定義迭代器的子類(lèi)以支持新的遍歷方式。
迭代器簡(jiǎn)化了聚合類(lèi)宛畦。由于引入了迭代器瘸洛,在原有的聚合對(duì)象中不需要再自行提供數(shù)據(jù)遍歷等方法,這樣可以簡(jiǎn)化聚合類(lèi)的設(shè)計(jì)次和。
在迭代器模式中反肋,由于引入了抽象層,增加新的聚合類(lèi)和迭代器類(lèi)都很方便踏施,無(wú)須修改原有代碼石蔗,滿足“開(kāi)閉原則”的要求。
缺點(diǎn):
由于迭代器模式將存儲(chǔ)數(shù)據(jù)和遍歷數(shù)據(jù)的職責(zé)分離畅形,增加新的聚合類(lèi)需要對(duì)應(yīng)增加新的迭代器類(lèi)养距,類(lèi)的個(gè)數(shù)成對(duì)增加,這在一定程度上增加了系統(tǒng)的復(fù)雜性日熬。
抽象迭代器的設(shè)計(jì)難度較大棍厌,需要充分考慮到系統(tǒng)將來(lái)的擴(kuò)展,例如JDK內(nèi)置迭代器Iterator就無(wú)法實(shí)現(xiàn)逆向遍歷竖席,如果需要實(shí)現(xiàn)逆向遍歷耘纱,只能通過(guò)其子類(lèi)ListIterator等來(lái)實(shí)現(xiàn),而ListIterator迭代器無(wú)法用于操作Set類(lèi)型的聚合對(duì)象毕荐。在自定義迭代器時(shí)束析,創(chuàng)建一個(gè)考慮全面的抽象迭代器并不是件很容易的事情。
6.5 中介者模式
中介者模式(Mediator Pattern):用一個(gè)中介對(duì)象(中介者)來(lái)封裝一系列的對(duì)象交互憎亚,中介者使各對(duì)象不需要顯式地相互引用员寇,從而使其耦合松散弄慰,而且可以獨(dú)立地改變它們之間的交互。中介者模式又稱為調(diào)停者模式丁恭,它是一種對(duì)象行為型模式。
舉例:
中介者模式將一個(gè)網(wǎng)狀的系統(tǒng)結(jié)構(gòu)變成一個(gè)以中介者對(duì)象為中心的星形結(jié)構(gòu)斋日,在這個(gè)星型結(jié)構(gòu)中牲览,使用中介者對(duì)象與其他對(duì)象的一對(duì)多關(guān)系來(lái)取代原有對(duì)象之間的多對(duì)多關(guān)系。所有成員通過(guò)中介者交互恶守,方便拓展新的成員第献,例如下面的例子,新增一個(gè)聊天室成員只需要新建一個(gè)成員實(shí)例兔港,然后再在聊天室中介者那注冊(cè)就可以加入聊天室了庸毫。
優(yōu)點(diǎn):
中介者模式簡(jiǎn)化了對(duì)象之間的交互,它用中介者和同事的一對(duì)多交互代替了原來(lái)同事之間的多對(duì)多交互衫樊,一對(duì)多關(guān)系更容易理解飒赃、維護(hù)和擴(kuò)展,將原本難以理解的網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)換成相對(duì)簡(jiǎn)單的星型結(jié)構(gòu)科侈。
中介者模式可將各同事對(duì)象解耦载佳。中介者有利于各同事之間的松耦合,我們可以獨(dú)立的改變和復(fù)用每一個(gè)同事和中介者臀栈,增加新的中介者和新的同事類(lèi)都比較方便蔫慧,更好地符合“開(kāi)閉原則”。
可以減少子類(lèi)生成权薯,中介者將原本分布于多個(gè)對(duì)象間的行為集中在一起姑躲,改變這些行為只需生成新的中介者子類(lèi)即可,這使各個(gè)同事類(lèi)可被重用盟蚣,無(wú)須對(duì)同事類(lèi)進(jìn)行擴(kuò)展黍析。
缺點(diǎn):
在具體中介者類(lèi)中包含了大量同事之間的交互細(xì)節(jié),可能會(huì)導(dǎo)致具體中介者類(lèi)非常復(fù)雜屎开,使得系統(tǒng)難以維護(hù)橄仍。
6.6 備忘錄模式
備忘錄模式(Memento Pattern):在不破壞封裝的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài)牍戚,并在該對(duì)象之外保存這個(gè)狀態(tài)侮繁,這樣可以在以后將對(duì)象恢復(fù)到原先保存的狀態(tài)。它是一種對(duì)象行為型模式如孝,其別名為T(mén)oken宪哩。
舉例:
備忘錄模式提供了一種狀態(tài)恢復(fù)的實(shí)現(xiàn)機(jī)制,使得用戶可以方便地回到一個(gè)特定的歷史步驟第晰,當(dāng)新的狀態(tài)無(wú)效或者存在問(wèn)題時(shí)锁孟,可以使用暫時(shí)存儲(chǔ)起來(lái)的備忘錄將狀態(tài)復(fù)原彬祖,當(dāng)前很多軟件都提供了撤銷(xiāo)操作,其中就使用了備忘錄模式品抽。
我們用一個(gè)簡(jiǎn)單的游戲存檔來(lái)舉例储笑,這也是備忘錄模式的一種應(yīng)用。
優(yōu)點(diǎn):
它提供了一種狀態(tài)恢復(fù)的實(shí)現(xiàn)機(jī)制圆恤,使得用戶可以方便地回到一個(gè)特定的歷史步驟突倍,當(dāng)新的狀態(tài)無(wú)效或者存在問(wèn)題時(shí),可以使用暫時(shí)存儲(chǔ)起來(lái)的備忘錄將狀態(tài)復(fù)原盆昙。
備忘錄實(shí)現(xiàn)了對(duì)信息的封裝羽历,一個(gè)備忘錄對(duì)象是一種原發(fā)器對(duì)象狀態(tài)的表示,不會(huì)被其他代碼所改動(dòng)淡喜。備忘錄保存了原發(fā)器的狀態(tài)秕磷,采用列表、堆棧等集合來(lái)存儲(chǔ)備忘錄對(duì)象可以實(shí)現(xiàn)多次撤銷(xiāo)操作炼团。
缺點(diǎn):
資源消耗過(guò)大澎嚣,如果需要保存的原發(fā)器類(lèi)的成員變量太多,就不可避免需要占用大量的存儲(chǔ)空間瘟芝,每保存一次對(duì)象的狀態(tài)都需要消耗一定的系統(tǒng)資源币叹。
6.7 觀察者模式
觀察者模式(Observer Pattern):定義對(duì)象之間的一種一對(duì)多依賴關(guān)系,使得每當(dāng)一個(gè)對(duì)象狀態(tài)發(fā)生改變時(shí)模狭,其相關(guān)依賴對(duì)象皆得到通知并被自動(dòng)更新颈抚。觀察者模式的別名包括發(fā)布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式嚼鹉、源-監(jiān)聽(tīng)器(Source/Listener)模式或從屬者(Dependents)模式贩汉。觀察者模式是一種對(duì)象行為型模式。
舉例:
觀察者模式是使用頻率最高的設(shè)計(jì)模式之一锚赤,它用于建立一種對(duì)象與對(duì)象之間的依賴關(guān)系匹舞,一個(gè)對(duì)象發(fā)生改變時(shí)將自動(dòng)通知其他對(duì)象,其他對(duì)象將相應(yīng)作出反應(yīng)线脚。
在 iOS 中赐稽,觀察者模式經(jīng)常使用到,下面我就用 KVO 實(shí)現(xiàn)了一個(gè)通過(guò)氣象臺(tái)觀察天氣變化的簡(jiǎn)單例子浑侥。
優(yōu)點(diǎn):
觀察者模式可以實(shí)現(xiàn)表示層和數(shù)據(jù)邏輯層的分離姊舵,定義了穩(wěn)定的消息更新傳遞機(jī)制,并抽象了更新接口寓落,使得可以有各種各樣不同的表示層充當(dāng)具體觀察者角色括丁。
觀察者模式在觀察目標(biāo)和觀察者之間建立一個(gè)抽象的耦合。觀察目標(biāo)只需要維持一個(gè)抽象觀察者的集合伶选,無(wú)須了解其具體觀察者史飞。由于觀察目標(biāo)和觀察者沒(méi)有緊密地耦合在一起尖昏,因此它們可以屬于不同的抽象化層次。
觀察者模式支持廣播通信构资,觀察目標(biāo)會(huì)向所有已注冊(cè)的觀察者對(duì)象發(fā)送通知抽诉,簡(jiǎn)化了一對(duì)多系統(tǒng)設(shè)計(jì)的難度。
觀察者模式滿足“開(kāi)閉原則”的要求吐绵,增加新的具體觀察者無(wú)須修改原有系統(tǒng)代碼迹淌,在具體觀察者與觀察目標(biāo)之間不存在關(guān)聯(lián)關(guān)系的情況下,增加新的觀察目標(biāo)也很方便拦赠。
缺點(diǎn):
如果一個(gè)觀察目標(biāo)對(duì)象有很多直接和間接觀察者巍沙,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間葵姥。
如果在觀察者和觀察目標(biāo)之間存在循環(huán)依賴荷鼠,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰榔幸。
觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的允乐,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。
6.8 狀態(tài)模式
狀態(tài)模式(State Pattern):允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為削咆,對(duì)象看起來(lái)似乎修改了它的類(lèi)牍疏。其別名為狀態(tài)對(duì)象(Objects for States),狀態(tài)模式是一種對(duì)象行為型模式拨齐。
舉例:
狀態(tài)模式用于解決復(fù)雜對(duì)象的狀態(tài)轉(zhuǎn)換以及不同狀態(tài)下行為的封裝問(wèn)題鳞陨。當(dāng)系統(tǒng)中某個(gè)對(duì)象存在多個(gè)狀態(tài),這些狀態(tài)之間可以進(jìn)行轉(zhuǎn)換瞻惋,所以對(duì)象在不同狀態(tài)下具有不同行為時(shí)可以使用狀態(tài)模式厦滤。狀態(tài)模式將一個(gè)對(duì)象的狀態(tài)從該對(duì)象中分離出來(lái),封裝到專門(mén)的狀態(tài)類(lèi)中歼狼,使得對(duì)象狀態(tài)可以靈活變化掏导。
我們可以做一個(gè)簡(jiǎn)單的例子,我設(shè)計(jì)了一個(gè)銀行賬戶系統(tǒng)羽峰,根據(jù)存錢(qián)余額來(lái)自動(dòng)設(shè)置賬戶的狀態(tài)趟咆,銀行賬戶在不同狀態(tài)下,進(jìn)行存錢(qián)梅屉、取錢(qián)和借錢(qián)的行為值纱。在不同狀態(tài)下,這些行為得到的回復(fù)也不一樣坯汤,比如說(shuō)沒(méi)有余額時(shí)無(wú)法取錢(qián)计雌,只能借錢(qián)。
優(yōu)點(diǎn):
封裝了狀態(tài)的轉(zhuǎn)換規(guī)則玫霎,在狀態(tài)模式中可以將狀態(tài)的轉(zhuǎn)換代碼封裝在環(huán)境類(lèi)或者具體狀態(tài)類(lèi)中凿滤,可以對(duì)狀態(tài)轉(zhuǎn)換代碼進(jìn)行集中管理妈橄,而不是分散在一個(gè)個(gè)業(yè)務(wù)方法中。
將所有與某個(gè)狀態(tài)有關(guān)的行為放到一個(gè)類(lèi)中翁脆,只需要注入一個(gè)不同的狀態(tài)對(duì)象即可使環(huán)境對(duì)象擁有不同的行為眷蚓。
允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對(duì)象合成一體,而不是提供一個(gè)巨大的條件語(yǔ)句塊反番,狀態(tài)模式可以讓我們避免使用龐大的條件語(yǔ)句來(lái)將業(yè)務(wù)方法和狀態(tài)轉(zhuǎn)換代碼交織在一起沙热。
可以讓多個(gè)環(huán)境對(duì)象共享一個(gè)狀態(tài)對(duì)象,從而減少系統(tǒng)中對(duì)象的個(gè)數(shù)罢缸。
缺點(diǎn):
狀態(tài)模式的使用必然會(huì)增加系統(tǒng)中類(lèi)和對(duì)象的個(gè)數(shù)篙贸,導(dǎo)致系統(tǒng)運(yùn)行開(kāi)銷(xiāo)增大。
狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜枫疆,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂爵川,增加系統(tǒng)設(shè)計(jì)的難度。
狀態(tài)模式對(duì)“開(kāi)閉原則”的支持并不太好息楔,增加新的狀態(tài)類(lèi)需要修改那些負(fù)責(zé)狀態(tài)轉(zhuǎn)換的源代碼寝贡,否則無(wú)法轉(zhuǎn)換到新增狀態(tài);而且修改某個(gè)狀態(tài)類(lèi)的行為也需修改對(duì)應(yīng)類(lèi)的源代碼值依。
6.9 策略模式
策略模式(Strategy Pattern):定義一系列算法類(lèi)圃泡,將每一個(gè)算法封裝起來(lái),并讓它們可以相互替換愿险,策略模式讓算法獨(dú)立于使用它的客戶而變化颇蜡,也稱為政策模式(Policy)。策略模式是一種對(duì)象行為型模式辆亏。
舉例:
使用策略模式時(shí)风秤,我們可以定義一些策略類(lèi),每一個(gè)策略類(lèi)中封裝一種具體的算法褒链。在這里唁情,每一個(gè)封裝算法的類(lèi)我們都可以稱之為一種策略,根據(jù)傳入不同的策略類(lèi)甫匹,使環(huán)境類(lèi)執(zhí)行不同策略類(lèi)中的算法甸鸟。
生活中也有很多類(lèi)似的例子,就比如說(shuō)商城的會(huì)員卡機(jī)制兵迅。我們?nèi)ド坛琴?gòu)物可以通過(guò)持有的會(huì)員卡打折抢韭,購(gòu)買(mǎi)同一件商品時(shí)替裆,持有不同等級(jí)的會(huì)員卡驹尼,能得到不同力度的折扣。下面的例子中我列舉了青銅硼一、白銀、黃金三種 Vip 會(huì)員卡鳍贾,傳入不同的會(huì)員卡最終需要支付的金額也會(huì)有所不同鞍匾。
優(yōu)點(diǎn):
策略模式提供了對(duì)“開(kāi)閉原則”的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為骑科,也可以靈活地增加新的算法或行為橡淑。
策略模式提供了管理相關(guān)的算法族的辦法。策略類(lèi)的等級(jí)結(jié)構(gòu)定義了一個(gè)算法或行為族咆爽,恰當(dāng)使用繼承可以把公共的代碼移到抽象策略類(lèi)中梁棠,從而避免重復(fù)的代碼。
策略模式提供了一種可以替換繼承關(guān)系的辦法斗埂。如果不使用策略模式符糊,那么使用算法的環(huán)境類(lèi)就可能會(huì)有一些子類(lèi),每一個(gè)子類(lèi)提供一種不同的算法呛凶。但是男娄,這樣一來(lái)算法的使用就和算法本身混在一起,不符合“單一職責(zé)原則”把兔,決定使用哪一種算法的邏輯和該算法本身混合在一起沪伙,從而不可能再獨(dú)立演化瓮顽;而且使用繼承無(wú)法實(shí)現(xiàn)算法或行為在程序運(yùn)行時(shí)的動(dòng)態(tài)切換县好。
使用策略模式可以避免多重條件選擇語(yǔ)句。多重條件選擇語(yǔ)句不易維護(hù)暖混,它把采取哪一種算法或行為的邏輯與算法或行為本身的實(shí)現(xiàn)邏輯混合在一起缕贡,將它們?nèi)坑簿幋a(Hard Coding)在一個(gè)龐大的多重條件選擇語(yǔ)句中,比直接繼承環(huán)境類(lèi)的辦法還要原始和落后拣播。
策略模式提供了一種算法的復(fù)用機(jī)制晾咪,由于將算法單獨(dú)提取出來(lái)封裝在策略類(lèi)中,因此不同的環(huán)境類(lèi)可以方便地復(fù)用這些策略類(lèi)贮配。
缺點(diǎn):
客戶端必須知道所有的策略類(lèi)谍倦,并自行決定使用哪一個(gè)策略類(lèi)。這就意味著客戶端必須理解這些算法的區(qū)別泪勒,以便適時(shí)選擇恰當(dāng)?shù)乃惴ㄖ缰Q言之,策略模式只適用于客戶端知道所有的算法或行為的情況圆存。
策略模式將造成系統(tǒng)產(chǎn)生很多具體策略類(lèi)叼旋,任何細(xì)小的變化都將導(dǎo)致系統(tǒng)要增加一個(gè)新的具體策略類(lèi)。
無(wú)法同時(shí)在客戶端使用多個(gè)策略類(lèi)沦辙,也就是說(shuō)夫植,在使用策略模式時(shí),客戶端每次只能使用一個(gè)策略類(lèi)油讯,不支持使用一個(gè)策略類(lèi)完成部分功能后再使用另一個(gè)策略類(lèi)來(lái)完成剩余功能的情況详民。
6.10 模板方法模式
模板方法模式:定義一個(gè)操作中算法的框架延欠,而將一些步驟延遲到子類(lèi)中。模板方法模式使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟沈跨。
舉例:
模板方法模式在 iOS 中的應(yīng)用也非常多衫冻,如 UIViewController 的生命周期函數(shù),定義在父類(lèi)谒出,子類(lèi)可以重寫(xiě)這些函數(shù)隅俘。
模板方法模式具體應(yīng)用又分為三類(lèi):
抽象方法:一個(gè)抽象方法由抽象類(lèi)聲明、由其具體子類(lèi)實(shí)現(xiàn)笤喳。
具體方法:一個(gè)具體方法由一個(gè)抽象類(lèi)或具體類(lèi)聲明并實(shí)現(xiàn)为居,其子類(lèi)可以進(jìn)行覆蓋也可以直接繼承。
鉤子方法:一個(gè)鉤子方法由一個(gè)抽象類(lèi)或具體類(lèi)聲明并實(shí)現(xiàn)杀狡,而其子類(lèi)可能會(huì)加以擴(kuò)展蒙畴。通常在父類(lèi)中給出的實(shí)現(xiàn)是一個(gè)空實(shí)現(xiàn),并以該空實(shí)現(xiàn)作為方法的默認(rèn)實(shí)現(xiàn)膳凝,當(dāng)然鉤子方法也可以提供一個(gè)非空的默認(rèn)實(shí)現(xiàn)。通過(guò)在子類(lèi)中實(shí)現(xiàn)的鉤子方法對(duì)父類(lèi)方法的執(zhí)行進(jìn)行約束恭陡,實(shí)現(xiàn)子類(lèi)對(duì)父類(lèi)行為的反向控制蹬音。
下面給出一個(gè)例子,在給定一個(gè)有固定模板的烹飪教程的情況下休玩,根據(jù)不同烹飪需求對(duì)教程中的內(nèi)容進(jìn)行動(dòng)態(tài)調(diào)整著淆。
第一個(gè)步驟prepareIngredients, 父類(lèi)中沒(méi)有具體實(shí)現(xiàn)為抽象方法拴疤,子類(lèi)中直接覆蓋永部。
第二個(gè)步驟加食用油方法addFat被鉤子方法isHealthyFood?給跳過(guò)了。
第三步addIngredients?在父類(lèi)中同樣是抽象方法呐矾,子類(lèi)直接覆蓋苔埋。
第四步addFlavouring?在父類(lèi)中有具體實(shí)現(xiàn),子類(lèi)繼承父類(lèi)的「加鹽操作」后又增加了新的「加黑胡椒」操作蜒犯。
優(yōu)點(diǎn):
在父類(lèi)中形式化地定義一個(gè)算法组橄,而由它的子類(lèi)來(lái)實(shí)現(xiàn)細(xì)節(jié)的處理,在子類(lèi)實(shí)現(xiàn)詳細(xì)的處理算法時(shí)并不會(huì)改變算法中步驟的執(zhí)行次序愧薛。
模板方法模式是一種代碼復(fù)用技術(shù)晨炕,它在類(lèi)庫(kù)設(shè)計(jì)中尤為重要,它提取了類(lèi)庫(kù)中的公共行為毫炉,將公共行為放在父類(lèi)中瓮栗,而通過(guò)其子類(lèi)來(lái)實(shí)現(xiàn)不同的行為,它鼓勵(lì)我們恰當(dāng)使用繼承來(lái)實(shí)現(xiàn)代碼復(fù)用。
可實(shí)現(xiàn)一種反向控制結(jié)構(gòu)费奸,通過(guò)子類(lèi)覆蓋父類(lèi)的鉤子方法來(lái)決定某一特定步驟是否需要執(zhí)行弥激。
在模板方法模式中可以通過(guò)子類(lèi)來(lái)覆蓋父類(lèi)的基本方法,不同的子類(lèi)可以提供基本方法的不同實(shí)現(xiàn)愿阐,更換和增加新的子類(lèi)很方便微服,符合單一職責(zé)原則和開(kāi)閉原則。
缺點(diǎn):
需要為每一個(gè)基本方法的不同實(shí)現(xiàn)提供一個(gè)子類(lèi)缨历,如果父類(lèi)中可變的基本方法太多以蕴,將會(huì)導(dǎo)致類(lèi)的個(gè)數(shù)增加,系統(tǒng)更加龐大辛孵,設(shè)計(jì)也更加抽象丛肮,此時(shí),可結(jié)合橋接模式來(lái)進(jìn)行設(shè)計(jì)魄缚。
6.11 訪問(wèn)者模式
訪問(wèn)者模式(Visitor Pattern):提供一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作表示宝与,它使我們可以在不改變各元素的類(lèi)的前提下定義作用于這些元素的新操作。訪問(wèn)者模式是一種對(duì)象行為型模式冶匹。
舉例:
訪問(wèn)者模式是一種較為復(fù)雜的行為型設(shè)計(jì)模式习劫,它包含訪問(wèn)者和被訪問(wèn)元素兩個(gè)主要組成部分,這些被訪問(wèn)的元素通常具有不同的類(lèi)型嚼隘,且不同的訪問(wèn)者可以對(duì)它們進(jìn)行不同的訪問(wèn)操作诽里。訪問(wèn)者模式使得用戶可以在不修改現(xiàn)有系統(tǒng)的情況下擴(kuò)展系統(tǒng)的功能,為這些不同類(lèi)型的元素增加新的操作嗓蘑。
在使用訪問(wèn)者模式時(shí)须肆,被訪問(wèn)元素通常不是單獨(dú)存在的匿乃,它們存儲(chǔ)在一個(gè)集合中桩皿,這個(gè)集合被稱為「對(duì)象結(jié)構(gòu)」,訪問(wèn)者通過(guò)遍歷對(duì)象結(jié)構(gòu)實(shí)現(xiàn)對(duì)其中存儲(chǔ)的元素的逐個(gè)操作幢炸。通過(guò)一個(gè)簡(jiǎn)單的例子了解訪問(wèn)者模式泄隔,訪問(wèn)者有財(cái)務(wù)部門(mén)FADepartment和 HR 部門(mén)HRDepartment,通過(guò)訪問(wèn)雇員Employee來(lái)查看雇員的工作情況宛徊。
優(yōu)點(diǎn):
增加新的訪問(wèn)操作很方便佛嬉。使用訪問(wèn)者模式,增加新的訪問(wèn)操作就意味著增加一個(gè)新的具體訪問(wèn)者類(lèi)闸天,實(shí)現(xiàn)簡(jiǎn)單暖呕,無(wú)須修改源代碼,符合“開(kāi)閉原則”苞氮。
將有關(guān)元素對(duì)象的訪問(wèn)行為集中到一個(gè)訪問(wèn)者對(duì)象中湾揽,而不是分散在一個(gè)個(gè)的元素類(lèi)中。類(lèi)的職責(zé)更加清晰,有利于對(duì)象結(jié)構(gòu)中元素對(duì)象的復(fù)用库物,相同的對(duì)象結(jié)構(gòu)可以供多個(gè)不同的訪問(wèn)者訪問(wèn)霸旗。
讓用戶能夠在不修改現(xiàn)有元素類(lèi)層次結(jié)構(gòu)的情況下,定義作用于該層次結(jié)構(gòu)的操作戚揭。
缺點(diǎn):
增加新的元素類(lèi)很困難诱告。在訪問(wèn)者模式中,每增加一個(gè)新的元素類(lèi)都意味著要在抽象訪問(wèn)者角色中增加一個(gè)新的抽象操作民晒,并在每一個(gè)具體訪問(wèn)者類(lèi)中增加相應(yīng)的具體操作精居,這違背了“開(kāi)閉原則”的要求。
破壞封裝潜必。訪問(wèn)者模式要求訪問(wèn)者對(duì)象訪問(wèn)并調(diào)用每一個(gè)元素對(duì)象的操作箱蟆,這意味著元素對(duì)象有時(shí)候必須暴露一些自己的內(nèi)部操作和內(nèi)部狀態(tài),否則無(wú)法供訪問(wèn)者訪問(wèn)刮便。
總結(jié)
系統(tǒng)地學(xué)習(xí)設(shè)計(jì)模式后空猜,你可以在過(guò)往的開(kāi)發(fā)經(jīng)歷中發(fā)現(xiàn),設(shè)計(jì)模式是無(wú)處不在的恨旱。在學(xué)習(xí)設(shè)計(jì)模式之前的很多時(shí)候我們是憑借過(guò)往經(jīng)驗(yàn)和智慧來(lái)完善系統(tǒng)的設(shè)計(jì)辈毯,而這些經(jīng)驗(yàn)很多和某個(gè)設(shè)計(jì)模式的思想不謀而合。
還有一些地方?jīng)]有完全理解搜贤,文中有誤之處還望不吝指出谆沃。
參考
https://juejin.im/user/57f8ffda2e958a005581e3c0/posts
https://design-patterns.readthedocs.io/zh_CN/latest/index.html
https://blog.csdn.net/lovelion/article/details/17517213
https://github.com/skyming/Trip-to-iOS-Design-Patterns