困惑
做者試圖從另外一個角度闡述設計模式,因此對 23 種具體設計模式進行了從新分類,但整本書讀下來比較困惑,在於幾點:html
- 分類標準不統一,有實現思路、實現內容、模式目的等標準,甚至還有「適應設計模式」這種分類,很有些無從分類的「自暴自棄」的味道。同時在這種分類方式下,還存在一個問題,即某設計模式的實現是會用到另外一個設計模式的,甚至其些設計模式的書中實現類圖會基本相同,可是卻屬於不一樣分類,帶來了新的困惑,好像要強迫你在學習一個設計模式時,要忘掉其餘設計模式的存在。
- 缺少對具體設計模式適用場景的充分闡述,知何殊不知爲什麼。
- 做爲入門書,未對更低層的原則進行科普,即便知道了各具體模式能夠達成哪些具體目的,卻沒法融匯到統一的思想出口。
- ?
可是總以爲還有一個抓不到的緣由,那麼再深刻探究一下,究竟是什麼令我產生困惑呢?這就須要瞭解設計模式的起源。編程
根源
設計模式(design pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。它描述了在軟件設計過程當中的一些不斷重複發生的問題,以及該問題的解決方案。設計模式
設計模式是問題的方案。數據結構
設計模式是經驗的總結。架構
首先,正確的學習方式應該是帶着問題找答案。若是答案被直接擺在面前殊不知問題,不管是誰都會產生「這是啥?!」的困惑。但更重要的是,經驗是具備普適性的,具體的設計模式其實體現的是具體的思想方式,這種思想方式與語言無關,同時在單一語言中也必定有多種實現形式,那麼此時就進入到了抽象和具象的衝突。若無頓悟的天分,接收到思想的抽象概念描述時,會有一種腦子懂了,卻無從下手的感受,同時若以書中有限的應用示例來描述,又沒法徹底體會到思想的方方面面。函數
因此設計模式的學習應該是快速的閱讀書籍,在對模式有輪廓性認識後,帶着問題,不斷實踐練習的一個過程,要在實踐中得出本身的體會,將從書中獲得的融到本身的骨子裏。這也是形成前面講的困惑的根本緣由了,實踐不夠呀。學習
這其中還有另外一個教訓,我曾經陷入了爲何能用這種設計模式而不能用另外一種設計模式的思惟旋渦,同樣,只靠想,不依託實踐,這些問題是解決不了的。因此不要把時間浪費在糾結的思考中。.net
也是所以,後續內容不會是面面俱到的長篇累牘,只會對設計模式的脈絡作基於目的的簡要闡述。設計
目的與手段
維護一個軟件的長期良性發展是究極目標,即提升可維護性,下降維護成本。能夠從抽象等級分爲 4 個層次。代理
- 目標:維護性。
- 標準:擴展性、重用性、高內聚、低耦合。
- 原則:7 大基本原則。
- 模式:23 + N 種設計模式。
應該經過提高擴展性、重用性等達到高內聚、低耦合的特性。
在這個過程當中應遵循 7 大原則,同時這些原則又是設計模式的基礎,是設計模式爲什麼如此設計的依據。
而模式則是更具體的思想範式,設計模式不只僅侷限於 23 種,跟隨技術水平的發展,也伴生出了新的問題,也就總結出了針對新問題的 N 種模式。
7 大基本原則
設計模式每每是基於類,接口來說的,而 JS 並不是基於類的語言,支持度不夠,同時咱們又不該該將模式的思想拘泥於類中,因此能夠將下述原則的應用個體,如類、接口,放到函數或模塊等其餘維度上體會。
-
單一職責原則:
- 單一職責原則規定一個類應該有且僅有一個引發它變化的緣由,不然類應該被拆分。
- 咱們沒必要要拘泥於類,該原則的根本目的是控制職責所在的個體複雜度。只須要明白單一個體只須要作好一件事,個體越簡單則可讀性越好,職責劃分越明確,則改動發生時,越不會影響其餘個體。
- 好比這種職責拆分能夠發生在函數粒度,也能夠發生在函數的聚合層面(類或者更外層的函數),職責和個體理想狀態下應該是一對一的。
- 這個原則要求咱們能清晰的認識到代碼邏輯中的多重職責,從而才能進行劃分。
-
接口隔離原則:
- 客戶端不該該被迫依賴於它不使用的方法。一個類對另外一個類的依賴應該創建在最小的接口上。
- 即對於依賴者,被依賴者應該只提供他關心的功能。當體如今接口上時,就是接口隔離原則,將有冗餘的接口拆分。
- 能夠避免因爲依賴者的增多致使接口膨脹,影響到其餘的依賴者。
- 相對於單一職責原則能夠理解爲單一職責原則是對內作最少承諾,而接口隔離原則是對外作最少的承諾。
-
依賴倒置原則:
- 高層模塊不該該依賴低層模塊,二者都應該依賴其抽象;抽象不該該依賴細節,細節應該依賴抽象。
- 即面向接口編程。咱們只須要對低層進行接口定義,高層只須要關注有哪些接口並進行調用,低層實現時只要實現了這些接口,那麼傳給高層的實例發生變化時,高層就不須要修改。下降了耦合度。
-
開閉原則:
- 軟件實體應當對擴展開放,對修改關閉。
- 在軟件修改時,儘可能經過擴展而實現而不是經過修改來實現,避免對現有邏輯的影響。
-
合成複用原則:
- 在複用時,要儘可能先使用組合(實例化是就存在)或者聚合(經過 API 調用添加爲成員變量)等關聯關係來實現,其次才考慮使用繼承關係來實現。
- 繼承強耦合,組合聚合是弱耦合。
-
里氏替換原則:
- 繼承必須確保超類所擁有的性質在子類中仍然成立。即子類能夠擴展父類的功能,但不能改變父類原有的功能。
-
迪米特法則:
- 只與你的直接朋友交談,不跟「陌生人」說話,又叫最少知識原則。即一個類對本身依賴的類知道的越少越好。
- 耦合是沒法徹底避免的。
- 被依賴的類不論多複雜,都應該將細節封裝在內部,對外暴露 API。
- 應該避免類中出現非直接的朋友關係(直接朋友關係:成員變量、參數、返回值)的依賴。
能夠看出這些原則都是爲了個體間的低耦合而努力。
模式的一句話描述
- 迭代器模式:爲了在不暴露數據的內部結構的前提下對外提供可替換的迭代方式。此模式隱藏內部細節,且可替換迭代方式。這種思路可推廣至迭代之外的其餘能力。
- 適配器模式:爲了使不兼容的接口協同工做,將現有接口包裝爲須要的接口。在處理代碼邊界即第三方依賴時也可以使用,能在第三方依賴被換掉時下降改動成本。
- 模板方法模式:在流程結構肯定,而步驟的具體實現不定或有差別時使用。即定義好模板,但將具體處理的實現交給子類,擴展新的能力時只需實現新的子類。
- 工廠方法模式:建立接口不變的狀況下,由用戶決定什麼哪一個實例時,使用。產品和工廠一一對應,擴展新的產品時須要增長新的產品類和對應的工廠類。
- 單例模式:單一類只容許生成一個實例時使用。
- 原型模式:避免較高的實例化成本時使用。經過複製生成實例,核心在於複製現有實例,避免重走實例化的過程。
- 建造者模式:最終產出物的組成部分相同,但須要組裝過程可替換時使用。經過組裝生成複雜實例,並將組裝過程抽離至獨立的類,核心在於側重組裝,那麼實現不一樣的組裝過程類就能產出不一樣的產出物。
- 抽象工廠模式:與工廠方法模式相似,當工廠類產出多個產品時可使用抽象工廠模式。注意區別是是工廠產出一個仍是多個產品。
- 橋接模式:在對外提供的功能接口內有多個維度的變化時使用,將類的對外接口和實現分爲獨立的兩個類,對外接口經過在內部組合使用實現類來完成具體實現,可減小維度引發的類數量的爆炸增加。
- 策略模式:當咱們完成任務的策略須要可被替換時使用。將經過策略完成任務的過程拆分爲調用和實現,實現部分提供成系統的方法簇,即具體策略,以參數形式傳給調用部分,從而實現策略可替換。
- 組合模式:當須要提供給用戶的是多個對象,且對象間是部分-總體的層次結構,且不但願用戶關心對象間差別,只要一套訪問接口時使用。經過多個子類實現相同接口實現。
- 裝飾器模式:給一個對象追加更多功能,且不改變提供給用戶的接口時使用。
- 訪問者模式:在不改變數據結構的前提下能夠添加對數據結構的新的操做時使用。經過將數據結構與操做分離的方式實現。
- 責任鏈模式:個體須要被多個對象處理,但處理對象間有沒有耦合關係時,爲了不增長系統複雜度時使用。經過將多個處理對象組成一條責任鏈,而後將待處理目標沿着這條鏈傳遞進行處理實現。Koa 中間件機制就是一種實踐。
- 門面模式:簡化用戶對複雜系統中子系統的聯繫,對外提供簡單易用的接口時使用。經過包裝更高層的類,由它調度子系統實現。
- 中介者模式:簡化複雜系統中子系統之間的聯繫,將交互封裝一箇中介對象,下降子系統對象間的耦合。能夠體會下與門面模式的不一樣。
- 觀察者模式:一個對象改變時須要致使其餘對象也改變,且不關心其餘對象具體是誰時可使用。經過觀察對象管理監聽他的全部觀察者,並在發生變化時通知全部觀察者實現。
- 備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態,以便之後當須要時能將該對象恢復到原先保存的狀態時使用。
- 狀態模式:對象與外部互動致使狀態變化,從而行爲不一樣時使用。經過將不一樣狀態下的行爲封裝爲不一樣的類,容許在狀態改變時經過切換狀態類改變行爲實現。能夠理解爲策略模式的一種特殊應用。是一種內置了多種「策略」,根據狀態變化切換「策略」的模式。
- 享元模式:有大量的實例須要公用或重複使用時使用。咱們能夠把這些實例當作享元,並管理起來。能夠當作同時使用了多個單例模式的類,由於存在管理能力,因此會受外部因素影響,在返回前修改單例的狀態。
- 代理模式:在咱們不想讓用戶直接使用對象的狀況下使用,如加以訪問控制。很簡單,實現方式就是加一層代理來間接引用對象。
- 命令模式:須要使命令發起者和執行者不可見,甚至須要對命令加以管理時使用。此模式經過將命令封裝爲一個類,將命令執行者做爲命令的依賴,分離命令調用者和命令實現者,同時因爲命令實例的存在又能夠對命令加以管理。
- 解釋器模式:將發生頻率足夠高的問題的各個實例表述爲一個簡單語言的句子,並構建一個解釋器解釋語言中的句子。經過對定義語句語法節點,並針對每類語法節點聲明類,對語句節點遍歷解析實現。
咱們能夠從上述描述中看到重複的幾個關鍵詞:拆分、不關心、不破壞,仍是在爲了個體間的低耦合而努力。
並且要注意的是,模式並不是完美,有些模式實現時甚至會增長內部耦合,增長系統複雜度,因此要關注目的,關注目的,關注目的,關注是否下降了所關注的可能變化的點的耦合度。
結語
最後以三個問題結束這篇文章。
學什麼?
咱們學設計模式,是爲了學習如何合理的組織咱們的代碼,如何解耦,如何真正的達到對修改封閉對擴展開放的效果,而不是去背誦那些類的繼承模式,而後本身記不住,回過頭來就罵設計模式把你的代碼搞複雜了,要反設計模式。
如何用?
爲了合理的利用設計模式,咱們應該明白一個概念,叫作擴展點。擴展點不是天生就有的,而是設計出來的。咱們設計一個軟件的架構的時候,咱們也要同時設計一下哪些地方之後能夠改,哪些地方之後不能改。
如何用的好?
「我亦無他,唯手熟爾。」
參考