1 設計原則概述
- 《
UNIX/LINUX
設計哲學》設計準則
① 小既是美。
② 每一個程序只作一件事情。
③ 快速創建原型。
④ 捨棄高效率而取可移植性。
⑤ 避免強制性的圖形化界面交互。
⑥ 讓每一個程序都成爲過濾器。
⑦ 尋求90%的解決方案。
註釋:花20%的成本解決80%的需求。
- 五大設計原則(
SOLID
)
① S
- 單一職責原則
② O
- 開放封閉原則
③ L
- 李氏置換原則
④ I
- 接口獨立原則
⑤ D
- 依賴倒置原則
- 單一職責原則
一個程序只作好一件事情。
- 開放封閉原則
對擴展開放,對修改封閉。
增長需求時,擴展新代碼,而非修改已有代碼。
- 李氏置換原則
子類能覆蓋父類。
父類能出現的地方子類就能出現。
- 接口獨立原則
保持接口的獨立,避免出現「胖接口」。
- 依賴倒置原則
面向接口編程,依賴於抽象而不依賴於具體。
2 單一職責原則
- 簡介
就一個類、對象以及方法而言,應該僅有一個引發它變化的緣由。
註釋:單一職責原則定義爲「引發變化的緣由」。若是咱們有兩個動機去改寫一個方法,那麼這個方法就具備兩個職責。
- 原則
一個對象(方法)只作一件事情。
- 設計模式驗證
① 代理模式
圖片預加載代理模式中,代理對象負責預加載職責,本體對象負責圖片加載職責。
② 迭代器模式
迭代器模式提供遍歷訪問聚合對象的職責。
③ 單例模式
將建立對象與管理單例分別封裝在兩個方法中,兩個方法相互獨立互不影響。
④ 裝飾者模式
讓類或者對象一開始只具備一些基礎的職責,更多的職責在代碼運行時被動態裝飾到對象上面。裝飾者模式能夠爲對象動態增長職責,從另外一個角度來看, 這也是分離職責的一種方式。
- 應用場景
① 若是有兩個職責老是同時變化,那就沒必要分離他們。即便兩個職責已經被耦合在一塊兒,但它們尚未發生改變的徵兆,那麼也許沒有必要主動分離它們。
② 在方便性與穩定性之間要有一些取捨。具體是選擇方便性仍是穩定性,並無標準答案,而是要取決於具體的應用環境。例如:jQuery
的 attr
是個很是龐大的方法,既負責賦值,又負責取值,這對於jQuery
的維護者來講,會帶來一些困難,但對於jQuery
的用戶來講,卻簡化了用戶的使用。
- 優缺點
① 優勢
按照職責把對象分解成更小的粒度,下降了單個類或者對象的複雜度,有助於代碼的複用,也有利於進行單元測試。
② 缺點
增長了編寫代碼的複雜度,也增大了這些對象之間相互聯繫的難度。
3 最少知識原則
- 簡介
最少知識原則(LKP
)指一個軟件實體應當儘量少地與其餘實體發生相互做用。這裏的軟件實體不只包括對象,還包括系統、類、模塊、函數、變量等。
- 減小對象之間的聯繫
單一職責原則指導咱們把對象劃分紅較小的粒度,提升對象的可複用性。但愈來愈多的對象之間可能會產生錯綜複雜的聯繫,若是修改了其中一個對象,極可能會影響到跟它相互引用的其餘對象。
最少知識原則要求咱們儘可能減小對象之間的交互。若是兩個對象之間沒必要彼此直接通訊,那麼這兩個對象就不要發生直接的相互聯繫。
- 設計模式驗證
① 中介者模式
增長一箇中介者對象,讓全部的相關對象都經過中介者對象來通訊,而不是互相引用。當一個對象發生改變時,只須要通知中介者對象便可。
② 外觀模式
外觀模式對客戶提供一個簡單易用的高層接口,高層接口會把客戶的請求轉發給子系統來完成具體的功能實現。
4 開放-封閉原則
- 簡介
軟件實體(類、模塊、函數)等應該是能夠擴展的,可是不可修改。
- 原則
當須要改變一個程序的功能或者給這個程序增長新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼。
- 實現方式
經過封裝變化的方式,能夠把系統中穩定不變的部分和容易變化的部分隔離開來。
(1) 利用對象多態性
利用對象的多態性來消除條件分支語句。
(2) 放置掛鉤
在程序有可能發生變化的地方放置一個掛鉤,掛鉤的返回結果決定了程序的下一步走向。
(3) 回調函數
把一部分易於變化的邏輯封裝在回調函數裏,而後把回調函數看成參數傳入一個穩定和封閉的函數中。
- 設計模式驗證
① 觀察者模式
當有新的訂閱者出現時,發佈者的代碼不須要進行任何修改;一樣當發佈者須要改變時,也不會影響到以前的訂閱者。
② 模板方法模式
子類的方法種類和執行順序都是不變的,因此咱們把這部分邏輯抽出來放到父類的模板方法裏面;而子類的方法具體怎麼實現則是可變的,因而把這部分變化的邏輯封裝到子類中。經過增長新的子類,便能給系統增長新的功能,並不須要改動抽象父類以及其餘的子類。
③ 策略模式
策略模式將各類算法都封裝成單獨的策略類,這些策略類能夠被交換使用。策略和使用策略的客戶代碼能夠分別獨立進行修改而互不影響。
④ 代理模式
圖片預加載示例中,代理函數proxyMyImage
負責圖片預加載,myImage
圖片加載函數不須要任何改動。
⑤ 職責鏈模式
新增處理函數時,不須要改動原有的鏈條節點代碼,只須要在鏈條中增長一個新的節點。
5 代碼重構
- 提煉函數
若是在函數中有一段代碼能夠被獨立出來,那咱們最好把這些代碼放進另一個獨立的函數中。
var getUserInfo = function () { ajax('http:// xxx.com/userInfo', function (data) { console.log('userId: ' + data.userId); console.log('userName: ' + data.userName); console.log('nickName: ' + data.nickName); }) }
- 合併重複的條件片斷
若是一個函數體內有一些條件分支語句,而這些條件分支語句內部散佈了一些重複的代碼,那麼就有必要進行合併去重工做。
var paging = function (currPage) { if (currPage <= 0) { currPage = 0; jump(currPage);
- 把條件分支語句提煉成函數
在程序設計中,複雜的條件分支語句是致使程序難以閱讀和理解的重要緣由,並且容易致使一個龐大的函數。
var getPrice = function (price) { var date = new Date(); if (date.getMonth() >= 6 && date.getMonth() <= 9) { return price * 0.8; } return price; };
- 合理使用循環
在函數體內,若是有些代碼實際上負責的是一些重複性的工做,那麼合理利用循環不只能夠完成一樣的功能,還可使代碼量更少。
var createXHR = function () { var xhr; try { xhr = new ActiveXObject('MSXML2.XMLHttp.6.0'); } catch (e) { try { xhr = new ActiveXObject('MSXML2.XMLHttp.3.0'); } catch (e) { xhr = new ActiveXObject('MSXML2.XMLHttp'); } } return xhr; }; var xhr = createXHR();
- 提早讓函數退出代替嵌套條件分支
嵌套的條件分支語句絕對是代碼維護者的噩夢。嵌套的條件分支每每是由一些深信「每一個函數只能有一個出口的」程序員寫出的。但實際上,若是對函數的剩餘部分不感興趣,那就應該當即退出。
var del = function (obj) { var ret; if (!obj.isReadOnly) {
- 傳遞對象參數代替過長的參數列表
有時候一個函數有可能接收多個參數,而參數的數量越多,函數就越難理解和使用。在使用的時候,還要當心翼翼,以避免少傳了某個參數或者把兩個參數搞反了位置。
這時咱們能夠把參數都放入一個對象內,不用再關心參數的數量和順序,只要保證參數對應的 key
值不變就能夠了。
- 儘可能減小參數數量
在實際開發中,向函數傳遞參數不可避免,但咱們應該儘可能減小函數接收的參數數量。
- 少用三目運算符
若是條件分支邏輯簡單且清晰,這無礙咱們使用三目運算符;但若是條件分支邏輯很是複雜,咱們最好的選擇仍是循序漸進地編寫 if...else...
。
- 合理使用鏈式調用
常用jQuery
的程序員至關習慣鏈式調用方法,在JavaScript
中,能夠很容易地實現方法的鏈式調用,即讓方法調用結束後返回對象自身。
使用鏈式調用的方式能夠省下一些字符和中間變量,但調試時不方便。若是咱們知道一條鏈中有錯誤出現,必須得先把這條鏈拆開才能加上一些調試log
或者增長斷點。
若是該鏈條的結構相對穩定,後期不易發生修改,可使用鏈式調用。若是該鏈條很容易發生變化,致使調試和維護困難,那麼仍是建議使用普通調用的形式。
- 分解大型類
面向對象設計鼓勵將行爲分佈在合理數量的更小對象之中。
- 用
return
退出多重循環 假設在函數體內有一個兩重循環語句,咱們須要在內層循環中判斷,當達到某個臨界條件時退出外層的循環。咱們大多數時候會引入一個控制標記變量或設置循環標記。 這兩種作法無疑都讓人頭暈目眩,更簡單的作法是在須要停止循環的時候直接退出整個方法。 若是在循環以後還有一些將被執行的代碼,咱們能夠把循環後面的代碼放到 return 後面,若是代碼比較多,就應該把它們提煉成一個單獨的函數。