代理是一个对象,它可以用来控制对另一个对象的访问。它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象。另外那个对象通常称为本体。代理可以代替其本体被实例化,并使其可被远程访问。它还可以把本体的实例化推迟到真正需要的时候,对于实例化比较费时的本体,或者因尺寸较大以至于不用时不宜保存在内存中的本体,这特别有用。在处理那些需要较长时间才能把数据载入用用户界面的类时,代理也大有裨益。
代理模式最基本的形式是对访问进行控制。代理对象和另一个对象(本体)实现的是同样的接口。实际上工作还是本体在做。它才是负责执行所分派的任务的那个对象或类。代理对象所做的不外乎节制对本体的访问。要注意,代理对象并不会在另一个对象的基础上添加方法或修改其方法(就像装饰那样),也不会简化那个对象的接口(就像门面元素那样)。它实现的接口与本体完全相同。所有对它进行的方法调用都会被传递给本体。
那种根本不实现任何访问控制的代理最简单。它所做的只是把所有方法调用传递到本体。这种代理离无用处,但它也可提供一个进一步发展的基础。
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 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会把构造函数的参数保存起来,直到有方法被调用才真正执行本体实例化。这样一来,如果图书馆对象一直未被用到,那么它就不会被创建出来。虚拟代理通常具有某种能触发本体的实例化的事件。 代理有许多方面都很像装饰者。装饰者和虚拟代理都要对其他对象进行包装,都要实现与被包装对象相同的接口,而且都要把方法调用传递给被包装对象。那么二者的区别:最大的区别在于装饰者会对被包装对象的功能进行修改或扩充,而代理只不过是控制对它的访问。除了有时可能会添加一些控制代码之外,代理并不会对传递给本体的方法调用进行修改。而装饰者就是为修改方法而生的。另一个区别表现在被包装的对象的创建方式上。在装饰者模式中,被包装对象的实例化过程是完全独立的。这个对象创建之后,你可随意为其裹上一个或更多装饰者。而在代理模式中,被包装对象的实例华是代理实例化过程的一部分。在某些类型的虚拟代理中,这种实例化受到严格控制,它必须在代理内部进行。此外,代理不会像装饰者那样互相包装。它们一次只使用一个。 /*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 }); }代理模式和装饰者模式的比较
包装Web服务的通用模式