軟件開發的基礎理論對於非科班出身的我來講一直是個弱項,前一段時間立了個flag,把 《JavaScript設計模式與開發實踐》 這本書裏的設計模式和設計原則整理成簡單易懂的便籤😂javascript
一是本身加深理解,倒逼輸出,二來能夠請你們幫忙糾錯,防止本身理解誤差;java
喜歡刷沸點小夥伴們可能有看見過最近發的設計模式便籤🔖, 因爲天天遲早看娃,不多有整塊的時間,只能一天抽幾分鐘整理一兩個便籤,還好這個flag算是勉勉強強的完成了,收穫確實不少,下面就是整理的筆記,若有錯誤,懇請拍磚,em....,用力拍👋👋👋。git
可能不少優秀的模式模式是潛移默化在你的代碼和實現思路里,只不過叫不上名字,若是粗略的理解一下設計模式,至少和別人討論實現思路時能夠噴出幾個名詞(開玩笑)😴,畢竟做爲一個編程人員這些知識仍是很很很重要的。程序員
就一個類而言,應僅有一個引發它變化的緣由。github
單一職責原則(SRP)的職責被定義爲「引發變化的緣由」。 若是咱們有兩個動機去改寫一個方法,那麼這個方法就具備兩個職責。每一個職責都是變化的一個軸線,若是一個方法承擔了過多的職責,那麼在需求的變遷過程當中,須要改寫這個方法的可能性就越大。ajax
此時,這個方法一般是一個不穩定的方法,修改代碼老是一件危險的事情,特別是當兩個職責耦合在一塊兒的時候,一個職責發生變化可能會影響到其餘職責的實現,形成意想不到的破壞,這種耦合性獲得的是低內聚和脆弱的設計。算法
SRP原則體現爲:一個對象/方法,只作一件事情。編程
SRP原則是全部原則中最簡單也是最難正確運用的原則之一。 要明確的是,並非全部的職責都應該一一分離。設計模式
SRP原則的優缺點 SRP原則的優勢是下降了單個類或者對象的複雜度,按照職責把對象分解成更小的粒度,這有助於代碼的複用,也有利於進行單元測試。當一個職責須要變動的時候,不會影響到其餘的職責。緩存
最明顯的缺點是會增長編寫代碼的複雜度。當咱們按照職責把對象分解成更小的粒度以後,實際上也增大了這些對象之間相互聯繫的難度。
最少知識原則(LKP)說的是一個軟件實體應當儘量少地與其餘實體發生相互做用。這裏的軟件實體是一個廣義的概念,不只包括對象,還包括系統、類、模塊、函數、變量等。
最少知識原則要求咱們在設計程序時,應當儘可能減小對象之間的交互。若是兩個對象之間沒必要彼此直接通訊,那麼這兩個對象就不要發生直接的相互聯繫。
最少知識原則也叫迪米特法則(Law of Demeter,LoD),「迪米特」這個名字源自1987年美國東北大學一個名爲「Demeter」的研究項目。 在實際開發中,是否選擇讓代碼符合最少知識原則,要根據具體的環境來定。
開放-封閉原則最先由Eiffel語言的設計者Bertrand Meyer在其著做Object-Oriented Software Construction 中提出。它的定義以下:
軟件實體(類、模塊、函數)等應該是能夠擴展的,可是不可修改。
當須要改變一個程序的功能或者給這個程序增長新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼。
無論是具體的各類設計模式,仍是更抽象的面向對象設計原則,好比單一職責原則、最少知識原則、依賴倒置原則等,都是爲了讓程序遵照開放-封閉原則而出現的。
讓程序保持徹底封閉是不容易作到的。就算技術上作獲得,也須要花費太多的時間和精力。 下面這段話引自Bob大叔的《敏捷軟件開發原則、模式與實踐》:
有句古老的諺語說:「愚弄我一次,應該羞愧的是你。再次愚弄我,應該羞愧的是我。」這也是一種有效的對待軟件設計的態度。爲了防止軟件揹着沒必要要的複雜性,咱們會容許本身被愚弄一次。
這有點像星矢說的:「聖鬥士不會被一樣的招數擊倒第二次。」
即保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
單例模式是一種經常使用的模式,有一些對象咱們每每只須要一個,好比線程池、全局緩存、瀏 覽器中的 window 對象等。在 JavaScript 開發中,單例模式的用途一樣很是普遍。試想一下,當我 們單擊登陸按鈕的時候,頁面中會出現一個登陸浮窗,而這個登陸浮窗是惟一的,不管單擊多少 次登陸按鈕,這個浮窗都只會被建立一次,那麼這個登陸浮窗就適合用單例模式來建立。
定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換。
一個基於策略模式的程序至少由兩部分組成。
代理模式是爲本體對象提供一個替身,以便控制對本體的訪問。
代理模式的關鍵是,當咱們不方便直接訪問一個對象或者不知足須要的時候,提供一個替身對象來控制對這個對象的訪問,咱們實際上訪問的是替身對象。替身對象對請求作出一些處理以後,再把請求轉交給本體對象
能夠幫助對象過濾掉一些不知足特定條件的請求,把一些開銷很大的請求,延遲到真正須要它的時候纔去執行等等。
指提供一種方法順序訪問一個聚合對象中的各個元素而又不須要暴露該對象的內部表示。
迭代器模式能夠把迭代的過程從業務邏輯中分離出來,在使用迭代器模式以後,即便不關心對象的內部構造,也能夠按順序訪問其中的每一個元素,如 jQuery 中的$.each 函數。
內部迭代即調用一次循環全部元素,外部迭代須要手動觸發下一個元素的迭代,如圖:
不管是內部迭代器仍是外部迭代器,只要被迭代的聚合對象擁有 length 屬性並且能夠用下標訪問,那它就能夠被迭代。
迭代器模式提供了循環訪問一個聚合對象中每一個元素的方法,但它沒有規定咱們以順序、倒序仍是中序來循環遍歷聚合對象。
迭代器能夠像普通 for 循環中的 break 同樣,提供一種跳出循環的方法。
迭代器模式是一種相對簡單的模式,簡單到不少時候咱們都不認爲它是一種設計模式。目前的絕大部分語言都內置了迭代器。
發佈—訂閱模式和觀察者模式相似,定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。
發佈—訂閱模式能夠普遍應用於異步編程中,這是一種替代傳遞迴調函數的方案。
取代對象之間硬編碼的通知機制,一個對象不用再顯式地調用另一個對象的某個接口。
讓兩個對象鬆耦合地聯繫在一塊兒,能夠在不太清楚彼此的細節的狀況下相互通訊。
當有新的訂閱者出現時,發佈者的代碼不須要任何修改;一樣發佈者須要改變時,也不會影響到以前的訂閱者。只要約定的事件名沒有變化,就能夠自由地改變它們。
發佈—訂閱模式的優勢很是明顯,時間、對象之間的解耦,從架構上來看,不管是 MVC 仍是 MVVM, 都少不了發佈—訂閱模式的參與。
建立訂閱者自己要消耗必定的時間和內存,訂閱一個消息後,也許此消息最後都未發生,但訂閱者始終存在內存中。另外,發佈—訂閱模式會弱化對象之間的聯繫,過分使用後,對象和對象之間的必要聯繫也將被深埋在背後,致使程序難以跟蹤維護和理解。
命令模式是最簡單和優雅的模式之一,命令模式中的命令(command)指的是一個執行某些特定事情的指令。
命令模式的由來實際上是回調(callback)函數的一個面向對象的替代品,跟許多其餘語言不一樣,JavaScript 能夠用高階函數方便地實現命令模式。
JavaScript 做爲將函數做爲一等對象的語言,跟策略模式同樣,命令模式也早已融入到了 JavaScript 語言之中。運算塊不必定要封裝在命令類中,也能夠封裝在普通函數中。
撤銷命令: 某個命令須要運行較長時間,能夠增長撤銷操做。
命令隊列: 咱們把命令存入一個隊列,能夠很簡單的實現如「回放」、「後退」的功能。
宏命令 : 一組命令的集合,一次執行一組命令。
命令模式在 JavaScript 語言中是一種隱形的模式。
組合模式就是用小的子對象來構建更大的對象,而這些小的子對象自己也許是由更 小的「孫對象」構成的。
組合模式將對象組合成樹形結構,以表示「部分-總體」的層次結構。 除了用來表示樹形結構以外,組合模式的另外一個好處是經過對象的多態性表現,使得用戶對單個對象和組合對象的使用具備一致性。
提供了一種遍歷樹形結構的方案,組合模式能夠很是方便地描述對象的層次結構。
統一地使用組合結構中的全部對象,不須要關心它到底是組合對象仍是單個對象。
有時候把上下級對象稱爲父子節點,但你們要知道,它們並不是真正意義上的父子關係。
只有用一致的方式對待列表中的每一個葉對象的時候,才適合使用組合模式。
咱們能夠把相同的操做應用在組合對象和單個對象上。大多數狀況下,咱們均可以忽略掉組合對象和單個對象之間的差異,從而用一致的方式來處理它們。
嚴重依賴抽象類,使用繼承和重寫父類的某些方法來實現功能的設計模式。
JavaScript 沒有從語法層面提供對抽象類怎麼辦?
模板方法模式是一種典型的經過封裝變化提升系統擴展性的設計模式。子類的方法種類和執行順序在抽象類中定義且不可變,新功能經過增長子類且不須要改動抽象父類及其餘子類便可實現,這也符合開放-封閉原則。
享元模式是一種用於性能優化的模式,核心是運用共享技術來有效支持大量細粒度的對象。
享元模式要求將對象的屬性劃分爲內部狀態與外部狀態,目標是儘可能減小共享對象的數量。
對象池維護一個裝載空閒對象的池子,須要對象的時候,不是直接 new,而是轉從對象池裏獲取。若是對象池裏沒有空閒對象,則建立一個新對象,當獲取出的對象完成它的職責以後, 再進入池子等待被下次獲取。
享元模式是爲解決性能問題而生的模式,大部分模式的誕生緣由都不同。在一個存在大量類似對象的系統中,享元模式能夠很好地解決大量對象帶來的性能問題。
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
傳統實現就像一根環環相扣打了死結的鏈條,若是要增長、拆除或者移動一個節點,就必須得先砸爛這根鏈條。
職責鏈模式的最大優勢就是解耦了請求發送者和 N 個接收者之間的複雜關係, 請求發送者只須要知道鏈中的第一個節點,弱化了發送者和一組接收者之間的強聯繫。
不能保證某個請求必定會被鏈中的節點處理,大部分節點沒有起到實質性的做用,僅是讓請求傳遞下去。 從性能方面考慮,咱們要避免過長的職責鏈帶來的性能損耗。
在 JavaScript 開發中,職責鏈模式是最容易被忽視的模式之一。只要運用得當,能夠下降發起請求的對象和處理請求對象之間的耦合性,能夠自由變化職責鏈中的節點數量和順序。
用來下降多個對象和類之間的通訊複雜性,它提供了一箇中介類,該類處理不一樣類之間的通訊,並支持鬆耦合,使代碼易於維護。
面向對象設計鼓勵將行爲分佈到各個對象中,把對象劃分紅更小的粒度,有助於加強對象的可複用性,但因爲這些細粒度對象之間的聯繫激增,又有可能會反過來下降它們的可複用性。
中介者模式的做用就是解除對象與對象之間的緊耦合關係。
增長一箇中介者對象後,全部的相關對象都經過中介者對象來通訊,而不是互相引用,因此當一個對象發生改變時,只須要通知中介者對象便可。
中介者使各對象之間耦合鬆散,並且能夠獨立地改變它們之間的交互。中介者模式使網狀的多對多關係變成了相對簡單的一對多關係。
中介者模式的缺點是系統中會新增一箇中介者對象,由於對象之間交互的複雜性,轉移成了中介者對象的複雜性,使得中介者對象常常是巨大的。中介者對象自身每每就是一個難以維護的對象。
在不改變對象自身的基礎上,程序運行期間動態給對象添加職責的方式稱爲裝飾者模式。
在程序開發中並不但願某個類天生就很是龐大,一次性包含許多職責; 裝飾者模式能夠動態地給某個對象添加一些額外的職責,而不會影響這個類派生的其餘對象。
在《設計模式》成書以前,GoF原想把裝飾者(decorator)模式稱爲包裝器(wrapper)模式。
從功能上而言,decorator能很好地描述這個模式,但從結構上看,wrapper的說法更加貼切。裝飾者模式將一個對象嵌入另外一個對象之中,實際上至關於這個對象被另外一個對象包裝起來,造成一條包裝鏈。請求隨着這條鏈依次傳遞到全部的對象,每一個對象都有處理這條請求的機會
代理模式和裝飾者模式最重要的區別在於它們的意圖和設計目的。
兩種模式都描述了怎樣爲對象提供必定程度上的間接引用,它們的實現部分都保留了對另一個對象的引用,而且向那個對象發送請求。
代理模式的目的是,當直接訪問本體不方便或者不符合須要時,爲這個本體提供一個替代者。本體定義了關鍵功能,而代理提供訪問本體以前作一些額外的事情,或拒絕對它的訪問, 代理模式強調一種關係(Proxy與它的實體之間的關係),這種關係能夠靜態的表達,一開始就能夠被肯定。
裝飾者模式的做用就是爲對象動態加入行爲,而裝飾者模式用於一開始不能肯定對象的所有功能時使用。代理模式一般只有一層代理,而裝飾者模式常常會造成一條長長的裝飾鏈。
容許一個對象在內部狀態改變時改變它的行爲。
狀態模式的關鍵是區分事物的內部狀態,事物狀態的改變會帶來事物行爲的改變,每種狀態都封裝成單獨的類,跟此種狀態有關的行爲都被封裝在這個類的內部,當請求對象的某個行爲時,把這個請求委託給當前的狀態對象的行爲便可。
適配器模式的做用是解決兩個軟件實體間的接口不兼容的問題。
當咱們試圖調用模塊或者對象的某個接口時,發現這個接口的格式並不符合目前的需求時, 建立一個適配器,將原接口轉換爲客戶但願的另外一個接口,客戶只須要和適配器打交道。 使用適配器模式以後,本來因爲接口不兼容而不能工做的兩個軟件實體能夠一塊兒工做。
適配器模式是一種「亡羊補牢」的模式,沒有人會在程序的設計之初就使用它。
適配器模式是一對相對簡單的模式。有一些模式跟適配器模式的結構很是類似,好比裝飾者模式、代理模式和外觀模式,這幾種模式都屬於「包裝模式」,都是由一個對象來包裝另外一個對象。區別它們的關鍵仍然是模式的意圖。
原文是這麼說的:
從某種角度來看,設計模式的目的就是爲許多重構行爲提供目標。
若是在函數中有一段代碼能夠被獨立出來,最好把這些代碼放進另一個獨立的函數中。
若是一個函數體內有一些條件分支語句,而這些條件分支語句內部散佈了一些重複的代碼,那麼就有必要進行合併去重工做。
在程序設計中,複雜的條件分支語句是致使程序難以閱讀和理解的重要緣由,並且容易致使一個龐大的函數。
在函數體內,若是有些代碼實際上負責的是一些重複性的工做,那麼合理利用循環不只能夠完成一樣的功能,還可使代碼量更少。
用《重構》裏的話說:
嵌套的條件分支每每是由一些深信「每一個函數只能有一個出口的」程序員寫出的。但實際上,若是對函數的剩餘部分不感興趣,那就應該當即退出。引導閱讀者去看一些沒有用的else片斷,只會妨礙他們對程序的理解。
一個函數接收的參數數量越多,函數就越難理解和使用。在使用的時候,還要防止少傳了某個參數或者把兩個參數搞反了位置。 使用對象就能夠不用再關心參數的數量和順序,只要保證參數對應的key值不變就能夠了。
若是一個函數不須要傳入任何參數就可使用,這種函數是深受人們喜好的。在實際開發中,向函數傳遞參數不可避免,但咱們應該儘可能減小函數接收的參數數量。
使用三目運算符和使用if、else代碼循環一百萬次,時間開銷仍處在同一個級別裏。 若是條件分支邏輯簡單清晰可用三目運算符,如邏輯複雜建議仍是使用if、else。
合理使用鏈式調用 鏈式調用帶來的壞處是調試很是不方便,若是鏈條很容易發生變化,建議使用普通調用的形式。
假設在函數體內有一個兩重循環語句,使用控制標記變量或者設置循環標記這兩種作法無疑都讓人頭暈目眩。 用return直接退出方法會帶來一個問題,未來不能執行循環以後的代碼,能夠return函數。
你要是能看到這裏,真的是太厲害了,這是一篇羅列知識點的總結,自己就很枯燥,我在整理彙總的時候已經快失去耐心了🤧,能看完的必定是很厲害的大牛,也想請你們推薦幾本關於軟件設計的理論方面的書,感謝親噠噠噠~~~,哦,對了🤗,撩騷一下,有收穫的話就給個當心心吧😍😍😘。