1. 程式人生 > >js設計模式——代理模式proxy

js設計模式——代理模式proxy

什麼是代理模式

代理模式是為一個物件提供一個代用品或佔位符,以便控制對它的訪問。

(可以想象一下明星與經紀人的關係,明星是請求的本體,經紀人就是代理proxy)

如何實現代理模式

代理物件內部含有對本體物件的引用,因而可以與呼叫本體的相關方法;同時,代理物件提供與本體物件相同的介面,方便在任何時刻代理本體物件。

例子(上程式碼)

代理模式的變體有很多,有:保護代理虛擬代理快取代理、防火牆代理、遠端代理、智慧引用代理、寫時複製代理。具體介紹前三種。

(1)保護代理

保護代理主要用於控制不同許可權的物件對本體物件的訪問許可權。比如很多人想訪問本體A,如果有代理B存在的話,B會首先剔除不滿足A的訪問條件的訪問者,符合條件的才能訪問。

作用:過濾請求

例如:許可權的劃分和管理就是使用保護代理proxy來完成的。

註冊普通使用者:code為“001”

論壇管理者   :code為“002”

系統管理者   :code為“003”

遊        客    :code為“000”

論壇開放了四個基礎功能

1,發帖

2,帖子稽核

3,清除帖子

4,留言

遊客不具備任何操作許可權,註冊使用者只能發帖,論壇管理者可以稽核以及刪帖操作,系統管理者具有所有功能許可權。

//使用者本體
function User(name,code){
    this.name = name ;
    this.code = code ;
} ;
User.prototype = {
    getName : function(){
        return this.name ;
    } ,
    getCode : function(){
        return this.code ;
    } ,
    post : function(){
        console.log("發帖子!") ;
    } ,
    remove : function(){
        console.log("刪除帖子!") ;
    } ,
    check : function(){
        console.log("稽核帖子!") ;
    } ,
    comment : function(){
        console.log("回覆帖子!") ;
    }
} ;
//代理論壇類
function Forum(user){
    this.user = user ;
} ;
Forum.prototype = {
    getUser : function(){
        return this.user ;
    } ,
    post : function(){
        if(this.user.getCode() == "001" || this.user.getCode() == "003"){
            return this.user.post() ;
        }
        console.log("沒許可權發帖子!") ;
    } ,
    remove : function(){
        if(this.user.getCode() == "002" || this.user.getCode() == "003"){
            return this.user.remove() ;
        }
        console.log("沒許可權刪除帖子!") ;
    } ,
    check : function(){
        if(this.user.getCode() == "002" || this.user.getCode() == "003"){
            return this.user.check() ;
        }
        console.log("沒許可權稽核帖子!") ;
    } ,
    comment : function(){
        if(this.user.getCode() == "003"){
            return this.user.comment() ;
        }
        console.log("沒許可權回覆帖子!") ;
    }
} ;
//功能測試
function ForumClient(){
     this.run = function(){
         new Forum(new User("bigbear","003")).check() ; // 稽核帖子
     }
 } ;

在該例子中,論壇代理有與user本體相同的介面,可以在滿足條件時,執行與本體相同的程式碼,與呼叫方法的人而言,是不透明的,我實現了呼叫,但不在乎是通過代理實現的,還是本體實現的。

(2)虛擬代理

虛擬代理是將呼叫本體方法的請求進行管理,等到本體適合執行時,再執行。

作用:將開銷很大的物件,延遲到真正需要它的時候再執行。

比如:利用虛擬代理實現圖片預載入功能:

/**在圖片預載入中實現虛擬代理 */
var myImage = (function(){
    var imageNode = document.createElement('img');
    document.body.appendChild(imageNode);

    return {
        setSrc: function(src){
            imageNode.src = src;
        }
    }
})()

//代理類
var proxyImage = (function(){
    var img = new Image();
    img.onload = function(){
        myImage.setSrc(this.src);
    }

    return {
        setSrc: function(src){
            myImage.setSrc('本地的圖片地址');
            img.src = src; //快取完畢之後會觸發img的onload事件
        }
    }
})()

比如:利用虛擬代理合並HTTP請求

/**虛擬代理合並http請求 */
//通過代理函式收集一段時間的請求,一次性發送給伺服器,減少頻繁的網路請求帶來的極大開銷
//模擬向伺服器傳送同步請求的函式
var synchronousFile = function(id){
    console.log('開始同步上傳檔案,id為:'+id);
}

//代理類收集一段時間的同步請求,統一發送
var proxySynchronousFile = (function(){
    var cache = [], //設定快取陣列
        timer; //定時器,通過閉包訪問定時器的引用

    return function(id){
        cache.push(id);
        if(timer){
            return;
        }
        timer = setTimeout(function(){
            synchronousFile(cache.join(','));
            clearTimeout(timer);
            timer = null;
            cache.length = 0;
        },2000)
    }
})()

var checkbox = document.getElementsByTagName('input');

for(var i=0,c;c=checkbox[i++];){
    c.onclick = function(){
        if(this.check === true){
            proxySynchronousFile(this.id);
        }
    }
}

在這些例子中,虛擬代理對請求進行擱置處理,等到合適的時機,對本體的介面進行呼叫,可以有效提升Web效能。

(3)快取代理

快取代理可以為開銷大的一些運算結果提供暫時性的儲存,如果再次傳進相同的引數是,直接返回結果,避免大量重複計算。

/**建立快取代理工廠 */
//將快取代理與工廠模式相結合,建立多種運算的快取代理
var mult = function(){
    var a = 1;
    for(var i=0;i<arguments.length;i++){
        a = a*arguments[i];
    }
    return a;
}
var plus = function(){
    var a = 0;
    for(var i=0; i<arguments.length; i++){
        a = a + arguments[i];
    }
    return a;
}
//高階函式:將函式作為引數或者返回值的函式
var proxyFactory = function(fn) {
    var cache = {}; //引數快取列表
    return function(){
        var args = Array.prototype.join.call(arguments,',');
        if(args in cache){
            return cache[args];
        }
        //引數屬性對應的是函式
        return cache[args] = fn.apply(this,arguments);
    }
}

//測試
var proxyMult = proxyFactory(mult),
    proxyPlus = proxyFactory(plus);

console.log(proxyMult(1,2,3,4));
console.log(proxyMult(1,2,3,4));
console.log(proxyPlus(5,6,7,8));
console.log(proxyPlus(5,6,7,8));

什麼情況下使用代理

當我們需要使用的物件很複雜或者需要很長時間去構造,這時就可以使用代理模式(Proxy)。例如:如果構建一個物件很耗費時間和計算機資源,代理模式(Proxy)允許我們控制這種情況,直到我們需要使用實際的物件。一個代理(Proxy)通常包含和將要使用的物件同樣的方法,一旦開始使用這個物件,這些方法將通過代理(Proxy)傳遞給實際的物件。

比如上面的程式碼:需要花很長的時間載入很多圖片,複雜的運算過程,頻繁的多次請求處理等;都可以用到代理模式。

小結

代理模式的一個好處就是對外部提供統一的介面方法,而代理類在介面中實現對真實類的附加操作行為,從而可以在不影響外部呼叫情況下,進行系統擴充套件。也就是說,我要修改真實角色的操作的時候,儘量不要修改他,而是在外部在“包”一層進行附加行為,即代理類。

參考書籍《JavaScript設計模式與開發實踐》

進擊的菜鳥,需要繼續努力啊!!!多學、多看、多實踐!!