设计模式原来如此-代理模式(Proxy Pattern)
代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有
1.抽象角色:声明真实对象和代理对象的共同接口
2.代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
3.真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
代理模式的优点
1.职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。
2.高扩展性
具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
3.智能化
通过动态代理,能够智能的代理委托角色。
代理模式分为静态代理和动态代理。
下面用个小例子说明一下静态代理,本例是个登录网络游戏,并打怪的例子。玩过网络游戏的都知道,打怪是很枯燥并且很累的过程,所以玩家就可以找代理(也就是游戏代练)来完成。
抽象主题角色,IGamePlayer
package com.zhouyu.proxy; public interface IGamePlayer { //登录游戏 public void login(String user,String password); //打怪 public void killBoss(); //升级 public void upgrade(); }
具体主题角色,GamePlayer
package com.zhouyu.proxy; public class GamePlayer implements IGamePlayer { private String name = ""; public GamePlayer(String name) { this.name = name; } @Override public void login(String user, String password) { System.out.println("登录名为:" + this.name + "登录成功"); } @Override public void killBoss() { System.out.println(this.name + "在打怪"); } @Override public void upgrade() { System.out.println(this.name + "又升了一级"); } }
Proxy代理主题角色,GamePlayerProxy
package com.zhouyu.proxy; public class GamePlayerProxy implements IGamePlayer { private IGamePlayer player; public GamePlayerProxy(IGamePlayer player) { this.player = player; } @Override public void login(String user, String password) { this.player.login(user,password); } @Override public void killBoss() { this.player.killBoss(); } @Override public void upgrade() { this.player.upgrade(); } }
客户端调用
package com.zhouyu.proxy; import java.util.Date; public class Client { public static void main(String[] args) { IGamePlayer player = new GamePlayer("张三"); IGamePlayer proxy = new GamePlayerProxy(player); System.out.println("开始游戏时间:" + new Date()); proxy.login("zhangsan","password"); proxy.killBoss(); proxy.upgrade(); System.out.println("结束游戏时间:" + new Date()); } }
通过使用静态代理,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
动态代理
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1)Interface InvocationHandler:该接口中仅定义了一个方法
public object invoke(Object obj,Methodmethod, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2)Proxy:该类即为动态代理类,其中主要包含以下内容
protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作
在使用动态代理类时,我们必须实现InvocationHandler接口
动态代理步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHandler h) 创建一个代理
4.通过代理调用方法
下面用个例子来完成动态代理,跟静态代理的例子一样,同样是网络游戏的例子,抽象主题角色IGamePlayer和具体主题角色GamePlayer,Proxy代理主题角色采用动态的。
package com.zhouyu.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Date; public class GamePlayerIH implements InvocationHandler { private Object object; public GamePlayerIH(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始游戏时间:" + new Date()); Object result = method.invoke(this.object,args); if(method.getName().equals("login")) { System.out.println("有人登录我的帐号"); } System.out.println("结束游戏时间:" + new Date()); return result; } }
客户端调用
package com.zhouyu.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { IGamePlayer player = new GamePlayer("张三"); InvocationHandler in = new GamePlayerIH(player); ClassLoader classLoader = player.getClass().getClassLoader(); IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(classLoader, new Class[]{IGamePlayer.class},in); proxy.login("zhangsan","password"); proxy.killBoss(); proxy.upgrade(); } }