轉(zhuǎn):讀《圖解設(shè)計(jì)模式》的所思所想

讀《圖解設(shè)計(jì)模式》的所思所想

轉(zhuǎn)自思否 https://segmentfault.com/a/1190000019971744
如有侵權(quán),請(qǐng)聯(lián)系我刪除,謝謝

困惑

作者試圖從另一個(gè)角度闡述設(shè)計(jì)模式,所以對(duì) 23 種具體設(shè)計(jì)模式進(jìn)行了重新分類,但整本書讀下來(lái)比較困惑,在于幾點(diǎn):

  1. 分類標(biāo)準(zhǔn)不統(tǒng)一,有實(shí)現(xiàn)思路、實(shí)現(xiàn)內(nèi)容、模式目的等標(biāo)準(zhǔn),甚至還有“適應(yīng)設(shè)計(jì)模式”這種分類,頗有些無(wú)從分類的“自暴自棄”的味道。同時(shí)在這種分類方式下,還存在一個(gè)問(wèn)題,即某設(shè)計(jì)模式的實(shí)現(xiàn)是會(huì)用到另一個(gè)設(shè)計(jì)模式的,甚至其些設(shè)計(jì)模式的書中實(shí)現(xiàn)類圖會(huì)基本相同,但是卻屬于不同分類,帶來(lái)了新的困惑,好像要強(qiáng)迫你在學(xué)習(xí)一個(gè)設(shè)計(jì)模式時(shí),要忘掉其他設(shè)計(jì)模式的存在。
  2. 缺乏對(duì)具體設(shè)計(jì)模式適用場(chǎng)景的充分闡述,知何卻不知為何。
  3. 作為入門書,未對(duì)更低層的原則進(jìn)行科普,即使知道了各具體模式可以達(dá)成哪些具體目的,卻無(wú)法融匯到統(tǒng)一的思想出口。
  4. ?

但是總覺(jué)得還有一個(gè)抓不到的原因,那么再深入探究一下,到底是什么令我產(chǎn)生困惑呢?這就需要了解設(shè)計(jì)模式的起源。

根源

設(shè)計(jì)模式(design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。它描述了在軟件設(shè)計(jì)過(guò)程中的一些不斷重復(fù)發(fā)生的問(wèn)題,以及該問(wèn)題的解決方案。

設(shè)計(jì)模式是問(wèn)題的方案。

設(shè)計(jì)模式是經(jīng)驗(yàn)的總結(jié)。

首先,正確的學(xué)習(xí)方式應(yīng)該是帶著問(wèn)題找答案。如果答案被直接擺在面前卻不知問(wèn)題,不論是誰(shuí)都會(huì)產(chǎn)生“這是啥?!”的困惑。但更重要的是,經(jīng)驗(yàn)是具有普適性的,具體的設(shè)計(jì)模式其實(shí)體現(xiàn)的是具體的思想方式,這種思想方式與語(yǔ)言無(wú)關(guān),同時(shí)在單一語(yǔ)言中也一定有多種實(shí)現(xiàn)形式,那么此時(shí)就進(jìn)入到了抽象和具象的沖突。若無(wú)頓悟的天分,接收到思想的抽象概念描述時(shí),會(huì)有一種腦子懂了,卻無(wú)從下手的感覺(jué),同時(shí)若以書中有限的應(yīng)用示例來(lái)描述,又無(wú)法完全體會(huì)到思想的方方面面。

所以設(shè)計(jì)模式的學(xué)習(xí)應(yīng)該是快速的閱讀書籍,在對(duì)模式有輪廓性認(rèn)識(shí)后,帶著問(wèn)題,不斷實(shí)踐練習(xí)的一個(gè)過(guò)程,要在實(shí)踐中得出自己的體會(huì),將從書中得到的融到自己的骨子里。這也是造成前面講的困惑的根本原因了,實(shí)踐不夠呀。

這其中還有另一個(gè)教訓(xùn),我曾經(jīng)陷入了為什么能用這種設(shè)計(jì)模式而不能用另一種設(shè)計(jì)模式的思維旋渦,一樣,只靠想,不依托實(shí)踐,這些問(wèn)題是解決不了的。所以不要把時(shí)間浪費(fèi)在糾結(jié)的思考中。

也是因此,后續(xù)內(nèi)容不會(huì)是面面俱到的長(zhǎng)篇累牘,只會(huì)對(duì)設(shè)計(jì)模式的脈絡(luò)做基于目的的簡(jiǎn)要闡述。

目的與手段

維護(hù)一個(gè)軟件的長(zhǎng)期良性發(fā)展是究極目標(biāo),即提高可維護(hù)性,降低維護(hù)成本??梢詮某橄蟮燃?jí)分為 4 個(gè)層次。

  1. 目標(biāo):維護(hù)性。
  2. 標(biāo)準(zhǔn):擴(kuò)展性、重用性、高內(nèi)聚、低耦合。
  3. 原則:7 大基本原則。
  4. 模式:23 + N 種設(shè)計(jì)模式。

應(yīng)該通過(guò)提升擴(kuò)展性、重用性等達(dá)到高內(nèi)聚、低耦合的特性。

在這個(gè)過(guò)程中應(yīng)遵循 7 大原則,同時(shí)這些原則又是設(shè)計(jì)模式的基礎(chǔ),是設(shè)計(jì)模式為何如此設(shè)計(jì)的依據(jù)。

而模式則是更具體的思想范式,設(shè)計(jì)模式不僅僅局限于 23 種,跟隨技術(shù)水平的發(fā)展,也伴生出了新的問(wèn)題,也就總結(jié)出了針對(duì)新問(wèn)題的 N 種模式。

7 大基本原則

設(shè)計(jì)模式往往是基于類,接口來(lái)講的,而 JS 并非基于類的語(yǔ)言,支持度不夠,同時(shí)我們又不應(yīng)該將模式的思想拘泥于類中,所以可以將下述原則的應(yīng)用個(gè)體,如類、接口,放到函數(shù)或模塊等其他維度上體會(huì)。

  • 單一職責(zé)原則:

    • 單一職責(zé)原則規(guī)定一個(gè)類應(yīng)該有且僅有一個(gè)引起它變化的原因,否則類應(yīng)該被拆分。
    • 我們不必要拘泥于類,該原則的根本目的是控制職責(zé)所在的個(gè)體復(fù)雜度。只需要明白單一個(gè)體只需要做好一件事,個(gè)體越簡(jiǎn)單則可讀性越好,職責(zé)劃分越明確,則改動(dòng)發(fā)生時(shí),越不會(huì)影響其他個(gè)體。
    • 比如這種職責(zé)拆分可以發(fā)生在函數(shù)粒度,也可以發(fā)生在函數(shù)的聚合層面(類或者更外層的函數(shù)),職責(zé)和個(gè)體理想狀態(tài)下應(yīng)該是一對(duì)一的。
    • 這個(gè)原則要求我們能清晰的認(rèn)識(shí)到代碼邏輯中的多重職責(zé),從而才能進(jìn)行劃分。
  • 接口隔離原則:

    • 客戶端不應(yīng)該被迫依賴于它不使用的方法。一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上。
    • 即對(duì)于依賴者,被依賴者應(yīng)該只提供他關(guān)心的功能。當(dāng)體現(xiàn)在接口上時(shí),就是接口隔離原則,將有冗余的接口拆分。
    • 可以避免由于依賴者的增多導(dǎo)致接口膨脹,影響到其他的依賴者。
    • 相對(duì)于單一職責(zé)原則可以理解為單一職責(zé)原則是對(duì)內(nèi)做最少承諾,而接口隔離原則是對(duì)外做最少的承諾。
  • 依賴倒置原則:

    • 高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴其抽象;抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。
    • 即面向接口編程。我們只需要對(duì)低層進(jìn)行接口定義,高層只需要關(guān)注有哪些接口并進(jìn)行調(diào)用,低層實(shí)現(xiàn)時(shí)只要實(shí)現(xiàn)了這些接口,那么傳給高層的實(shí)例發(fā)生變化時(shí),高層就不需要修改。降低了耦合度。
  • 開(kāi)閉原則:

    • 軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。
    • 在軟件修改時(shí),盡量通過(guò)擴(kuò)展而實(shí)現(xiàn)而不是通過(guò)修改來(lái)實(shí)現(xiàn),避免對(duì)現(xiàn)有邏輯的影響。
  • 合成復(fù)用原則:

    • 在復(fù)用時(shí),要盡量先使用組合(實(shí)例化是就存在)或者聚合(通過(guò) API 調(diào)用添加為成員變量)等關(guān)聯(lián)關(guān)系來(lái)實(shí)現(xiàn),其次才考慮使用繼承關(guān)系來(lái)實(shí)現(xiàn)。
    • 繼承強(qiáng)耦合,組合聚合是弱耦合。
  • 里氏替換原則:

    • 繼承必須確保超類所擁有的性質(zhì)在子類中仍然成立。即子類可以擴(kuò)展父類的功能,但不能改變父類原有的功能。
  • 迪米特法則:

    • 只與你的直接朋友交談,不跟“陌生人”說(shuō)話,又叫最少知識(shí)原則。即一個(gè)類對(duì)自己依賴的類知道的越少越好。
    • 耦合是無(wú)法完全避免的。
    • 被依賴的類不論多復(fù)雜,都應(yīng)該將細(xì)節(jié)封裝在內(nèi)部,對(duì)外暴露 API。
    • 應(yīng)該避免類中出現(xiàn)非直接的朋友關(guān)系(直接朋友關(guān)系:成員變量、參數(shù)、返回值)的依賴。

可以看出這些原則都是為了個(gè)體間的低耦合而努力。

模式的一句話描述

  1. 迭代器模式:為了在不暴露數(shù)據(jù)的內(nèi)部結(jié)構(gòu)的前提下對(duì)外提供可替換的迭代方式。此模式隱藏內(nèi)部細(xì)節(jié),且可替換迭代方式。這種思路可推廣至迭代以外的其他能力。
  2. 適配器模式:為了使不兼容的接口協(xié)同工作,將現(xiàn)有接口包裝為需要的接口。在處理代碼邊界即第三方依賴時(shí)也可使用,能在第三方依賴被換掉時(shí)降低改動(dòng)成本。
  3. 模板方法模式:在流程結(jié)構(gòu)確定,而步驟的具體實(shí)現(xiàn)不定或有差異時(shí)使用。即定義好模板,但將具體處理的實(shí)現(xiàn)交給子類,擴(kuò)展新的能力時(shí)只需實(shí)現(xiàn)新的子類。
  4. 工廠方法模式:創(chuàng)建接口不變的情況下,由用戶決定什么哪個(gè)實(shí)例時(shí),使用。產(chǎn)品和工廠一一對(duì)應(yīng),擴(kuò)展新的產(chǎn)品時(shí)需要增加新的產(chǎn)品類和對(duì)應(yīng)的工廠類。
  5. 單例模式:?jiǎn)我活愔辉试S生成一個(gè)實(shí)例時(shí)使用。
  6. 原型模式:避免較高的實(shí)例化成本時(shí)使用。通過(guò)復(fù)制生成實(shí)例,核心在于復(fù)制現(xiàn)有實(shí)例,避免重走實(shí)例化的過(guò)程。
  7. 建造者模式:最終產(chǎn)出物的組成部分相同,但需要組裝過(guò)程可替換時(shí)使用。通過(guò)組裝生成復(fù)雜實(shí)例,并將組裝過(guò)程抽離至獨(dú)立的類,核心在于側(cè)重組裝,那么實(shí)現(xiàn)不同的組裝過(guò)程類就能產(chǎn)出不同的產(chǎn)出物。
  8. 抽象工廠模式:與工廠方法模式類似,當(dāng)工廠類產(chǎn)出多個(gè)產(chǎn)品時(shí)可以使用抽象工廠模式。注意區(qū)別是是工廠產(chǎn)出一個(gè)還是多個(gè)產(chǎn)品。
  9. 橋接模式:在對(duì)外提供的功能接口內(nèi)有多個(gè)維度的變化時(shí)使用,將類的對(duì)外接口和實(shí)現(xiàn)分為獨(dú)立的兩個(gè)類,對(duì)外接口通過(guò)在內(nèi)部組合使用實(shí)現(xiàn)類來(lái)完成具體實(shí)現(xiàn),可減少維度引起的類數(shù)量的爆炸增長(zhǎng)。
  10. 策略模式:當(dāng)我們完成任務(wù)的策略需要可被替換時(shí)使用。將通過(guò)策略完成任務(wù)的過(guò)程拆分為調(diào)用和實(shí)現(xiàn),實(shí)現(xiàn)部分提供成系統(tǒng)的方法簇,即具體策略,以參數(shù)形式傳給調(diào)用部分,從而實(shí)現(xiàn)策略可替換。
  11. 組合模式:當(dāng)需要提供給用戶的是多個(gè)對(duì)象,且對(duì)象間是部分-整體的層次結(jié)構(gòu),且不希望用戶關(guān)心對(duì)象間差異,只要一套訪問(wèn)接口時(shí)使用。通過(guò)多個(gè)子類實(shí)現(xiàn)相同接口實(shí)現(xiàn)。
  12. 裝飾器模式:給一個(gè)對(duì)象追加更多功能,且不改變提供給用戶的接口時(shí)使用。
  13. 訪問(wèn)者模式:在不改變數(shù)據(jù)結(jié)構(gòu)的前提下可以添加對(duì)數(shù)據(jù)結(jié)構(gòu)的新的操作時(shí)使用。通過(guò)將數(shù)據(jù)結(jié)構(gòu)與操作分離的方式實(shí)現(xiàn)。
  14. 責(zé)任鏈模式:個(gè)體需要被多個(gè)對(duì)象處理,但處理對(duì)象間有沒(méi)有耦合關(guān)系時(shí),為了避免增加系統(tǒng)復(fù)雜度時(shí)使用。通過(guò)將多個(gè)處理對(duì)象組成一條責(zé)任鏈,然后將待處理目標(biāo)沿著這條鏈傳遞進(jìn)行處理實(shí)現(xiàn)。Koa 中間件機(jī)制就是一種實(shí)踐。
  15. 門面模式:簡(jiǎn)化用戶對(duì)復(fù)雜系統(tǒng)中子系統(tǒng)的聯(lián)系,對(duì)外提供簡(jiǎn)單易用的接口時(shí)使用。通過(guò)包裝更高層的類,由它調(diào)度子系統(tǒng)實(shí)現(xiàn)。
  16. 中介者模式:簡(jiǎn)化復(fù)雜系統(tǒng)中子系統(tǒng)之間的聯(lián)系,將交互封裝一個(gè)中介對(duì)象,降低子系統(tǒng)對(duì)象間的耦合??梢泽w會(huì)下與門面模式的不同。
  17. 觀察者模式:一個(gè)對(duì)象改變時(shí)需要導(dǎo)致其他對(duì)象也改變,且不關(guān)心其他對(duì)象具體是誰(shuí)時(shí)可以使用。通過(guò)觀察對(duì)象管理監(jiān)聽(tīng)他的所有觀察者,并在發(fā)生變化時(shí)通知所有觀察者實(shí)現(xiàn)。
  18. 備忘錄模式:在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),以便以后當(dāng)需要時(shí)能將該對(duì)象恢復(fù)到原先保存的狀態(tài)時(shí)使用。
  19. 狀態(tài)模式:對(duì)象與外部互動(dòng)導(dǎo)致?tīng)顟B(tài)變化,從而行為不同時(shí)使用。通過(guò)將不同狀態(tài)下的行為封裝為不同的類,允許在狀態(tài)改變時(shí)通過(guò)切換狀態(tài)類改變行為實(shí)現(xiàn)??梢岳斫鉃椴呗阅J降囊环N特殊應(yīng)用。是一種內(nèi)置了多種“策略”,根據(jù)狀態(tài)變化切換“策略”的模式。
  20. 享元模式:有大量的實(shí)例需要公用或重復(fù)使用時(shí)使用。我們可以把這些實(shí)例當(dāng)做享元,并管理起來(lái)??梢援?dāng)做同時(shí)使用了多個(gè)單例模式的類,因?yàn)榇嬖诠芾砟芰Γ詴?huì)受外部因素影響,在返回前修改單例的狀態(tài)。
  21. 代理模式:在我們不想讓用戶直接使用對(duì)象的情況下使用,如加以訪問(wèn)控制。很簡(jiǎn)單,實(shí)現(xiàn)方式就是加一層代理來(lái)間接引用對(duì)象。
  22. 命令模式:需要使命令發(fā)起者和執(zhí)行者不可見(jiàn),甚至需要對(duì)命令加以管理時(shí)使用。此模式通過(guò)將命令封裝為一個(gè)類,將命令執(zhí)行者作為命令的依賴,分離命令調(diào)用者和命令實(shí)現(xiàn)者,同時(shí)由于命令實(shí)例的存在又可以對(duì)命令加以管理。
  23. 解釋器模式:將發(fā)生頻率足夠高的問(wèn)題的各個(gè)實(shí)例表述為一個(gè)簡(jiǎn)單語(yǔ)言的句子,并構(gòu)建一個(gè)解釋器解釋語(yǔ)言中的句子。通過(guò)對(duì)定義語(yǔ)句語(yǔ)法節(jié)點(diǎn),并針對(duì)每類語(yǔ)法節(jié)點(diǎn)聲明類,對(duì)語(yǔ)句節(jié)點(diǎn)遍歷解析實(shí)現(xiàn)。

我們可以從上述描述中看到重復(fù)的幾個(gè)關(guān)鍵詞:拆分、不關(guān)心、不破壞,還是在為了個(gè)體間的低耦合而努力。

而且要注意的是,模式并非完美,有些模式實(shí)現(xiàn)時(shí)甚至?xí)黾觾?nèi)部耦合,增加系統(tǒng)復(fù)雜度,所以要關(guān)注目的,關(guān)注目的,關(guān)注目的,關(guān)注是否降低了所關(guān)注的可能變化的點(diǎn)的耦合度。

結(jié)語(yǔ)

最后以三個(gè)問(wèn)題結(jié)束這篇文章。

學(xué)什么?

我們學(xué)設(shè)計(jì)模式,是為了學(xué)習(xí)如何合理的組織我們的代碼,如何解耦,如何真正的達(dá)到對(duì)修改封閉對(duì)擴(kuò)展開(kāi)放的效果,而不是去背誦那些類的繼承模式,然后自己記不住,回過(guò)頭來(lái)就罵設(shè)計(jì)模式把你的代碼搞復(fù)雜了,要反設(shè)計(jì)模式。

如何用?

為了合理的利用設(shè)計(jì)模式,我們應(yīng)該明白一個(gè)概念,叫做擴(kuò)展點(diǎn)。擴(kuò)展點(diǎn)不是天生就有的,而是設(shè)計(jì)出來(lái)的。我們?cè)O(shè)計(jì)一個(gè)軟件的架構(gòu)的時(shí)候,我們也要同時(shí)設(shè)計(jì)一下哪些地方以后可以改,哪些地方以后不能改。

如何用的好?

“我亦無(wú)他,惟手熟爾?!?/p>

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容