Java设计模式(五)
--------------代理模式
前言
这里先举几个经典的例子:
1. Windows快捷方式
Windows快捷方式可以在使一个对象出现在系统的任何磁盘,而不修改原对象,对快捷方式调用与对原对象调用方式一致,这样快捷方式就形成了一个代理角色。
2.我们去中关村买电脑,一般我们都会找代理商去购买,很少有直接去找电脑生产厂家的(也因为不知道去哪里找,DELL的除外),一般在代理商那里我们不但能买到电脑还能得到不少优惠,这样一个过程就形成了一个代理模式。
在我们的面向对象的程序设计中,也存在着代理商这样的角色。下面就跟着这篇文章来看看代理模式的世界吧。。。。。。
代理模式
为什么要使用代理?
授权机制
不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.
不能直接操作到某个对象,但又必须和那个对象有所互动
举例两个具体情况:
(1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.
(2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.
总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.
角色组成
我们还延续电脑代理商的例子阐述一下,这个代理模式中角色的划分
我们先提一下我们要实现的代理模式的需求:我们要求代理商分为3个等级,提供的服务也不尽相同,但都包括销售电脑这一项,而电脑生产商只有一个,只负责把电脑交给代理商负责销售。
抽象主题角色
声明了真实主题和代理主题的共同接口,也就是主要提供的服务。
下面代码就是声明了一个电脑销售的服务
public interface AbstractSale {
public void saleComputer();
} |
代理主题角色
内部包含对真实主题的引用,并且提供与真实主体角色相同的接口。
很明显根据需求,代理商要区分等级,不同等级提供不同服务,那么在代理主体角色一定会增加权限判断和服务扩展(这两方面都是代理模式的重要应用)。
public class ComputerProxy implements AbstractSale {
private ComputerProductor cp = ComputerProductor.getInstance();
private int level;
public ComputerProxy(int level) { this.level = level; }
@Override public void saleComputer() { // TODO Auto-generated method stub if (this.level >= Constants.SECOND_LEVEL) { this.preSale(); } cp.saleComputer(); if (this.level == Constants.ONE_LEVEL) { this.afterService(); } }
private void preSale() { System.out.println("卖场 打折优惠活动!"); }
private void afterService() { System.out.println("代理商售后跟踪!"); } } |
真实主题角色
定义真是的对象,根据需求电脑生产商只有一个,所有代理源头都是同一生产商,所以我们使用单例模式来实现。
public class ComputerProductor implements AbstractSale {
private static ComputerProductor instance = null;
private ComputerProductor() {
}
public static synchronized ComputerProductor getInstance() { if (instance == null) { instance = new ComputerProductor(); } return instance;
}
@Override public void saleComputer() { // TODO Auto-generated method stub System.out.println("一台电脑卖出!"); }
} |
测试类 public class MyTest { public static void main(String[] args) { // TODO Auto-generated method stub AbstractSale sale = new ComputerProxy(Constants.ONE_LEVEL); sale.saleComputer(); AbstractSale sale2 = new ComputerProxy(Constants.SECOND_LEVEL); sale2.saleComputer(); } } 常量设置类 public interface Constants { public static final int ONE_LEVEL=3; public static final int SECOND_LEVEL=2; public static final int LOW_LEVEL=1; } |
动态代理模式
自从JDK1.3以后,JAVA语言通过java.lang.reflect库中,提供了三个类来直接支持代理模式。分别是:Proxy,InvocoationHandler和Method。那么我们首先就先来了解一下这3个类或接口。
JDK支持动态代理的类和接口
Proxy
提供用于创建动态代理类
重要方法:
newProxyInstance(ClassLoader classloader, class<?>[] interfaces
,InvocationHandler h);(整个代理类产生的入口)
参数表:
classloader: 真实主题角色的类加载器
interfaces: 代理类需要实现的接口列表
h: 代理主题角色的代理处理程序
InvocationHandler
关联的调用处理程序类。
重要方法:
public Object invoke(Object proxy, Method method, Object[] arg)
throws Throwable
参数表:
Object proxy :生成的代理类
Method method :包含当前代理的方法信息类
Object[] arg :代理方法的参数表数组
返回值:Object 代理方法的返回值,我们可以利用
object x= method.invoke(Object obj, Object...args);得到
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。在动态代理中一般都是抽象代理角色中定义的接口方法。
重要方法:
public Object invoke(Object obj, Object...args)
throws IllegalAccessException ,IllegalArgumentException,
参数表:
Object obj: 当前代理的真实代理类对象
Object...args: 代理方法的参数表
动态代理角色及实现
我们还延续上面电脑代理商的例子来定义我们具体的角色,我设想这样一个场景:我去组装新电脑的同时还想带一个CPU给我的“老爷车” 升级,那么按照原来的思路我们就要找两个代理商分别购置上面产品,那么我能不能找一个神通广大的全能代理商呢,我想要什么产品他都能给我代理到,这就是我们现在要介绍的动态代理了。
抽象主题角色
我们定义两个抽象主题角色,分别是购买电脑和购买CPU的
public interface AbstractSale { public String saleComputer(String bland); } public interface AbstractCPUSale { public void saleCPU(); } |
真实主题角色
两个厂商分别买整机和CPU
public class ComputerProductor implements AbstractSale { private static ComputerProductor instance = null; private ComputerProductor() { } public static synchronized ComputerProductor getInstance() { if (instance == null) { instance = new ComputerProductor(); } return instance; } @Override public String saleComputer(String bland) { // TODO Auto-generated method stub System.out.println("一台电脑卖出!"); return bland; } } |
public class InterProducter implements AbstractCPUSale { @Override public void saleCPU() { // TODO Auto-generated method stub System.out.println("卖出一个盒装INTER-CPU"); } } |
代理主题角色处理程序
定义具体完成代理工作的程序,我们可以在其中进行功能扩展,或根据不同的代理对象,做出不同的代理响应。
/** * 完成动态对象代理过程 * @author Benson */ public class ProxyHandler implements InvocationHandler { private Object real; // 传入的真实代理类 public ProxyHandler(Object real) { this.real = real; } @Override /* * arg0 代理类 (Proxy) arg1 代理的方法 arg2 代理方法的参数 */ public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable { if (this.real instanceof ComputerProductor) { String type = (String) arg2[0]; String x = ""; if (type.equals("联想") || type.equals("Dell")) { if (type.equals("联想")) { this.preSale(); this.afterService(); } else if (type.equals("Dell")) { this.afterService(); } x = (String) method.invoke(real, type); } else { System.out.println("我们没有代理该品牌电脑!"); } return x; } else if (this.real instanceof InterProducter) { method.invoke(real, arg2); } return null; } // 扩展方法 private void preSale() { System.out.println("卖场 打折优惠活动!"); } private void afterService() { System.out.println("代理商售后跟踪!"); } } |
代理主题角色
这里的代理主题角色是由Proxy类为我们自动生成的,当然我们也可定义一个该角色,负责封装这个 生成过程,在实际操作中由个人习惯而定。
我们在这里就把客户端调用类和代理主题的生成合并处理了
public class MyTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ComputerProductor cp = ComputerProductor.getInstance(); // 厂家(需要代理的类 ) ProxyHandler handler = new ProxyHandler(cp);
AbstractSale as = (AbstractSale) Proxy.newProxyInstance( ComputerProductor.class.getClassLoader(), ComputerProductor.class.getInterfaces(), handler); String bland = as.saleComputer("联想"); System.out.println("此产品 品牌是:" + bland);
InterProducter ip=new InterProducter(); handler = new ProxyHandler(ip); AbstractCPUSale ac = (AbstractCPUSale) Proxy.newProxyInstance( InterProducter.class.getClassLoader(), InterProducter.class .getInterfaces(), handler); ac.saleCPU(); } } |
通过这种方式我们就在运行根据真实主题角色动态生成代理类,从而实现了非常灵活的动态代理关系。
与普通代理的区别
动态代理和普通的代理模式的区别,就是动态代理中的代理类是由java.lang.reflect.Proxy类在运行期时根据接口定义,采用Java反 射功能动态生成的。和java.lang.reflect.InvocationHandler结合,可以加强现有类的方法实现。
总结
代理模式从宏观上理解就是:一个人或一个对象(代理主体角色)代替另一个人或者一个对象(真实主体角色)完成某项任务(抽象主体角色中定义的接口)。
代理模式跟适配器模式的区别:适配器改变了接口,代理没有。代理是为了对接口进行一个封装并且实现存取控制。适配器是为了适配原有接口,实现代码复用(目的不是控制);
所谓动态代理是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个动态代理其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。