静态代理
随着业务规模的增大,为了方便管理两间工厂,小成和他的合伙人建立了一间公司,把一些不是很重要的生意交给手下业务员代表公司去和其他公司谈,如果业务员超常发挥,还可能为公司谈好一笔任务之外的生意。这样老板小成就可以轻松很多了,小成一有空就想写代码,一想这个不就是代理模式吗,然后就开始写下代码。
介绍
在有些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个代理来实现间接引用。就像我们现在的不能直接访问谷歌,要通过代理翻墙才行。
代理模式的定义就是为其他对象提供一种代理以控制对这个对象的访问。在代理模式中,代理者在代替被代理者执行操作时,还可以增加自己的操作。
代理模式中有几个重要的角色:
- 抽象对象父类(Subject):是真实对象类和代理类的共同父类,这样一来在任何使用真实对象类的地方都可以使用代理类。
- 真实对象类(RealSubject):定义了代理类所代表的真实对象类,真正执行操作的对象。
- 代理类(Proxy):代理类里面含有对真实对象类的引用,从而可以操作真实对象,而且代理类可以在将客户端调用真实对象的操作之前或之后,加上自己的一些操作,而不是单纯的将调用传递给真实主题对象,像下面的代码,公司代理额外谈好一单生意就是代理自己的操作。
package scut.designmodel.ProxyPattern;
//公司类
abstract class Company {
//公司名字
public String CompanyName;
//公司谈判
public abstract void Negotiate();
//公司签约
public abstract void Sign();
}
//小成的公司,真正的谈判对象,也就是被代理的对象
class XiaoChengCompany extends Company {
@Override
public void Negotiate() {
System.out.println(CompanyName +"正在与其他公司谈判");
}
@Override
public void Sign() {
System.out.println(CompanyName +"签好了合约");
}
}
//小成公司的代理
class XiaoChengCompanyProxy extends Company {
private XiaoChengCompany mXiaoChengCompany;
//代理建立的构造函数,建立一个公司实例,把公司名信息给它
public XiaoChengCompanyProxy(String CompanyName){
mXiaoChengCompany = new XiaoChengCompany();
mXiaoChengCompany.CompanyName = CompanyName;
}
@Override
public void Negotiate() {
mXiaoChengCompany.Negotiate();
ExtraBusiness();
}
@Override
public void Sign() {
mXiaoChengCompany.Sign();
}
//代理的额外动作
public void ExtraBusiness(){
System.out.println("代理额外谈好了一单生意");
}
}
//使用代理和进行其他公司进行谈判签约的过程
public class ProxyPattern {
public static void main(String[] arg){
XiaoChengCompanyProxy mXiaoChengCompanyProxy = new XiaoChengCompanyProxy("小成的公司");
mXiaoChengCompanyProxy.Negotiate();
mXiaoChengCompanyProxy.Sign();
}
}
运行结果:
小成的公司正在与其他公司谈判
代理额外谈好了一单生意
小成的公司签好了合约
应用场景
- 当需要为一个对象再不同的地址空间提供局部的代表时。远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 当需要创建开销非常大的对象时。 虚拟代理可以通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
- 当需要控制对原始对象的访问时。保护代理可以控制对真实对象的使用权限。
- 当需要在访问对象时知想一些附加操作时。比如通过代理对象计算访问实际对象的次数。
优点
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
- 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
- 保护代理可以控制对真实对象的使用权限。
缺点
- 静态代理类和委托类实现了相同的接口,静态代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 静态代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
动态代理
小成的公司虽然刚刚成立,但是还是成立了各种部门分管事务。有一些部门很缺人,例如产品部和广告部就需要人手,然后就会委托人事部去招人,人事部就是他们的代理。小成一开始想用静态代理模式来实现这样的代码,但是静态代理对象只服务于一个部门,如果要代理另外一个部门又要重新写,那为什么不写一个可以代理所有部门的代理呢?于是小成就从网上找到了动态代理模式。
介绍
动态代理模式解决了静态代理的要手动实现代理的缺点,只需要把代理类实现InvocationHandler接口,就可以通过基于java反射机制的method.invoke方法来自动调用被代理类的方法,不用手动实现。
动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现。我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实对象类(RealSubject)相同的接口,而是把这种实现推迟到运行时。
可以看下面的代码:
动态代理类:
为了能让PersonnelDepartment类能够在运行时才去实现真实对象类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口。
package scut.designmodel.DynamicProxyPattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class PersonnelDepartment implements InvocationHandler {
// 代理对象
private Object ProxyObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object ProxyObject){
this.ProxyObject =ProxyObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
ProxyObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
//InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("人事部准备场地");
Object result=null;
switch (method.getName()) {
case "Negotiate":
try {
/*原对象方法调用前代理的动作*/
System.out.println("人事部邀请应聘者面试谈判");
//运用java反射机制调用目标方法
result = method.invoke(ProxyObject, args);
/*原对象方法调用后代理的动作*/
System.out.println("谈判成功,人事部制定合同");
} catch (Exception e) {
e.printStackTrace();
System.out.println("谈判失败");
throw e;
}
break;
case "Sign":
try {
//原对象方法调用前代理的动作
System.out.println("人事部邀请应聘者面试签约");
//运用java反射机制调用目标方法
result = method.invoke(ProxyObject, args);
//原对象方法调用后代理的动作
System.out.println("签约成功,人事部安排新同事上班");
} catch (Exception e) {
e.printStackTrace();
System.out.println("签约失败");
throw e;
}
break;
}
System.out.println("----------------");
return result;
}
}
实际类:
package scut.designmodel.DynamicProxyPattern;
//部门类
interface Department {
//部门谈判
public void Negotiate();
//部门签约
public void Sign();
}
//广告部门,真正的谈判对象,也就是被代理的对象
class AdvertisingDepartment implements Department {
@Override
public void Negotiate() {
System.out.println("广告部正在和应聘者谈判");
}
@Override
public void Sign() {
System.out.println("广告部和应聘者签好了合约");
}
}
//产品部门,真正的谈判对象,也就是被代理的对象
class ProductDepartment implements Department {
@Override
public void Negotiate() {
System.out.println("产品部正在和应聘者谈判");
}
@Override
public void Sign() {
System.out.println("产品部和应聘者签好了合约");
}
}
//人事部动态代理两个部门的招聘工作流程
public class DynamicProxyPattern {
public static void main(String[] arg){
PersonnelDepartment personnelDepartment = new PersonnelDepartment();
Department mAdvertisingDepartment = (Department) personnelDepartment.newProxyInstance(new AdvertisingDepartment());
mAdvertisingDepartment.Negotiate();
mAdvertisingDepartment.Sign();
Department mProductDepartment = (Department) personnelDepartment.newProxyInstance(new ProductDepartment());
mProductDepartment.Negotiate();
mProductDepartment.Sign();
}
}
结果:
人事部准备场地
人事部邀请应聘者面试谈判
广告部正在和应聘者谈判
谈判成功,人事部制定合同
1.
----------------
人事部准备场地
人事部邀请应聘者面试签约
广告部和应聘者签好了合约
签约成功,人事部安排新同事上班
----------------
人事部准备场地
人事部邀请应聘者面试谈判
产品部正在和应聘者谈判
谈判成功,人事部制定合同
----------------
人事部准备场地
人事部邀请应聘者面试签约
产品部和应聘者签好了合约
签约成功,人事部安排新同事上班
----------------
应用场合
动态代理模式的应用场景其实和静态代理的差不多,只是如果静态代理要代理的对象比较多的时候,就建议使用动态代理,可以减少很多重复的代码。
优点
- 只需要一个动态代理类就可以解决创建多个静态代理的麻烦,避免不断的重复多余的代码。
- 调用目标代码时,会在方法“运行时”才动态调用真实类对象,不需要事先实例化,更加灵活多变。
缺点
- 相比静态代理的直接调用被代理类对象,动态代理要通过反射,这样的话效率降低了一点。
- JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,不过可以通过cglib来实现针对类的代理。
参考
- http://blog.csdn.net/hejingyuan6/article/details/36203505#t2
- 《设计模式其实很简单》,刘径舟,张玉华等编著——清华大学出版社,2013.7