代理模式(Proxy Pattern):为其他对象提供代理以控制这个对象的访问。
代理模式结构:
Subject:接口,定义了 RealSubject和 Proxy 的共用接口,这样就可以在任何使用 RealSubject 的地方都可以使用 Proxy 。
RealSubject:定义Proxy所代表的实体。
Proxy:保存一个引用,使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。
关于Proxy的解释,也许有的读者并不是很理解这两句话是什么意思,看过资料后发现,很多人都是这样说,虽然没有错,但是能解释清楚点就更好了。所谓解释嘛,一方面能方便大家理解,另一方面也能提醒自己,哦,原来我是这样考虑的。
首先,保存一个引用,意思是说,在 Proxy 中我们会用 UML 中的关联关系,在 Proxy 类中引入另一个类 RealSubject 的对象,使得代理可以访问实体,意思是说 我们引入的对象可以访问 RealSubject 这个实体类,访问这个实体类什么,当然就是调用这个实体类的方法啦。
接着,并提供一个与 Subject 的接口相同的接口,意思是说,代理类 Proxy 和被代理类 RealSubject 都实现了接口Subject,并不是看到提供这个词就认为需要创建一个新接口什么的,这样代理就可以用来替代实体,意思是说,代理和实体都实现了接口中的方法,那么代理调用自身的方法时,方法达到的效果就如同被代理类在完成相应的操作,也就是被代理类的目的成功完成。通俗点讲,就是,我代表你,我做的事就是你要做的事,但实际我不是你,我只是代表你做了你想做的事。
理解了上面的结构关系之后,看代码的思路就很清晰了。我们来试试代码。
本文依旧用 Java 来完成代码的展示。
1、新建代理接口:
package controller; /** * Created by Administrator on 2017/8/3. */ public interface GiveGift { /** * 定义目标方法,也就是被代理类想完成的事 */ void giveFlowers(); void giveFood(); void giveRing(); }
2、定义一个目标类,在本例中表示追求目标:
package entity; import lombok.Getter; import lombok.Setter; /** * Created by Administrator on 2017/8/3. */ @Getter @Setter public class PursuitGoal { private String name; }
3、新建被代理类,本例中表示真命天子:
package real_subject; import controller.GiveGift; import entity.PursuitGoal; import lombok.AllArgsConstructor; /** * Created by Administrator on 2017/8/3. */ @AllArgsConstructor public class Pursuit implements GiveGift { private PursuitGoal pursuitGoal; public void giveFlowers() { System.out.println("送给"+pursuitGoal.getName() + "鲜花"); } public void giveFood() { System.out.println("送给" + pursuitGoal.getName() + "零食"); } public void giveRing() { System.out.println("送给"+ pursuitGoal.getName() + "戒指"); } }
4、新建代理类,本类中表示信使:
package proxy; import controller.GiveGift; import entity.PursuitGoal; import real_subject.Pursuit; /** * Created by Administrator on 2017/8/3. */ public class ProxyMan implements GiveGift { private Pursuit pursuit; public ProxyMan(PursuitGoal pursuitGoal) { pursuit = new Pursuit(pursuitGoal); } public void giveFlowers() { pursuit.giveFlowers(); } public void giveFood() { pursuit.giveFood(); } public void giveRing() { pursuit.giveRing(); } }
5、新建测试类,本例代表揭开谜底:
import entity.PursuitGoal; import org.junit.Test; import proxy.ProxyMan; /** * Created by Administrator on 2017/8/3. */ public class TestProxy { @Test public void testProxy(){ PursuitGoal pursuitGoal = new PursuitGoal(); pursuitGoal.setName("英语老师"); ProxyMan proxyMan = new ProxyMan(pursuitGoal); proxyMan.giveFood(); proxyMan.giveFlowers(); proxyMan.giveRing(); } }
哦,原来追求目标是英语老师。
代理模式总结:
①.优点:代理类可以为被代理的对象增加了一些扩展,给被代理类增加一些额外功能,只是功能的细节只需交给代理完成,我们只需要集中目标类功能的实现。便于以后的修改。代理模式在一定程度上降低被代理类和目标类之间的系统耦合度,将功能划分的更加清晰,利于后期维护。
②.不足:由于在客户端和真是主题之间增加了代理层,因此有些类型的代理模式可能会造成请求的速度变慢并且实现代理模式需要额外的工作,有的代理模式实现起来比较复杂
③.应用场景:
应用场景这部分总结引用自 http://www.cnblogs.com/Eason-S/p/5856141.html
当我们需要使用的对象很复杂或者需要很长时间去构造,这时就可以使用代理模式(Proxy)。例如:如果构建一个对象很耗费时间和计算机资源,代理模式(Proxy)允许我们控制这种情况,直到我们需要使用实际的对象。一个代理(Proxy)通常包含和将要使用的对象同样的方法,一旦开始使用这个对象,这些方法将通过代理(Proxy)传递给实际的对象。 一些可以使用代理模式(Proxy)的情况:
一个对象,比如一幅很大的图像,需要载入的时间很长。
一个需要很长时间才可以完成的计算结果,并且需要在它计算过程中显示中间结果
一个存在于远程计算机上的对象,需要通过网络载入这个远程对象则需要很长时间,特别是在网络传输高峰期。
一个对象只有有限的访问权限,代理模式(Proxy)可以验证用户的权限
代理模式(Proxy)也可以被用来区别一个对象实例的请求和实际的访问,例如:在程序初始化过程中可能建立多个对象,但并不都是马上使用,代理模式(Proxy)可以载入需要的真正的对象。这是一个需要载入和显示一幅很大的图像的程序,当程序启动时,就必须确定要显示的图像,但是实际的图像只能在完全载入后才可以显示!这时我们就可以使用代理模式(Proxy)。
使用代理模式来将由一系列无关逻辑组合在一起的代码进行解耦合,比如业务代码中的日志代码就可以在代理中进行。spring的AOP就是典型的动态代理应用。
好了,代理模式介绍到此结束。