在软件系统中,有些对象有时候由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),如果直接访问会给使用者或者系统结构带来很多麻烦,这时可以在客户程序和目标对象之间增加一层中间层,这个中间层就是代理。
代理模式的定义:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式可分为以下几类:
(1)远程代理:为一个位于不同的地址空间的对象提供一个局域代表对象。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。
(2)保护代理:控制对一个对象的访问,如果需要可以给不同的用户提供不同级别的使用权限。好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
(3)虚拟代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。好处是代理对象可以在必要的时候才将被代理的对象加载。
(4)智能引用代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
这里给出两个使用该模式的例子:
应用一:虚拟代理
例如:word文档打开
Word文档通常会含有链接、图片、表格等对象,但是并不是每次刚打开word时都要创建和实例化这些对象,特别是实例化图片对象很消耗资源。事实上,我们没必要实例化所有图片,当我们在查看word时,每次只是看到其中的一部分,所以没有必要实例化所有资源,可以使用一个虚代理物件,代替图片被载入,来加快打开文档速度,当我们看下一页时再载入图片也不迟。
类图如图所示:
如上图所示,当文档被开启时, ProxyImage代理代替RealImage物件被载入,在还没卷动至图片显示处时,也就是还没有调用 ProxyImage的Draw()时,图片并不会被载入,因而可以加速文档的开启;如果需要显示图片了, ProxyImage的 Draw()会被调用,而这时才真正创建RealImage物件,以从硬盘中载入图片。
主要类代码:
public interface Image //Image公共接口 { public abstract void Draw(); } //ProxyImage类实现Image接口 public class ProxyImage implements Image { private RealImage realimage; public void Draw() { If(realimage==NULL) { Realimage=new RealImage(); } realimage.Draw(); } } //RealImage类实现Image接口 public class RealImage implements Image { public void Draw() { Console.WriteLine(“载入图片……”);} } 主程序 public class App { public static void Main() { ProxyImage proxy = new ProxyImage(); proxy.Draw(); } }
应用二:远程访问
例子:远程数学运算访问
本地客户程序需要调用远程服务器提供的数学运算服务,也就是说数学运算服务和客户程序不在同一个地址空间之内,我们现在要面对的是跨越Internet这样一个网络障碍:这时候调用数学运算服务就没有下面那么简单了,因为我们更多的还要去考虑网络的问题,对接收到的结果解包等一系列操作。为了避免由于网络等障碍引起的复杂性,引用Proxy模式,用一个本地的代理来代替远程数学运算类打点一切,即为我们的系统引入了一层间接层,示意图如下
设计的类图如下:
如上图所示:我们在ProxMath中对实现数据类的访问,让ProxyMath来代替网络上的RealMath类,这样我们看到ProxMathy就好像是本地RealMath类,它与客户程序处在了同一地址空间内。
主要代码:
public interface Math //Math公共接口 { public abstract double Add(double x,double y); public abstract double Sub(double x,double y); public abstract double Mul(double x,double y); public abstract double Dev(double x,double y); } public class ProxyMath implements Math //Proxy类,继承于Math { private RealMath realmath = new RealMath(); //Proxy类中不光有调用Math类的方法,还包含一些网络通信,与远程服务器交换数据,此处省略。 public double Add(double x,double y) { return math.Add(x,y); } public double Sub(double x,double y) { return realmath.Sub(x,y); } public double Mul(double x,double y) { return realmath.Mul(x,y); } public double Dev(double x,double y) { return realmath.Dev(x,y); } } public class RealMath implements Math { public double Add(double x,double y) { return x + y; } public double Sub(double x,double y) { return x - y; } public double Mul(double x,double y) { return x * y; } public double Dev(double x,double y) { return x / y; } } 测试主程序 public class App { public static void Main() { ProxyMath proxy = new ProxyMath(); double addresult = proxy.Add(2,3); double subresult = proxy.Sub(2,3); double mulresult = proxy.Mul(2,3); double devresult = proxy.Dev(2,3); } }
从上面的两个例子可以概括出Proxy模式的工作原理:
首先,代理并不改变主题的接口,因为模式的用意是不让客户端感觉到代理的存在;其次,代理使用委派将客户端的调用委派给真实的主题对象,换言之,代理起到的是一个传递请求的作用;第三,代理在传递请求之前和之后都可以执行特定的操作(如网络通信、检查对象是否存在等),而不是单纯传递请求。