设计模式解密(7)- 代理模式
1、简介
定义:为其他对象提供一种代理以控制对这个对象的访问 ;
分类:
- 静态代理(静态定义代理类,我们自己静态定义的代理类)
- 动态代理(通过程序动态生成代理类,该代理类不是我们自己定义的。而是由程序自动生成)-- 这是与静态代理主要的区别!!!
主要解决:在直接访问对象时带来的问题,直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层;
何时使用:想在访问一个类时做一些控制;
如何解决:增加中间层;
应用实例:JDK自带的动态代理,CGLIB,Spring AOP,....
英文:Proxy
类型:结构型模式
2、类图及组成
(引)类图
代理模式一般会有三个角色:
抽象角色:指代理角色(经纪人)和真实角色(明星)对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
将统一的流程控制都放到代理角色中处理!
3、静态代理
3-1、实例引入:电影明星通过经纪人实现日常活动
定义一个抽象角色:
package com.designpattern.proxy.staticproxy; /** * 定义一个抽象角色:电影明星 * @author Json */ public interface MovieStar { /** * 找电影合作 */ void discussCooperation(); /** * 签合同 */ void signContract(); /** * 出演电影角色 */ void playRoleInMovie(); /** * 收演出费 */ void collectMoney(); }
定义一个具体角色:
package com.designpattern.proxy.staticproxy; /** * 真实角色:电影明星 * @author Json */ public class RealMovieStar implements MovieStar { @Override public void discussCooperation() { System.out.println("真实角色:discussCooperation()"); } @Override public void signContract() { System.out.println("真实角色:signContract()"); } @Override public void playRoleInMovie() { System.out.println("真实角色:playRoleInMovie()"); } @Override public void collectMoney() { System.out.println("真实角色:collectMoney()"); } }
定义一个代理类:经纪人
package com.designpattern.proxy.staticproxy; /** * 代理角色:经纪人 * @author Json */ public class ProxyMovieStar implements MovieStar { /** * 真实对象的引用 */ private MovieStar star; public ProxyMovieStar(MovieStar star){ this.star = star; } @Override public void discussCooperation() { System.out.println("代理角色:discussCooperation()"); } @Override public void signContract() { System.out.println("代理角色:signContract()"); } @Override public void playRoleInMovie() { star.playRoleInMovie(); //这个需要真实角色实现的操作 } @Override public void collectMoney() { System.out.println("代理角色:collectMoney()"); } }
下面测试一下:
package com.designpattern.proxy.staticproxy; /** * 测试 * @author Json */ public class Test { public static void main(String[] args) { RealMovieStar real = new RealMovieStar(); MovieStar proxy = new ProxyMovieStar(real); proxy.discussCooperation(); proxy.signContract(); proxy.playRoleInMovie();//需要真实对象实现操作(出演电影) proxy.collectMoney(); } }
结果:
代理角色:discussCooperation() 代理角色:signContract() 真实角色:playRoleInMovie() 代理角色:collectMoney()
3-2、优缺点
优点:
业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1、代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2、代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理要是应用在程序规模稍大时就比较糟糕了。
4、动态代理
4-1、实例引入
抽象角色、真实角色与静态代理一致:
package com.designpattern.proxy.dynamicproxy; /** * 定义一个抽象角色:电影明星 * @author Json */ public interface MovieStar { /** * 找电影合作 */ void discussCooperation(); /** * 签合同 */ void signContract(); /** * 出演电影角色 */ void playRoleInMovie(); /** * 收演出费 */ void collectMoney(); }
package com.designpattern.proxy.dynamicproxy; /** * 真实角色:电影明星 * @author Json */ public class RealMovieStar implements MovieStar { @Override public void discussCooperation() { System.out.println("真实角色:discussCooperation()"); } @Override public void signContract() { System.out.println("真实角色:signContract()"); } @Override public void playRoleInMovie() { System.out.println("真实角色:playRoleInMovie()"); } @Override public void collectMoney() { System.out.println("真实角色:collectMoney()"); } }
代理角色变成了代理角色处理器:
package com.designpattern.proxy.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 代理角色的处理器 * @author Json */ public class ProxyMovieStarHandler implements InvocationHandler { private MovieStar moviestar; public ProxyMovieStarHandler(MovieStar moviestar){ super(); this.moviestar = moviestar; } /** * 所有的流程控制都在invoke方法中 * proxy:代理类 * method:正在调用的方法 * args:方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object object = null; System.out.println("调用之前的处理逻辑"); if (method.getName().equals("playRoleInMovie")) { object = method.invoke(moviestar, args);//激活调用的方法 } System.out.println("调用之后的处理逻辑"); return object; } }
测试:
package com.designpattern.proxy.dynamicproxy; import java.lang.reflect.Proxy; /** * 测试 * @author Json */ public class Test { public static void main(String[] args) { MovieStar star = new RealMovieStar(); //处理器 ProxyMovieStarHandler handler = new ProxyMovieStarHandler(star); //代理类 MovieStar proxyMovieStar = (MovieStar) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{MovieStar.class}, handler); //调用代理类的playRoleInMovie,实际调用的是真实角色的playRoleInMovie proxyMovieStar.playRoleInMovie(); } }
结果:
调用之前的处理逻辑 真实角色:playRoleInMovie() 调用之后的处理逻辑
4-2、优缺点
优点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke);而不用一个一个的写代理类,这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强;
缺点:
那就是只能代理基于接口的类,而无法代理没有接口的委托类。关于这一点,可以使用CGLib类库来做到代理无接口类。
5、使用场景
按职责来划分,通常有以下使用场景:
1、远程代理
2、虚拟代理
3、Copy-on-Write 代理
4、保护(Protect or Access)代理
5、Cache代理
6、防火墙(Firewall)代理
7、同步化(Synchronization)代理
8、智能引用(Smart Reference)代理
6、与其他模式的对比
1、和适配器模式的区别: 适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
适配器为它所适配的对象提供了一个不同的接口,代理提供了与它的实体相同的接口。
PS: 用于访问保护的代理可能会拒绝执行实体会执行的操作,因此,它的接口实际上可能只是实体接口的一个子集。
2、和装饰器模式的区别: 装饰器模式为了增强功能,而代理模式是为了加以控制。
7、总结
静态代理与动态代理,它们都能实现相同的功能,而我们看从静态代理到动态代理的这个过程,我们会发现其实动态代理只是对类做了进一步抽象和封装,使其复用性和易用性得到进一步提升;
PS:源码地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:原文地址 http://www.cnblogs.com/JsonShare/p/7161324.html