定义与意图:
为某一对象提供一个代理和占位,以达到控制访问的目的。有三种不同的代理,远程代理,虚拟代理,保护代理。
类图:
对比:
主要是使用场景上容易和装饰器模式混淆。因为我们在代理类中为真正的被代理对象装饰了一定的功能,但从类图结构上,二者有较大的区分度。代理需要转发被代理对象的所有接口。而装饰器类没有这种约束。
在选择上,主要从意图上进行区分。如果目的是接管某一类型的所有操作,控制对其进行访问,那么应该使用代理模式,通常在编译时已经确定。而如果只是对某一类型的特定功能进行动态扩展,则使用装饰器模式。
示例代码
#include <iostream>
using namespace std;
class Subject
{
public:
void Request();
};
class RealSubject : Subject
{
public :
void Request()
{
cout<<"Called RealSubject.Request()"<<endl;
}
};
class Proxy : Subject
{
private :
RealSubject* _realSubject;
public: void Request()
{
// Use 'lazy initialization'
if (_realSubject == NULL)
{
_realSubject = new RealSubject();
}
_realSubject->Request();
}
};
void main()
{
// Create proxy and request a service
Proxy* proxy = new Proxy();
proxy->Request();
system("pause");
}
Java中的动态代理
从代理的类图来看,代理类要实现被代理类的所有接口,要表现的像一个被代理对象,即便只是想控制一两个接口,也需要实现所有其他接口,这样在实现代码时要比较繁琐。为此java提供了动态代理的实现方式。动态代理会拦截被代理类型上的所有的方法调用,这样可以根据调用名称过滤我们需要特殊处理的方法,其他的转发给原始对象处理即可。动态代理创建方法是Proxy.newInstance()方法,这个方法的第二个参数是要代理的类型的方法数组,第三个参数是代理类的类型。
示例代理:
public interface Subject {
public void doSomething1();
public void doSomething2();
}
class RealSubject implements Subject{
@Override
public void doSomething1() {
System.out.println("RealSubject doSomething1");
}
@Override
public void doSomething2() {
System.out.println("RealSubject doSomething2");
}
}
class LogProxy implements InvocationHandler{
private Object subject = null;
public LogProxy(Object object){
this.subject = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before call method:" + method.getName());
Object obj = method.invoke(this.subject, args);
System.out.println("after call method:" + method.getName());
return obj;
}
}
public class Client {
public static void main(String[] vargs) {
Subject subject = (Subject) Proxy.newProxyInstance(
RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(),
new LogProxy(new RealSubject()));
subject.doSomething1();
subject.doSomething2();
}
}
Java中许多框架都使用了动态代理的功能,所谓面向方面的编程AOP也依赖于动态代理进行实现。但动态代理的限制是只能代理实现了特定的接口的类型的对象。如果一个类型没有实现任何接口,那么就不能用动态代理了。为了解决这种限制,新的一些库如cglib等出现,其原理是在生成的java类的字节码中插入代码,以达到动态实现被代理对象的方法委托。由于cglib依赖于java虚拟机字节码的实现,这种字节码在后面的Android系统所使用的dalvik及art虚拟机并不兼容,也就无法在android中进行使用。
不过在android开源项目中也有bytecode的生成工具,如byte Buddy就是一个支持android通过字节码生成类的开源项目。利用此项目,就可以实现在特定的方法中插入代理,从而实现android上任意类型的动态代理。
参考文献: