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