設(shè)計模式簡介
相信你只要接觸過軟件編程,那么一定聽說過設(shè)計模式。所謂設(shè)計模式,是為解決特定問題,一套通用的、可重用的軟件設(shè)計方案。我們通常所說的設(shè)計模式,是針對面向?qū)ο笳Z言而言。對于每一位使用面向?qū)ο笳Z言的從業(yè)者,在學(xué)習(xí)完該語言的基礎(chǔ)知識后,也一定了解如何編寫面向?qū)ο蟮某绦颉5?,面向?qū)ο缶烤褂惺裁春锰帲覀冇謶?yīng)該如何靈活運用面向?qū)ο髞碓O(shè)計程序呢?
面向?qū)ο蟪霈F(xiàn)之前,程序是面向過程的。兩者在軟件設(shè)計上有著很大的不同。
面向過程,我們首要思考邏輯過程是什么,如何設(shè)計這個過程。
面向?qū)ο?,首先要考慮的是有哪些對象,對象有什么行為,最后才是行為的邏輯。
面向?qū)ο蟮目梢宰屇愠绦虻脑O(shè)計和真實世界更為契合。這會帶來如下優(yōu)點:
- 現(xiàn)實世界有千百年積累下來數(shù)以萬億計的優(yōu)秀設(shè)計,無論是具體的機械設(shè)備,還是方法論,或者工程理論。我們都可以拿來作為軟件設(shè)計的參考。
- 面向?qū)ο箝_發(fā)出的軟件,讓其他開發(fā)者更容易理解。我們每個人都熟知我們所生活的世界。面向?qū)ο罂梢宰尶菰锏拇a更加鮮活,甚至憑你的經(jīng)驗,也能猜出對象的某個行為應(yīng)該是怎樣的邏輯。
夸了半天面向?qū)ο?,你可能會想,面向?qū)ο蟠_實不錯,我所使用的語言也是面向?qū)ο蟮?,那我編寫的程序天然也就擁有了面向?qū)ο蟮膬?yōu)點。這種想法是錯誤的。語言僅僅是個工具,而面向?qū)ο缶幊淌且环N思維。如果對面向?qū)ο缶幊虥]有深入理解,那么你寫出的程序,也只是披著面向?qū)ο蟮耐庖露?。此外,即使你充分理解了面向?qū)ο蟮乃季S,也不一定能夠靈活運用面向?qū)ο蠼鉀Q問題。那么,此時就輪到設(shè)計模式出場了!
2. 設(shè)計模式來自哪里
設(shè)計模式不是憑空想出來的。作為通用的設(shè)計方式,設(shè)計模式是跨語言的。要想做到跨語言,那么它的根基一定是萬物都要遵守的規(guī)律。設(shè)計模式來源于真實世界,前輩們通過不斷地歸納總結(jié)、實踐,將一些已經(jīng)存在的設(shè)計理論運用于軟件領(lǐng)域,并很好地解決了軟件設(shè)計上的問題。最終呈現(xiàn)給我們這些豐富的設(shè)計模式。
當我們學(xué)習(xí)設(shè)計模式之后,就可以深刻體會到為什么設(shè)計模式來源于真實世界。這里我先舉個例子,比如訂閱者模式。一聽名字你一定可以映射到現(xiàn)實生活中某些類似的方式,比如訂報紙,訂牛奶。訂的人就是訂閱者,送的人就是發(fā)布者。發(fā)布訂閱的核心思想,再加上軟件的特性,就構(gòu)成了訂閱者模式。
3. 設(shè)計原則
那么使用設(shè)計模式能為軟件開發(fā)帶來什么好處呢?這要從設(shè)計模式的設(shè)計原則說起,一般來說有6大原則,如下:
- 單一職責原則
顧名思義,一個類最好只有一個職責。這樣的好處是引起類發(fā)生變化的原因會很少。我們開發(fā)新需求的時候,就會很少去修改這個類。而且職責越單一也越容易被復(fù)用。 - 開閉原則
軟件應(yīng)該對擴展開放,對修改關(guān)閉。通俗易懂的說,就是你的軟件不能因為加功能,就不斷地修改已有的類。而是應(yīng)該通過增加類,以插拔的方式來實現(xiàn)。舉個例子,Macbook的變壓器插頭是可以替換的,如果說某一天插口的標準換了,那么蘋果只需要開發(fā)一個新的插頭就好了,而不需要重新開發(fā)整個變壓器。開閉原則確保了代碼最大程度的可復(fù)用性。并且確保了成熟代碼的穩(wěn)定性。
- 里氏代換原則
子類型可以替換掉自己的父類。這意味著我們編寫的軟件,所有使用父類的地方,都可以替換為子類對象,而程序的行為不會發(fā)生改變。通過里氏代換原則,我們可以實現(xiàn)開閉原則,通過增加子類實現(xiàn)新的功能,而不是不斷地修改父類。在需要的地方則用子類代替父類。如何實現(xiàn)里氏代換原則呢?首先子類不能重寫父類的非抽象方法,一旦重寫了非抽象方法,就會改變父類的行為。但是子類可以增加自己的方法和屬性,以此達到擴展的目的。 - 依賴倒轉(zhuǎn)原則
簡單說就是應(yīng)該依賴接口,而不是實現(xiàn)。也就是我們常說的面向接口編程。這樣類和類之間就不會直接依賴,從而能夠?qū)崿F(xiàn)開閉原則。類依賴接口,當需要擴展的時候我們可以替換實現(xiàn)。 - 迪米特法則
也稱為最少知道原則。如果兩個類沒有必要直接通信,那么兩個類就沒有必要相互作用。可以通過第三方來間接調(diào)用。類之間的耦合度越弱,越容易被復(fù)用。在弱耦合的關(guān)系中,一個類的修改,造成的影響會很小。所以我們在做設(shè)計的時候需要考慮哪些應(yīng)該對外暴露,哪些應(yīng)該封裝起來。不同功能模塊間的調(diào)用,應(yīng)該由更為高層的類來實現(xiàn),從而屏蔽掉底層的實現(xiàn)。 - 接口隔離原則
接口隔離原則指導(dǎo)你如何設(shè)計接口。不要讓接口變得臃腫,而是應(yīng)該把接口按照行為不同細拆。比如你要生產(chǎn)一把可以拼刺刀的步槍,那它應(yīng)該實現(xiàn)兩個接口,刀的接口和槍的接口。而不是使用一個接口覆蓋所有刀和槍的所有行為。這樣不同的接口可以組合使用。而且如果你只需要刀或者槍的行為,可以單獨實現(xiàn)需要的接口, 不需要實現(xiàn)一個大而全的接口,從而去實現(xiàn)很多用不到的方法。
如果你的代碼滿足以上設(shè)計原則,就會更為健壯、靈活和優(yōu)雅。那么如何做到上面這些原則呢?很簡單,學(xué)習(xí)好設(shè)計模式,靈活運用設(shè)計模式解決你的問題。
4. 為什么要學(xué)習(xí)設(shè)計模式
前面已經(jīng)給出了設(shè)計模式的定義----為解決特定的問題,一套通用的、可重用的軟件設(shè)計方案。我們面對的問題不一樣,需要選擇不同的設(shè)計模式來解決問題。
這就好比木匠有 20 種工具,分別用于做不同的事情。而設(shè)計模式就是軟件設(shè)計的工具,根據(jù)你遇到的問題不同,供你選擇使用。而學(xué)習(xí)設(shè)計模式的目的,就是讓你熟知工具的樣子,工具能夠做什么事情,解決什么樣的問題。當你再遇到設(shè)計問題時,自然就會想到采用什么設(shè)計模式來解決。
設(shè)計模式有多厲害呢?我可以講一個親身經(jīng)歷,曾經(jīng)我有一位同事寫了一段代碼來完成一個功能。code review時,我和他說你可以看一下設(shè)計模式,這段代碼使用XX模式來實現(xiàn)會更為的優(yōu)雅。兩天后他找到我說:“設(shè)計模式太厲害了!感覺我前幾年代碼都白寫了!” 你不要覺得夸張,他的這個感覺,也是我初學(xué)設(shè)計模式后的感覺----原來程序還可以這么寫!以前我們解決問題的工具是錘子、斧子,而現(xiàn)在全都是機械化工具。設(shè)計模式就是這么神奇的東西。
5. 學(xué)習(xí)基礎(chǔ)
只要你掌握了一門面向?qū)ο蟮木幊陶Z言,并且熟悉面向?qū)ο蟮南嚓P(guān)知識即可。當然如果有一定的編程基礎(chǔ)更好。此外需要了解一些UML相關(guān)的知識,方便理解類圖。
6. 小結(jié)
本節(jié)從面向?qū)ο笾v起,講解了什么是設(shè)計模式,程序設(shè)計的原則有哪些,為什么要學(xué)習(xí)設(shè)計模式。設(shè)計模式是寫好程序的根基。我們常常提到代碼要高內(nèi)聚、松耦合,代碼要健壯等等。這些優(yōu)秀代碼的特性,全都來自于設(shè)計模式。足以見得設(shè)計模式的重要性。本小結(jié)內(nèi)容比較多,總結(jié)如下: