一、 引入
日常生活中我们经常会经常见到中介、婚姻介绍所、代购等,作为中间代理人代办某项事务。在代理过程中会涉及到一件事情(目前由代理机构正在代办)、真正需要办理业务的一方和代理业务一方。例如,小王通过中介买房,这一过程涉及角色包括,具体主题对象(小王)、代理对象(中介)。我们要实现的动作是从房地中心买房,如何确保中介和小王都会进行买房的动作呢?为了保持具体对象和代理对象行为的一致性,我们引入抽象对象(接口),代理对象和真实主题对象均实现该接口。通过代理类中间层,有效控制了委托类对象(小王、小李等想要买房的人)对房产中心的直接访问,解耦了两个类的直接通信。又如有时程序中临时变量赋值,客户端调用web服务,电脑快捷方式作为应用程序的代理都是代理的实际应用,在日常业务中随处可见,具有重要作用。
二、定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
三、场景应用及实现
场景描述:男生B1追女生G,通过另一位男生B2传递情书、鲜花等动作,最终B2与G在一起。
分析:该场景中设计3类对象,分别为追求者B1,B2,被追求者G。实现的方法包括送情书、送鲜花等;
3.1 没有代理的代码
3.1.1 结构图
3.1.2 代码实现
class Girl { public string Name { get; set; } public int Age { get; set; } public Girl(string name,int age) { this.Name = name; this.Age = age; } } class Pursuite { Girl girl; public Pursuite(Girl _girl){ this.girl=_girl; } public void sendFlowers() { Console.Write(girl.Name+"give you flowers"); } public void sendDolls() { Console.Write(girl.Name + "give you dolls"); } public void sendChoclate() { Console.Write(girl.Name + "give you choclates"); } } class Program { static void Main(string[] args) { Girl girl = new Girl("Mary",18); Pursuite b1 = new Pursuite(girl);// 然而,B1与girl并不认识 b1.sendChoclate(); b1.sendDolls(); b1.sendFlowers(); Console.Read(); } }
上述代码实现中,B1与girl直接接触,并未通过“代理“B2进行追求。
3.2 优化——只有代理的代码
3.2.1 结构图
3.2.2 代码实现
class Proxy { Girl girl; public Proxy(Girl _girl) { this.girl = _girl; } public void sendFlowers() { Console.Write(girl.Name + "give you flowers"); } public void sendDolls() { Console.Write(girl.Name + "give you dolls"); } public void sendChoclate() { Console.Write(girl.Name + "give you choclates"); } } class Program { static void Main(string[] args) { Girl girl = new Girl("Mary",18); //Pursuite b1 = new Pursuite(girl);// 然而,B1与girl并不认识 Proxy b2 = new Proxy(girl); b2.sendChoclate(); b2.sendDolls(); b2.sendFlowers(); Console.Read(); } }
此时,代码中只有代理者B2和被追求者,要求代理的B1没有出现。场景中B1与B2均存在,且礼物虽然是B2送的,但是由B1买的,那么如何证明呢?
场景业务应该是:B1通过B2将礼物送给Girl。而追求者B1与Girl有相似的地方,B1送礼物,Girl接受礼物,实现的为同一个方法。因此,可以抽象为实现同一个接口。
3.3 优化——符合实际的代码
3.3.1 结构图
3.3.2 代码实现
class Girl { public string Name { get; set; } public int Age { get; set; } public Girl(string name,int age) { this.Name = name; this.Age = age; } } interface IGiveGift { void sendFlowers(); void sendDolls(); void sendChoclate(); } class Proxy:IGiveGift { Pursuite pursuite; public Proxy(Girl girl) { pursuite = new Pursuite(girl); } public void sendFlowers() { pursuite.sendFlowers(); } public void sendDolls() { pursuite.sendDolls(); } public void sendChoclate() { pursuite.sendChoclate(); } } class Pursuite:IGiveGift { Girl girl; public Pursuite(Girl _girl) { this.girl = _girl; } public void sendFlowers() { Console.Write(girl.Name + "give you flowers"); } public void sendDolls() { Console.Write(girl.Name + "give you dolls"); } public void sendChoclate() { Console.Write(girl.Name + "give you choclates"); } } class Program { static void Main(string[] args) { Girl girl = new Girl("小小",18); Proxy proxy = new Proxy(girl); proxy.sendChoclate(); proxy.sendDolls(); proxy.sendFlowers(); } }
此时,girl与代理proxy直接接触,但接受到的确实追求者B1送的礼物。
4、代理模式的结构
代理模式的核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层,代理模式结构如下图所示:
代理模式包含3个角色:
- 抽象角色,声明真实主题和代理主题的共同接口,通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,并调用目标对象。内部包含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候代替真实主体;控制真实主题的应用,负责在需要的时候创建主题对象(或创建真实主题对象)。代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯的将调用传递给真实主题对象。
- 真实角色:代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
5. 代理模式的优缺点
优点:(1)职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。(2)代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。(3)高扩展性。
缺点:由于在客户端和真实主题之间增加了一个代理对象,所以会造成请求的处理速度变慢实现代理类也需要额外的工作,从而增加了系统的实现复杂度,比如说远程代理。
注: 代理类和委托类必须实现同一个接口,因为代理真正调用的业务逻辑是在委托类中实现的。