现在很多商品在全国各地都有代理经销商,这为我们的生活带来很大便利。例如,现在我们想要喝红酒,不必满大街去找红酒生产工厂,只要找到超市或者红酒厂家的代理经销商就行了。如果有你想买的红酒,直接就可以买到,如果没有,代理商可以从厂家进货,等货来了再通知你。下面,我们就以此为例,来看看代理模式是怎么实现的。
13.2模式定义
代理模式(ProxyPattern),是软件设计模式中的一项基本技巧,在代理模式中,有两个对象参与处理同一请求,接收的请求由代理对象委托给真实对象处理,代理对象控制请求的访问,它在客户端应用程序与真实目标对象之间起到了一个中介桥梁的作用。代理模式使用对象聚合代替继承,有效地降低了软件模块之间的耦合度。
在代理模式中,涉及的角色有以下几类:
2)真实角色:真正处理请求的目标对象。
3)代理角色:代理对象角色内部含有真实对象的引用,从而代理对象可以将请求转为真实对象处理。同时,代理对象在执行真实对象操作的前后还可以添加附加操作。
13.3模式实现
13.3.1创建红酒生产厂商
1.红酒工厂接口——IRedWine
首先,创建生产红酒接口“IRedWine”,该接口含有两个方法:生产红酒和销售红酒。
packagecom.demo.real;/***CreatedbyDanielon2018/3/21.*红酒接口*/publicinterfaceIRedWine{//生产红酒方法publicvoidproduct();//销售红酒方法publicvoidsell();}2.红酒工厂实现——RealRedWineFactory
packagecom.demo.real.impl;importcom.demo.real.IRedWine;/***CreatedbyDanielon2018/3/21.*真正的生产红酒工厂*/publicclassRealRedWineFactoryimplementsIRedWine{//生产红酒方法@Overridepublicvoidproduct(){System.out.println("红酒工厂生产红酒……");}//销售红酒方法@Overridepublicvoidsell(){System.out.println("红酒工厂销售红酒……");}}13.3.2到红酒工厂购买红酒
红酒工厂已经建立起来了,现在你可以直接到工厂买到想要的酒了。
packagecom.demo;importcom.demo.real.IRedWine;importcom.demo.real.impl.RealRedWineFactory;/***CreatedbyDanielon2018/3/21.*主应用程序*/publicclassClient{publicstaticvoidmain(String[]args){//创建红酒生产工厂IRedWineredWine=newRealRedWineFactory();//工厂生产红酒redWine.product();//工厂销售红酒redWine.sell();}}运行结果如下:
13.3.3创建红酒代理商
到红酒生产工厂购买红酒显然是不现实的做法,下面,我们创建红酒代理商类“RedWineProxy”,在该类中含有红酒工厂的对象引用,如果代理类能够解决(有客户想要买的酒),就直接交易,不能解决则交给真实对象红酒工厂处理。
packagecom.demo.proxy;importcom.demo.real.IRedWine;/***CreatedbyDanielon2018/3/21.*红酒代理商*/publicclassRedWineProxyimplementsIRedWine{//真正的红酒生产商privatefinalIRedWineredWine;//代理商出售红酒的权限privatefinalbooleanpermission=true;//默认构造方法publicRedWineProxy(IRedWineredWine){this.redWine=redWine;}//代理商生产红酒方法(代理商不生产红酒,从真正的生产工厂拿酒销售)@Overridepublicvoidproduct(){//判断代理商是否具有红酒代理权if(this.permission){//代理商有权卖红酒,是合法的System.out.println("[这是合法的红酒代理商]");System.out.println("代理商接到订单,通知工厂生产……");this.redWine.product();}else{System.out.println("[这是非法的红酒代理商!]");}}//代理商销售红酒方法@Overridepublicvoidsell(){if(this.permission){this.redWine.sell();System.out.println("代理商从工厂拿到批发价红酒,然后以代理价销售……");}else{System.out.println("[这是非法的红酒代理商!]");}}}可以看到,在红酒代理商中,构造方法将真实的红酒生产厂商对象实例引入。
13.3.4到红酒代理商处购买红酒
packagecom.demo;importcom.demo.proxy.RedWineProxy;importcom.demo.real.IRedWine;importcom.demo.real.impl.RealRedWineFactory;/***CreatedbyDanielon2018/3/21.*应用程序*/publicclassClient2{publicstaticvoidmain(String[]args){//创建真实的红酒工厂对象实例IRedWinerealRedWineFactory=newRealRedWineFactory();//获得代理对象实例IRedWineredWineProxy=newRedWineProxy(realRedWineFactory);//代理商生产红酒(其实真正生产的是工厂)redWineProxy.product();//代理商销售红酒(批发价拿货,然后以代理价出售)redWineProxy.sell();}}运行结果如下:
上面红酒代理商的例子只是一个简单的代理模式的应用。其实,代理模式的应用不止这么简单,还有动态代理和面向切面编程(AOP)的内容,而且应用得非常广泛。例如,目前非常流行的SpringMVC框架中就大量应用了AOP技术。
13.4设计原则
1.延迟加载,提高系统效率
13.5使用场合
1)远程代理(RemoteProxy)为一个对象在不同的地址空间提供局部代理。
2)虚拟代理(VirtualProxy)中,若一个对象的创建非常耗时,可通过代理对象去调用,在真实对象创建前,返回一个假的调用,等真实对象创建好了,这时返回给客户端的就是一个真实对象的相应方法调用。
3)保护代理(ProtectionProxy)控制对原始对象的访问。
4)智能指引(SmartReference)取代了简单的指针,它在访问对象时执行一些附加操作。
以上情况是代理模式的各种使用场合。代理模式的使用是非常普遍的,它在处理一些延迟加载和对原始对象的访问控制上显得非常有效。
1.扩展:JavaSDK中的代理模式
在我们的示例中,讲解的是简单的静态代理模式的应用,其实代理模式的应用非常广泛,在JDK中就为我们引入了动态代理技术。所谓动态代理,就是一个系统在没有统一接口的情况下,在运行时,动态地对那些接口不同的对象提供代理支持。动态代理的核心是java.lang.reflect.InvocationHandler接口,要使用动态代理就必须实现该接口。这个接口的委派任务是在invoke(Objectproxy,Methodm,Object[]args)方法里实现的。
JDK中的动态代理是在运行时生成代理对象,在使用java.lang.reflect.Proxy生成动态代理对象时必须提供一组代理对象需要实现的接口,以及实现java.lang.reflect.InvocationHandler接口的真实处理对象handler给它,然后,通过Proxy.newProxyInstance的调用,动态生成的代理对象就是实现了提供的这些接口,当然,可以将该动态代理对象作为其中的任意一种接口来使用。需要注意的是,这个生成的动态代理对象不会做实质性的工作,实际的工作是由真实处理对象handler来执行的。
2.动态代理实现
1)新建“ITarget”代理接口,其中含有一个operation接口方法。
packagecom.demo.dynamic;/***Createdbylsqon2018/3/25.*要代理的接口*/publicinterfaceITarget{//操作方法publicvoidoperation();}2)新建“TargetImpl”真实处理对象实现类,注意,因为是使用动态代理,所以不需要实现ITarget接口,而是实现java.lang.reflect.InvocationHandler接口。
packagecom.demo.dynamic;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;/***Createdbylsqon2018/3/25.*实际的处理类*/publicclassTargetImplimplementsInvocationHandler{//实现方法@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("---method:"+method.getName());System.out.println("--动态代理实现了!");returnnull;}}我们在实际的处理对象中,打印了代理方法名称和一些静态文字。
到目前为止,ITarget接口和TargetImpl实现类还没有任何关联,下面,我们就在客户端应用程序中动态代理ITarget接口。新建DynamicClient客户端应用程序如下:
importcom.demo.dynamic.ITarget;importcom.demo.dynamic.TargetImpl;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Proxy;/***Createdbylsqon2018/3/25.*动态代理客户端应用程序*/publicclassDynamicClient{publicstaticvoidmain(String[]args){//真实处理对象InvocationHandlerhandler=newTargetImpl();//创建代理类实例对象ITargettarget=(ITarget)Proxy.newProxyInstance(ITarget.class.getClassLoader(),newClass[]{ITarget.class},handler);//操作方法target.operation();}}在客户端应用程序中,我们首先创建了一个真实的处理对象handler,当然了,其类型是InvocationHandler。然后开始动态地创建代理对象,代理接口就是ITarget(生成的动态代理对象的类型就是ITarget类型),实际的处理对象则是handler。最后,我们调用动态代理对象的operation方法。运行程序,结果如下:
从结果可以看出,我们的动态代理已经生效了。没有一个具体的代理实现类,完全通过动态设置就可以达到动态代理的目的。