定义
在《Head First 设计模式》一书中设计模式的定义如下:
代理模式为另一个对象提供一个替身或者占位符以控制对这个对象的访问
这是什么意思呢,也就是使用代理模式创建了一个对象的代表对象,让代表对象控制对这个对象的访问,被代理的对象可以是远程对象,创建开销大的对象,或者需要基于权限控制对象的访问。
1. 代理的对象是远程对象,比如说,你需要调用一个对象下的一个方法,而该对象是跑在远程的服务器上的,也就是在另外一个 JVM上面,这样就可以使用代理模式来代理对远程对象的访问,
2. 创建开销大的对象,比如加载配置文件等。
3. 基于权限控制对象的访问,比如一个论坛来说,不同权限的人可以看到的板块不同,普通游客的权限就看不了VIP权限的板块等。
此外,代理对象可以在调用真实对象的方法之前,之后可以进行相应的处理。
类图
下面是它的类图
可以看到代理模式一般有三个角色,
顶层接口:定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
真实主题:真正实现业务逻辑的类;
代理类:用来代理和封装真实主题;
例子
下面以网站上加载图片为例:
由于网络的原因,图片加载需要很长时间,而此时,在页面显示“图片正在加载中,请稍等...”,加载成功后,就显示图片:
首先定义顶层接口:
/** * 共同接口 * @author Administrator * */public interface Icon { /** * 获取图片的宽度 * @return */ int getWidth(); /** * 获取图片的高度 * @return */ int getHeight(); /** * 加载图片 */ void loadImage();}
代理对象:
/** * 代理对象,拥有真实对象的引用 * * @author Administrator * */public class IconProxy implements Icon { /** * 真实对象 */ private ImageIcon imageIcon; public IconProxy() { imageIcon = new ImageIcon(); } @Override public int getWidth() { System.out.println("正在获取宽度,请稍等..."); // 模拟真实对象的耗时操作 waitTime(3000); if (imageIcon != null) { int width = imageIcon.getWidth(); System.out.println("获取宽度成功,宽度为:" + width); return width; } return 0; } @Override public int getHeight() { System.out.println("正在获取高度,请稍等..."); // 模拟真实对象的耗时操作 waitTime(3000); if (imageIcon != null) { int height = imageIcon.getHeight(); System.out.println("获取宽度成功,宽度为:" + height); return height; } return 0; } @Override public void loadImage() { System.out.println("正在加载图片,请稍等..."); // 模拟真实对象的耗时操作 waitTime(3000); if (imageIcon != null) { imageIcon.loadImage(); } } void waitTime(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } }}
真实对象:
/** * 真实对象 * @author Administrator * */public class ImageIcon implements Icon{ @Override public int getWidth() { // 复杂计算 return 1000; } @Override public int getHeight() { // 复杂计算 return 500; } @Override public void loadImage() { System.out.println("加载成功"); }}
测试:
public static void main(String[] args) { Icon proxy = new IconProxy(); proxy.getWidth(); proxy.getHeight(); proxy.loadImage(); }
结果:
正在获取宽度,请稍等...获取宽度成功,宽度为:1000正在获取高度,请稍等...获取宽度成功,宽度为:500正在加载图片,请稍等...加载成功
上面这种方法,需要我们为每一个类写一个代理类,代理类是在运行之前就已经写好的,这种代理方式称之为静态代理,
而动态代理是代理类不需要我们编写,而是在运行的时候,动态生成的。
java的API中,可以使用 java.lang.reflect.InvocationHandler 类来实现动态代理。
下面用 java.lang.reflect.InvocationHandler 来改写上面的例子:
顶层接口还是不变:
/** * 共同接口 * @author Administrator * */public interface Icon { /** * 获取图片的宽度 * @return */ int getWidth(); /** * 获取图片的高度 * @return */ int getHeight(); /** * 加载图片 */ void loadImage();}
真实主题也不变:
/** * 真实对象 * @author Administrator * */public class ImageIcon implements Icon{ @Override public int getWidth() { // 复杂计算 return 1000; } @Override public int getHeight() { // 复杂计算 return 500; } @Override public void loadImage() { System.out.println("加载成功"); }}
之后,定一个类实现 java.lang.reflect.InvocationHandler 接口,该类是动态创建代理需要的。
public class IconInvocationHandler implements InvocationHandler { private Icon icon; public IconInvocationHandler(Icon icon) { this.icon = icon; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { return method.invoke(icon, args); } catch (InvocationTargetException e){ throw e.getCause(); } }}
之后就可以使用 java.lang.reflect.Proxy 来动态的创建我们的代理对象
Proxy.newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h)
测试:
public static void main(String[] args) { Icon imageIcon = new ImageIcon(); InvocationHandler handler = new IconInvocationHandler(imageIcon); Icon icon = (Icon) Proxy.newProxyInstance(imageIcon.getClass().getClassLoader(), imageIcon.getClass().getInterfaces(), handler); System.out.println("正在加载宽度,请稍等..."); waitTime(3000); int width = icon.getWidth(); System.out.println("加载成功,宽度为:" + width); System.out.println("正在加载高度,请稍等..."); waitTime(3000); int height = icon.getHeight(); System.out.println("加载成功,高度为:" + height); System.out.println("正在加载图片,请稍等..."); waitTime(3000); icon.loadImage(); } // 模拟耗时操作 static void waitTime(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } }
结果:
正在加载宽度,请稍等...加载成功,宽度为:1000正在加载高度,请稍等...加载成功,高度为:500正在加载图片,请稍等...加载成功
相关模式
策略模式:
模板方法模式:
单例模式:
适配器模式:
装饰者模式:
观察者模式:
状态模式: