设计模式(十三)—— 代理模式
模式简介
为其他对象提供一种代理以控制对这个对象的访问。
在一些情况下,客户端不能或者不想直接引用一个对象,可以借助一个第三方代理来实现间接引用。代理对象在客户端和目标对象之间起到中介作用,通过代理对象,可以去掉客户不能看到的内容或服务,也可以添加客户需要的额外服务。
想象一下,商家想找明星艺人演出,要先找到经纪人,谈妥了相关事宜,经纪人通知相关艺人在适当的时间进行表演。这里的经纪人就相当于代理对象。在这个示例中,使用代理带来了哪些好处呢?
- 控制了目标对象的访问权限。不是任何人都能找到明星,明星没有那么多时间(保护代理)。
- 明星艺人在全世界各地表演,无法与商家见面,经济人作为代表讨论相关事宜(远程代理)。
- 艺人出场费用较高,开销太大,商家可以与经纪人先进行讨论,待演出时艺人再出场表演,降低成本(虚代理)
结构说明
角色说明
- Subject
抽象主题。定义RealSubject和Proxy的共同接口,以便在任何可以使用RealSubject的地方都能够使用Proxy。
- RealSubject
Proxy代理的目标实体。
- Proxy
代理类,包含一个指向RealSubject实体的引用。
源码结构
首先创建抽象主题Subject,它是一个抽象类,包含抽象方法Request。
abstract class Subject
{
public abstract void Request();
}
创建目标实体类RealSubject,继承自Subject类,Request方法。
class RealSubject : Subject
{
public override void Request()
{
Console.WriteLine("Request by real subject");
}
}
创建代理类Proxy,其中包含一个私有成员,引用RealSubject目标实体对象,以便将请求转发给真正的目标实体对象,注意这里Proxy也继承自Subject,那么在使用RealSubject的地方都能够使用Proxy。
class Proxy : Subject
{
private Subject _realSubject;
public override void Request()
{
if (_realSubject == null)
{
_realSubject = new RealSubject();
}
_realSubject.Request();
}
}
客户端调用,客户端不需要知道目标实体类,通过代理就可以完成相应的请求。
class Program
{
static void Main(string[] args)
{
Proxy proxy = new Proxy();
proxy.Request();
Console.ReadLine();
}
}
工作原理
客户端发送请求给代理,代理对象则在适当的时候向RealSubject转发请求。
示例分析
生活中一个非常简单的例子,例如互联网上面我们很多网站不能访问。通过代理类检查用户访问的网站是否被禁止,如果没有,正常访问;否则,返回Access Denied。
创建抽象类Internet,包含抽象方法Connect(这里也可以声明为接口)。
abstract class Internet
{
public abstract void Connect(string url);
}
分别创建RealInternet类以及代理类InternetProxy,RealInternet实现Connect方法,直接访问传入的地址。而代理类InternetProxy维护一个禁止访问的集合,在执行Connect方法时,首先判断传入的地址是否包含在该集合中,如果存在,返回Access Denied;否则调用RealInternet的Connect方法。
class RealInternet : Internet
{
public override void Connect(string url)
{
Console.WriteLine($"Connecting to {url}");
}
}
class InternetProxy : Internet
{
private Internet _realInternet = new RealInternet();
private readonly List<string> _bannedSites;
public InternetProxy()
{
_bannedSites = new List<string>
{
"abc.com",
"111.com"
};
}
public override void Connect(string url)
{
if (_bannedSites.Contains(url))
{
Console.WriteLine("Access Denied");
}
else
{
_realInternet.Connect(url);
}
}
}
客户端调用,通过代理访问aaa.com以及abc.com,输出结果如下:
class Program
{
static void Main(string[] args)
{
InternetProxy proxy = new InternetProxy();
proxy.Connect("aaa.com");
proxy.Connect("abc.com");
Console.ReadLine();
}
}
适用场景
根据代理模式的使用目的,常见的代理模式分为以下几种类型:
- 远程代理(Remote Proxy)
为一个对象在不同的地址空间提供局部代表。
- 虚代理(Virtual Proxy)
如果需要创建一个资源消耗较大的对象,可以先创建一个消耗较小的代理对象来表示,在适当时间创建真正的对象。
- 保护代理(Protection Proxy)
控制一个对象的访问,如上一节中的示例。
- 智能指引(Smart Reference)
当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来。
优缺点
优点
-
远程代理使得客户端可以访问远程机器上的对象。
-
虚代理可以减少系统资源的消耗。
-
保护代理可以控制客户端对真实对象的访问权限。
缺点
- 因为增加了代理对象,所以系统处理请求的速度会变慢。
代理模式与装饰者模式的区别
从结构上来看,代理模式和装饰者模式非常相似,Proxy和Decorator中都包含了对目标对象的引用,在使用时向目标对象发送请求。二者之间的差异主要在于它们的设计目的不同:Proxy主要强调客户端不能或者不想直接引用一个对象时,为这个对象提供一个替代品。 而Decorator更侧重于通过组合的方式,为目标对象增加一些额外的职责。 如《设计模式(十)—— 装饰者模式》一篇中讲到,装饰者模式可以在客户端通过多个装饰者以递归组合的方式对组件进行功能上的扩展,而代理模式更强调一种替代关系。