代理模式是GoF提出的23种设计模式中最为经典的模式之一,代理模式是对象的结构模式,它给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。简单的说,代理对象可以完成比原对象更多的职责,当需要为原对象添加横切关注功能时,就可以使用原对象的代理对象。我们在打开Office系列的Word文档时,如果文档中有插图,当文档刚加载时,文档中的插图都只是一个虚框占位符,等用户真正翻到某页要查看该图片时,才会真正加载这张图,这其实就是对代理模式的使用,代替真正图片的虚框就是一个虚拟代理;Hibernate的load方法也是返回一个虚拟代理对象,等用户真正需要访问对象的属性时,才向数据库发出SQL语句获得真实对象。代理模式的类图如下所示:
下面用一个找枪手代考的例子演示代理模式的使用:
/**
* 参考人员接口
*/
public interface Candidate {
/**
* 答题
*/
public void answerTheQuestions();
}
/**
* 懒学生
*/
public class LazyStudent implements Candidate {
private String name; // 姓名
public LazyStudent(String name) {
this.name = name;
}
@Override
public void answerTheQuestions() {
// 懒学生只能写出自己的名字不会答题
System.out.println(”姓名: ” + name);
}
}
/**
* 枪手
*/
public class Gunman implements Candidate {
private Candidate target; // 被代理对象
public Gunman(Candidate target) {
this.target = target;
}
@Override
public void answerTheQuestions() {
// 枪手要写上代考的学生的姓名
target.answerTheQuestions();
// 枪手要帮助懒学生答题并交卷
System.out.println(”奋笔疾书正确答案”);
System.out.println(”交卷”);
}
}
public class ProxyTest1 {
public static void main(String[] args) {
Candidate c = new Gunman(new LazyStudent(“王小二”));
c.answerTheQuestions();
}
}
优点
1. 职责清晰 真实的角色就是实现的业务逻辑,不用关心其他非本指责的事务
2. 高扩展性 具体主题角色不能怎么变化,只要它实现了接口,代理类完全可以在不做任何修改的情况下使用
3. 智能化 动态代理是最好的体现,即在运行阶段才指定实际代理对象(Struts如何把表单元素映射到对象上?)
使用场景 典型应用如Spring AOP面向切面编程技术的底层实现原理也是使用了动态代理模式。
扩展
1. 普通代理 要求客户端只能访问代理角色,而不能访问真实角色
2. 强制代理 要求客户端必须通过真实角色找到代理角色(演员和经纪人)
3. 虚拟代理 指在需要的时候,才初始化主题对象,可以避免被代理对象太多而引起的初始化缓慢问题
动态代理 指在实现阶段不用关心代理谁,而在运行阶段才指定代理哪个对象,动态代理根据被代理的接口生成所有的方法即给定一个接口,动态地理就宣传我已经实现该接口下的所有方法了(通过InvocationHandler接口)
类图:
源码
1. 接口
public interface IGamePlayer {
public void login(String user,String password);
public void killBoss();
public void upgrade();
}
2. 实现类
public class GamePlayer implements IGamePlayer {
private String name="";
public GamePlayer(String _name){
this.name=_name;
}
@Override
public void killBoss() {
System.out.println(this.name+"在打怪");
}
@Override
public void login(String user, String password) {
System.out.println("登录名为"+user+"的用户 "+this.name+"登录成功");
}
@Override
public void upgrade() {
System.out.println(this.name+"又升了一级");
}
}
3. 动态代理类
public class GamePlayIH implements InvocationHandler {
//被代理者
Class cls =null;
//被代理的实例
Object obj=null;
//我要代理谁
public GamePlayIH(Object _obj){
this.obj=_obj;
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result =method.invoke(this.obj, args);
if(method.getName().equalsIgnoreCase("login")){
System.out.println("有人登录");
}
else if(method.getName().equalsIgnoreCase("upgrade")){
System.out.println("有人升级了");
}
return result;
}
}
4. 场景类
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("XXX");
InvocationHandler handler = new GamePlayIH(player);
System.out.println("开始打游戏,记下时间戳 : "+System.currentTimeMillis());
ClassLoader c1=player.getClass().getClassLoader();
//动态生成一个代理类
IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(c1, new Class[]{IGamePlayer.class}, handler);
proxy.login("guojun", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是:"+System.currentTimeMillis());
}
}