【js设计模式笔记---代理模式】

来源:互联网 发布:明星开的淘宝店 编辑:程序博客网 时间:2024/05/01 18:40

代理模式

代理是一个对象,它可以用来控制对另一个对象的访问。它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象。另外那个对象通常称为本体。代理可以代替其本体被实例化,并使其可被远程访问。它还可以把本体的实例化推迟到真正需要的时候,对于实例化比较费时的本体,或者因尺寸较大以至于不用时不宜保存在内存中的本体,这特别有用。在处理那些需要较长时间才能把数据载入用用户界面的类时,代理也大有裨益。

代理结构

代理模式最基本的形式是对访问进行控制。代理对象和另一个对象(本体)实现的是同样的接口。实际上工作还是本体在做。它才是负责执行所分派的任务的那个对象或类。代理对象所做的不外乎节制对本体的访问。要注意,代理对象并不会在另一个对象的基础上添加方法或修改其方法(就像装饰那样),也不会简化那个对象的接口(就像门面元素那样)。它实现的接口与本体完全相同。所有对它进行的方法调用都会被传递给本体。

代理如何控制对本体的访问

那种根本不实现任何访问控制的代理最简单。它所做的只是把所有方法调用传递到本体。这种代理离无用处,但它也可提供一个进一步发展的基础。

var Publication =new Interface(“Publication”,[‘getIsbn’,’setIsbn’,’getTitle’,’setTitle’,’getAuthor’,’setAuthor’,’display’]);

var Book =function(isbn,title,author){…} //implements Publication

//Libraryinterface

var Library = new Interface(“Library”,[“findBooks”,”checkoutBook”,”returnBook”]);

/*PublicLibraryclass*/

var PublicLibrary= function(books){

   this.catalog = {};

   for(var i=0,len=books.length;i<len;i++){

       this.catalog[books[i].getIsbn()] ={book:book[i],available:true};

   }

}

 

 

PublicLibrary.prototype={

    findBooks:function(searchString){

        var results=[];

        for(var isbn in this.catalog){

           if(!this.catalog.hasOwnProperty(isbn))continue;

           if(searchString.match(this.catalog[isbn].getAuthor()) ||searchString.match(this.catalog[isbn].getTitle())){

               results.push(this.catalog[isbn]);

            }

        }

        return results;

    },

    checkoutBook:function(book){

        var isbn = book.getIsbn();

        if(this.catalog[isbn]){

            if(this.catalog[isbn].available){

                this.catalog[isbn].available=false;

                return this.catalog[isbn];

            }else{

                throw new Error("PublicLibrary: book"+book.getTitle()+ " is not currentlyavailable.");

            }

        }

    },

    returnBook:function(boo){

        var isbn = book.getIsbn();

        if(this.catalog[isbn]){

            this.catalog[isbn].available=true;

        }else{

            throw new Erro("PublicLibrary:book"+book.getTitle()+" not found.");

        }

    }

}

这个类非常简单。它可以用来查书、借书和还书。下面是一个没有实现任何访问控制的PublicLibrary类的代理:

/*PublicLibraryProxyclass , a useless proxy*/

 

var  PublicLibraryProxy = function(catalog){

    this.library = new PublicLibrary(catalog);

};

PublicLibraryProxy.prototype= {

findBooks:function(searchString){

    return this.library.findBooks(searchString);

},

checkoutBook:function(book){

     return this.library.checkoutBook(book);

},

returnBook:function(book){

      return this.library.returnBook(book);

}

}

 

前面已经说过,这种类型的代理没有什么用处。在各种类型的代理中,虚拟代理最有用的类型之一。虚拟代理用于控制对那种创建开销很大的本体的访问。它会把本体的实例化推迟到有方法被调用的时候,有时还会提供关于实例化状态的反馈。它还可以在本体被加载之前扮演其替身的角色。作为一个例子,假设PublicLibrary的实例化很慢,不能在网页加载的时候立即完成。我们可以为其创建一个虚拟代理,让它把PublicLibrary的实例化推迟到必要的时候

/*PublicLibraryVirtualProxyclass*/

var PublicLibraryVirtualProxy= function(catalog){

   this.library = null;

   this.catalog = catalog;

}

PublicLibraryVirtualProxy.prototype= {

     _initializeLibrary:function(){

        if(this.library===null){

            if(this.library ===null){

               this.library = new PublicLibrary(this.catalog);

            }

        },

findBooks:function(searchString){

     this._ initializeLibrary();

    returnthis.library.findBooks(searchString);

},

checkoutBook:function(book){

     this._ initializeLibrary();

     returnthis.library.checkoutBook(book);

},

returnBook:function(book){

     this._ initializeLibrary();

      returnthis.library.returnBook(book);

}

     }

}

 

PublicLibraryProxy和PublicLibraryVirtualProxy之间的关键区别在于后者不会立即创建PublicLibrary实例。PublicLibraryVirtualProxy会把构造函数的参数保存起来,直到有方法被调用才真正执行本体实例化。这样一来,如果图书馆对象一直未被用到,那么它就不会被创建出来。虚拟代理通常具有某种能触发本体的实例化的事件。

代理模式和装饰者模式的比较

代理有许多方面都很像装饰者。装饰者和虚拟代理都要对其他对象进行包装,都要实现与被包装对象相同的接口,而且都要把方法调用传递给被包装对象。那么二者的区别:最大的区别在于装饰者会对被包装对象的功能进行修改或扩充,而代理只不过是控制对它的访问。除了有时可能会添加一些控制代码之外,代理并不会对传递给本体的方法调用进行修改。而装饰者就是为修改方法而生的。另一个区别表现在被包装的对象的创建方式上。在装饰者模式中,被包装对象的实例化过程是完全独立的。这个对象创建之后,你可随意为其裹上一个或更多装饰者。而在代理模式中,被包装对象的实例华是代理实例化过程的一部分。在某些类型的虚拟代理中,这种实例化受到严格控制,它必须在代理内部进行。此外,代理不会像装饰者那样互相包装。它们一次只使用一个。

 

包装Web服务的通用模式

/*WebserviceProxyclass*/

var WebsrviceProxy= function(){

     this.xhrHandler =XhrManager.createXhrHandler();

};

WebsrviceProxy.prototype= {

_xhrFailure:function(statusCode){

    throw newError(“StatsProxy:Asynchronous request for stats failed.”);

},

_fetchData:function(url,dataCallback,getVars){

    varthat  = this;

    varcallback={

             success:function(responseText){

                      var obj = eval(“(“+responseText+”)”);

                       dataCallback(obj);

               },

              failure:that._xhrFailure

    };

    vargetVarArray = [];

    for(varNamein getVars){

         getVarArray.push(varName+”=”+getVars[varName]);

     }

    if(getVarArray.length>0){

         url =url+”?”+getVarArray.join(“&”);

      }

      xhrHandler.request(“GET”,url,callback);

}

}

使用这个通用模式时,只需要从WebserviceProxy派生一个子类,然后再借助_fetchData方法实现需要的方法即可。如果把StatsProxy类实现为WebserviceProxy的子类,其结果大致如下:

/*StatsProxy class*/

var  StatsProxy = function(){};

extend(statsProxy,WebserviceProxy);

 

StatsProxy.prototype.getPageviews= function(callback, startDate,endDate,page){

     this._fetchData(‘/stats/getPageviews/’,callback,{

       ‘startDate’:startDate,

       ‘endDate’:endDate,

       ‘page’:page

});

 

}

0 0