代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。
代理模式类图:
在上面类图中,代理模式所涉及的角色有三个:
抽象主题角色(Person):声明了真实主题和代理主题的公共接口,这样一来在使用真实主题的任何地方都可以使用代理主题。
代理主题角色(Friend):代 理主题角色内部含有对真实主题的引用,从而可以操作真实主题对象;代理主题角色负责在需要的时候创建真实主题对象;代理角色通常在将客户端调用传递到真实 主题之前或之后,都要执行一些其他的操作,而不是单纯地将调用传递给真实主题对象。例如这里的PreBuyProduct和PostBuyProduct 方法就是代理主题角色所执行的其他操作。
真实主题角色(RealBuyPerson):定义了代理角色所代表的真是对象。
附:在实际开发过程中,我们在客户端添加服务引用的时候,在客户程序中会添加一些额外的类,在客户端生成的类扮演着代理主题角色,我们客户端也是直接调用这些代理角色来访问远程服务提供的操作。这个是远程代理的一个典型例子。
先看一下C#的代理模式:
namespace 代理模式 { class Program { static void Main(string[] args) { SchoolGirl jiaojiao = new SchoolGirl(); jiaojiao.Name = "李娇娇"; Proxy daili = new Proxy(jiaojiao); daili.GiveDolls(); daili.GiveFlowers(); daili.GiveChocolate(); Console.Read(); } } //送礼物 interface GiveGift { void GiveDolls(); void GiveFlowers(); void GiveChocolate(); } class Proxy : GiveGift { Pursuit gg; public Proxy(SchoolGirl mm) { gg = new Pursuit(mm); } public void GiveDolls() { gg.GiveDolls(); } public void GiveFlowers() { gg.GiveFlowers(); } public void GiveChocolate() { gg.GiveChocolate(); } } class Pursuit : GiveGift { SchoolGirl mm; public Pursuit(SchoolGirl mm) { this.mm = mm; } public void GiveDolls() { Console.WriteLine(mm.Name + " 送你洋娃娃"); } public void GiveFlowers() { Console.WriteLine(mm.Name + " 送你鲜花"); } public void GiveChocolate() { Console.WriteLine(mm.Name + " 送你巧克力"); } } class SchoolGirl { private string name; public string Name { get { return name; } set { name = value; } } } }
js模拟高级语言的代理模式:
var SchoolGirl = function(name){ this.name = name; }; var Pursuit = function(mm){ this.mm = mm; }; Pursuit.prototype.giveDolls = function(){ alert(this.mm.name + '送你洋娃娃'); }; Pursuit.prototype.giveFlowers = function(){ alert(this.mm.name + '送你鲜花'); }; Pursuit.prototype.giveChocolate = function(){ alert(this.mm.name + '送你巧克力'); }; var Proxy = function(mm){ this.gg = new Pursuit(mm); }; Proxy.prototype.giveDolls = function(){ this.gg.giveDolls(); }; Proxy.prototype.giveFlowers = function(){ this.gg.giveFlowers(); }; Proxy.prototype.giveChocolate = function(){ this.gg.giveChocolate(); }; //调用: var jiaojiao = new SchoolGirl(); jiaojiao.name = "李娇娇"; var daili = new Proxy(jiaojiao); daili.giveDolls(); daili.giveFlowers(); daili.giveChocolate();
js语言特性的代理模式:
var Flower = function(){}; var Pursuit = { sendFlower:function(target){ var flower = new Flower(); target.receiveFlower(flower); } }; var Proxy = { sendFlower:function(target){ Pursuit.sendFlower(target); } }; var SchoolGirl = { receiveFlower:function(flower){ console.log('收到花' + flower); } }; //调用: Proxy.sendFlower(SchoolGirl);
js虚拟代理实现图片预加载:
var myImage = (function(){ var imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc:function(src){ imgNode.src = src; } }; })(); var proxyImage = (function(){ var img = new Image(); img.onload = function(){ myImage.setSrc(this.src); } return { setSrc:function(src){ myImage.setSrc('file:// /C:Users/svenzeng/Desktop/loading.gif'); img.src = src; } } })(); //调用: proxyImage.setSrc('http://imgcache.qq.com/music/photo/k/000GGDys0yAonk.jpg');
js缓存代理实现计算器:
//计算乘积 var mult = function(){ var a = 1; for(var i=0,l=arguments.length; i<l; i++){ a = a * arguments[i]; } return a; }; //计算加和 var plus = function(){ var a = 0; for(var i=0,l=arguments.length; i<l; i++){ a = a + arguments[i]; } return a; }; //创建缓存代理工厂 var createProxyFactory = function(fn){ var cache = {}; return function(){ var args = [].join.call(arguments,','); if(args in cache){ return cache[args]; } return cache[args] = fn.apply(this,arguments); } }; //调用: var proxyMult = createProxyFactory(mult), proxyPlus = createProxyFactory(plus); alert(proxyMult(1,2,3,4)); //24 alert(proxyMult(1,2,3,4)); //24 alert(proxyPlus(1,2,3,4)); //10 alert(proxyPlus(1,2,3,4)); //10
总结:代理模式包括许多小分类,在js开发中最常用的是虚拟代理和缓存代理。虽然代理模式非常有用,但我们在编写业务的时候,往往不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。