代理在日常生活中很普遍,比如办身份证代理,产品销售代理,通信收费代理等,在计算机中也普遍存在,比如上网代理,远程调用代理,注册申请代理等。在代理过程中主要涉及的角色包括代理者,被代理者和事务,通过代理者和被代理者在是否参与事务,可以将代理分为职责代理和接口代理,所谓职责代理就是代理者完全行使被代理者的职责,被代理者不参与事务过程(至少在代理期间),比如代理总统,代理加工等,而接口代理就是被代理者和代理者都会参与事务过程,只是被代理者不直接面对用户,比如代理注册,代理收费,远程调用代理等。前者的实现不在本模式讨论范围之内,在程序设计中的代理模式都是指接口代理。下面是代理者模式的简图:
* | ̄ ̄ ̄ ̄ ̄ ̄|
* | Client |
* |___________| | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
* | | Subject |
* |----------->|-----------------------------|
* | Request() |
* |________________|
* △
* |
* | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
* | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| | ̄ ̄ ̄ ̄ ̄ ̄ ̄|
* | RealSubject | | Proxy |
* |----------------------------------- ---|←--RealSubject---------|----------------------| | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
* | Request() | | Request()○--|----→| RealSubject Request() |
* | | | | | |
* |_____________ ________| |____________| |____________________|
*
角色:用户,抽象对象,真实对象,代理对象。
从图上可以看出,用户维护一个对抽象对象的引用,而代理对象和真实对象都是抽象的子类,因此用户既可以使用真实对象又可以使用代理对象。代理对象维护一个真实对象的引用,而不是维护一个对抽象对象的引用,这是特别要注意的一个地方,因为装饰模式,合成模式中的装饰者,和合成者都是维护对抽象对象的引用。当然这种结构也使得代理模式不是很灵活,好处是职责专一。
从代理模式的简图可以看出,这里的代理都是接口代理,真实对象不直接与用户交互,这部分职责是由代理对象来完成的,这样做的好处就是在用户与真实对象之间多了一个代理层,这样就增加了设计的灵活性,因为我们可以在代理层做一些额外的工作,比如权限控制,约束检查等。
虽然代理模式也可以通过代理对象像装饰模式那样为真实对象增加职责,但代理模式的本意其实是为真实对象提供一种代理以控制对真实对象的访问,更多体现的是一种授权机制。这样做的好处是真实对象的职责可以更加单一,内聚性更好,比如对数据库访问对象增加一个数据库访问代理对象,谁可以访问数据库的权限控制就交由代理对象来检查,而数据库访问则只需要负责好自己的数据库访问职责即可。
代理模式的分类及应用场景:
A)远程代理:用户与真实对象不在同一空间,用户无法直接访问真实对象,这时需要在用户端建立一个代理对象来与真实对象进行交互,而用户通过代理对象来完成与真实对象的交互,实现了真实对象对用户的透明。RPC,WebService调用都是这种模式。
B)智能指针:也叫智能引用,用户使用代理指针替代真实指针,这样在代理指针中可以增加对真实指针进行管理的功能,比如访问计数,对象持续化加载,对象锁定等。典型的应用:类的属性访问机制,文件访问指针等。
C) 虚代理:真实对象的创建如果开销很大,需要很长时间,这时可以通过代理对象来先应付用户的访问,典型的应用就是网页上的图片占位符。QQ接收图片也是这种模式。之所以叫虚代理,是因为最终应用的完成是真实对象完成的,代理对象只是起到一种暂时顶替的作用。
D) 保护代理:这是代理模式在控制对象访问中的一种典型应用。
实现举例:
public class Proxy_Subject
{
public virtual void Request()
{
}
}
public class Proxy_RealSubject : Proxy_Subject
{
public override void Request()
{
System.Windows.Forms.MessageBox.Show("I want to read newspaper!");
}
public Proxy_RealSubject()
{
}
}
/// <summary>
/// Proxy 的摘要说明。
/// </summary>
public class Proxy_Proxy : Proxy_Subject
{
private Proxy_RealSubject realSubject;
private bool IsCanRead;
public Proxy_Proxy(bool bCanRead)
{
this.IsCanRead = bCanRead;
}
public override void Request()
{
if(this.IsCanRead)
{
realSubject = new Proxy_RealSubject();
realSubject.Request();
}
else
{
System.Windows.Forms.MessageBox.Show("You can't read newspaper!");
}
}
}
public class Proxy_Client
{
private Proxy_Subject subject;
public Proxy_Client(Proxy_Subject subject)
{
this.subject = subject;
}
public void DoSubject()
{
subject.Request();
}
}
public class Proxy_Test
{
public static void Test()
{
//使用代理进行
Proxy_Client client1= new Proxy_Client(new Proxy_Proxy(false));
client1.DoSubject();
//不使用代理
Proxy_Client client2= new Proxy_Client(new Proxy_RealSubject());
client2.DoSubject();
}
}
后记:前面模式中说过的 spring.net中的AoP编程使用了装饰模式,从模式来看其实也可以认为是代理模式,因为这两种模式都能为类增加附加的行为,需要从真实意义上来区别,有的时候其实很难区分这种实际的语义是装饰还是代理。但我觉得模式是用来到达设计目的的,只要能满足,是否是某种标准的模式,不在关键。