《JavaScript設計模式與開發實踐》原則篇(3)—— 開放-封閉原則

在面向對象的程序設計中,開放封閉原則(OCP)是最重要的一條原則。不少時候,一個程序具備良好的設計,每每說明它是符合開放封閉原則的。 當須要改變一個程序的功能或者給這個程序增長新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼。設計模式

故事背景

假設咱們是一個大型 Web 項目的維護人員,在接手這個項目時,發現它已經擁有10萬行以上的JavaScript代碼和數百個 JS 文件。 不久後接到了一個新的需求,即在 window.onload 函數中打印出頁面中的全部節點數量。這 固然難不倒咱們了。因而咱們打開文本編輯器,搜索window.onload函數在文件中的位置,在函數內部添加如下代碼bash

window.onload = function(){ 
    // 原有代碼略
    console.log( document.getElementsByTagName( '*' ).length ); 
};
複製代碼

應用OCP原則

Function.prototype.after = function( afterfn ){ 
    var __self = this;
    return function(){
        var ret = __self.apply( this, arguments ); 
        afterfn.apply( this, arguments );
        return ret;
    } 
};
window.onload = ( window.onload || function(){} ).after(function(){ 
    console.log( document.getElementsByTagName( '*' ).length );
});
複製代碼

經過動態裝飾函數的方式,咱們徹底不用理會從前 window.onload 函數的內部實現,就算拿到的是一份混淆壓縮過的代碼也沒有關係。只要它從前是個穩定運行的函數,那麼之後也不會由於咱們的新增需求而產生錯誤。新增的代碼和原有的代碼能夠互不影響。app

編寫符合OCP代碼的方法

過多的條件分支語句是形成程序違反開放封閉原則的一個常見緣由。每當須要增長一個新 的 if 語句時,都要被迫改動原函數。實際上,每當咱們看到一大片的 if 或者 swtich-case 語句時,第一時間就應該考慮,可否利用對象的多態性來重構它們。編輯器

利用多態的思想

利用對象的多態性來讓程序遵照開放封閉原則,是一個經常使用的技巧。函數

  • 不符合OCP
var makeSound = function( animal ){
    if ( animal instanceof Duck ){ 
        console.log( '嘎嘎嘎' ); 
    } else if ( animal instanceof Chicken ) {
        console.log( '咯咯咯' );
    }
};
var Duck = function(){}; 
var Chicken = function(){};
makeSound( new Duck() ); 
makeSound( new Chicken() );

//動物世界裏增長一隻狗以後,makeSound 函數必須改爲:
var makeSound = function( animal ){ 
    if ( animal instanceof Duck ){
        console.log( '嘎嘎嘎' ); 
    } else if ( animal instanceof Chicken ) {
        console.log( '咯咯咯' ); 
    } else if ( animal instanceof Dog ) {
        console.log('汪汪汪' ); 
    }
};
var Dog = function(){};
// 增長跟狗叫聲相關的代碼
 makeSound( new Dog() ); // 增長一隻狗
複製代碼

利用多態的思想,咱們把程序中不變的部分隔離出來(動物都會叫),而後把可變的部分封 裝起來(不一樣類型的動物發出不一樣的叫聲),這樣一來程序就具備了可擴展性。當咱們想讓一隻狗發出叫聲時,只需增長一段代碼便可,而不用去改動原有的 makeSound函數post

var makeSound = function( animal ){ 
    animal.sound();
};
var Duck = function(){};
Duck.prototype.sound = function(){ 
    console.log( '嘎嘎嘎' );
};
var Chicken = function(){};
Chicken.prototype.sound = function(){ 
    console.log( '咯咯咯' );
};
makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
/********* 增長動物狗,不用改動原有的 makeSound 函數 ****************/
var Dog = function(){}; Dog.prototype.sound = function(){
console.log( '汪汪汪' ); };
makeSound( new Dog() ); // 汪汪汪
複製代碼
放置掛鉤

放置掛鉤(hook)也是分離變化的一種方式。咱們在程序有可能發生變化的地方放置一個掛鉤,掛鉤的返回結果決定了程序的下一步走向。這樣一來,本來的代碼執行路徑上就出現了一個 分叉路口,程序將來的執行方向被預埋下多種可能性。ui

使用回調函數

在 JavaScript 中,函數能夠做爲參數傳遞給另一個函數,這是高階函數的意義之一。在這 種狀況下,咱們一般會把這個函數稱爲回調函數。在 JavaScript版本的設計模式中,策略模式和命令模式等均可以用回調函數輕鬆實現。 回調函數是一種特殊的掛鉤。咱們能夠把一部分易於變化的邏輯封裝在回調函數裏,而後把 回調函數看成參數傳入一個穩定和封閉的函數中。當回調函數被執行的時候,程序就能夠由於回 調函數的內部邏輯不一樣,而產生不一樣的結果。this

總結

開放封閉原則是一個看起來比較虛幻的原則,但咱們仍是能找到一些讓程序儘可能遵照開放封閉原則的規律,最明顯的就是找出程序中將要發生變化的地方,而後把變化封裝起來。 經過封裝變化的方式,能夠把系統中穩定不變的部分和容易變化的部分隔離開來。在系統的 演變過程當中,咱們只須要替換那些容易變化的部分,若是這些部分是已經被封裝好的,那麼替換起來也相對容易。而變化部分以外的就是穩定的部分。在系統的演變過程當中,穩定的部分是不須要改變的。spa

系列文章:

《JavaScript設計模式與開發實踐》最全知識點彙總大全prototype

相關文章
相關標籤/搜索