《JavaScript設計模式與開發實踐》模式篇(12)—— 裝飾者模式

在傳統的面嚮對象語言中,給對象添加功能經常使用繼承的方式,可是繼承的方式並不靈活, 還會帶來許多問題:一方面會致使超類和子類之間存在強耦合性,當超類改變時,子類也會隨之 改變;另外一方面,繼承這種功能複用方式一般被稱爲「白箱複用」,「白箱」是相對可見性而言的, 在繼承方式中,超類的內部細節是對子類可見的,繼承經常被認爲破壞了封裝性。裝飾者模式可以在不改變對象自身的基礎上,在程序運行期間給對象動態地添加職責。跟繼承相比,裝飾者是一種更輕便靈活的作法,這是一種「即用即付」的方式ajax

故事背景

假設咱們在編寫一個飛機大戰的遊戲,隨着經驗值的增長,咱們操做的飛機對象能夠升級成更厲害的飛機,一開始這些飛機只能發射普通的子彈,升到第二級時能夠發射導彈,升到第三級時能夠發射原子彈。設計模式

代碼實現(使用裝飾者模式)

var plane = {
    fire: function(){
        console.log( '發射普通子彈' ); 
    }
}
var missileDecorator = function(){ 
    console.log( '發射導彈' );
}
var atomDecorator = function(){ 
    console.log( '發射原子彈' );
}
var fire1 = plane.fire;
plane.fire = function(){ 
    fire1();
    missileDecorator(); 
}
var fire2 = plane.fire;
plane.fire = function(){ 
    fire2();
    atomDecorator(); 
}
plane.fire();
// 分別輸出: 發射普通子彈、發射導彈、發射原子彈
複製代碼

使用AOP實現裝飾者模式

首先給出 Function.prototype.before 方法和 Function.prototype.after 方法bash

Function.prototype.before = function( beforefn ){
    var __self = this; // 保存原函數的引用
    return function(){ // 返回包含了原函數和新函數的"代理"函數
        beforefn.apply( this, arguments ); // 執行新函數,且保證 this 不被劫持,新函數接受的參數 // 也會被原封不動地傳入原函數,新函數在原函數以前執行
        return __self.apply( this, arguments ); // 執行原函數並返回原函數的執行結果,  // 而且保證 this 不被劫持
} }
Function.prototype.after = function( afterfn ){ 
    var __self = this;
    return function(){
        var ret = __self.apply( this, arguments ); 
        afterfn.apply( this, arguments );
        return ret;
    } 
};
複製代碼

AOP 的應用實例

  • 數據統計上報 好比頁面中有一個登陸 button,點擊這個 button 會彈出登陸浮層,與此同時要進行數據上報, 來統計有多少用戶點擊了這個登陸 button
    • 未使用AOP
    var showLogin = function(){ 
       console.log( '打開登陸浮層' ); 
       log( this.getAttribute( 'tag' ) );
    }
    var log = function( tag ){
       console.log( '上報標籤爲: ' + tag );
       (new Image).src = 'http:// xxx.com/report?tag=' + tag;
    }
    document.getElementById( 'button' ).onclick = showLogin;
    複製代碼
    • 使用AOP
    var showLogin = function(){ 
        console.log( '打開登陸浮層' );
    }
    var log = function(){
        console.log( '上報標籤爲: ' + this.getAttribute( 'tag' ) );
    }
    showLogin = showLogin.after( log ); // 打開登陸浮層以後上報數據
    document.getElementById( 'button' ).onclick = showLogin;
    複製代碼
  • 插件式的表單驗證 咱們不少人都寫過許多表單驗證的代碼,在一個 Web 項目中,可能存在很是多的表單,如 註冊、登陸、修改用戶信息等。在表單數據提交給後臺以前,經常要作一些校驗,好比登陸的時 候須要驗證用戶名和密碼是否爲空
    • 未使用AOP
    var formSubmit = function(){
        if ( username.value === '' ){
            return alert ( '用戶名不能爲空' ); 
        }
        if ( password.value === '' ){
            return alert ( '密碼不能爲空' );
        }
        var param = {
            username: username.value, password: password.value
        }
        ajax( 'http:// xxx.com/login', param );
    }
    submitBtn.onclick = function(){ 
        formSubmit();
    }
    複製代碼
    • 使用AOP
    var validata = function(){
        if ( username.value === '' ){
            alert ( '用戶名不能爲空' );
            return false; 
        }
        if ( password.value === '' ){ 
            alert ( '密碼不能爲空' ); 
            return false;
        } 
    }
    var formSubmit = function(){ 
        var param = {
            username: username.value,
            password:password.value
        }
        ajax( 'http:// xxx.com/login', param ); 
    }
    formSubmit = formSubmit.before( validata );
    submitBtn.onclick = function(){ 
        formSubmit();
    }
    複製代碼

小結

裝飾者模式和代理模式的結構看起來很是相像,這兩種模式都描述了怎樣爲對象提供 必定程度上的間接引用,它們的實現部分都保留了對另一個對象的引用,而且向那個對象發送 請求。 代理模式和裝飾者模式最重要的區別在於它們的意圖和設計目的。代理模式的目的是,當直接訪問本體不方便或者不符合須要時,爲這個本體提供一個替代者。本體定義了關鍵功能,而代理提供或拒絕對它的訪問,或者在訪問本體以前作一些額外的事情。裝飾者模式的做用就是爲對 象動態加入行爲。app

系列文章:

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

相關文章
相關標籤/搜索