代理模式在我们日常开发中也是一个很重要的设计模式。我们见到的代理可以分为两种,分别是静态代理和动态代理。
我们先来看看代理的概念吧,假如我们现在有两个类,分别为Client类和Subject 类,在实现某一业务逻辑时,Client需要与Subject进行交互。出于某些考量,我们不能让Client直接调用Subject提供的接口,这些因素可能包括但不限于:
- 控制Client对Subject的访问权限。
- Client的调用代码和Subject的实现代码均已写好,但是我们需要在Client对Subject的调用中加入一些处理逻辑或者参数日志等。
- Client对象根本无法访问Subject对象,需要中间架设一个代理桥梁。
当面临这些问题需要解决时,我们则需要考虑是否需要设计一套代码实现代理模式来解决问题。
通过前面的介绍我们知道,代理我们之前我们只关注两个类,Client和Subject之间的交互,引入代理模式后,首先我们需要增加一个代理类Proxy来取代原先的Subject类,作为Client直接的交互对象。为了让Client面向接口编程,我们可以把原先的Client与Subject之间涉及的交互抽象成一个接口类Subject。把原先的Subject重命名为RealSubject类并实现Subject接口。同时增加一个Proxy类与实现Subject接口。我们看一下下面的前后对比代码就会更加明白代理模式如何演化过来的了。
class Client{
public void doSomeThing() {
Subject sub = new Subject();
sub.doWork();
}
}
class Subject{
public void doWork() {
System.out.println("Subject doing work");
}
}
然后我们把静态代码模式引入进来。代码变成下面的样子
class Client{
public void doSomeThing() {
Subject sub = new Proxy();
sub.doWork();
}
}
interface Subject{
public void doWork() ;
}
class Proxy implements Subject{
@Override
public void doWork() {
RealSubject realSub = new RealSubject();
realSub.doWork();
}
}
class RealSubject implements Subject{
@Override
public void doWork() {
System.out.println("RealSubject doing work");
}
}
对于两种方式,主调代码都是一样的
public static void main(String[] args) {
Client client = new Client();
client.doSomeThing();
}
Java里面还有一种借助反射机制实现的动态代理。相信用过Spring的对于AOP应该是熟悉的,AOP说白了就是一个代理模式。使用动态代理方式。Java中提供了相应的基础类实现动态代理,分别是java.lang.reflect.InvocationHandler
和 java.lang.reflect.Proxy
具体代码如下:
class Client{
public void doSomeThing() {
//Subject sub = new StaticProxy();
//sub.doWork();
Subject proxy = (Subject) DynamicProxy.newInstance(new RealSubject());
proxy.doWork();
}
}
interface Subject{
public void doWork() ;
}
class DynamicProxy implements InvocationHandler{
private Object object;
private DynamicProxy() {}
public static Object newInstance(Object object) {
DynamicProxy proxy = new DynamicProxy();
proxy.object = object;
Object result = Proxy.newProxyInstance(proxy.getClass().getClassLoader(), object.getClass().getInterfaces(), proxy);
return result;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(object, args);
return null;
}
}
class RealSubject implements Subject{
@Override
public void doWork() {
System.out.println("RealSubject doing work");
}
}
动态代理需要实现InvocationHandler
接口,其中的方法调用在invoke方法中。method是实际执行的方法,args是method方法执行时带的参数。 method.invoke() 的第一个参数是方法所作用的对象。有点像JavaScript里面的apply 方法的使用。Proxy.newProxyInstance() 方法利用Java放射机制动态加载指定的动态代理对象。这里就是一个实现了Subject接口的代理对象。
下面我们来看看C++中的代理模式吧。因为C++没有提供反射机制,所以所说的代理模式就是静态代理了
class Subject{
public:
virtual void doWork() = 0;
virtual ~Subject(){}
};
class RealSubject :public Subject{
public:
void doWork(){
cout<< "RealSubject doing work\n" ;
}
};
class Proxy : public Subject{
public:
void doWork(){
RealSubject *realSub = new RealSubject();
realSub->doWork();
delete realSub;
}
};
class Client{
public:
void doSomeThing(){
Subject *sub = new Proxy();
sub->doWork();
delete sub;
}
};
主调代码如下
int main(int argc, const char * argv[]) {
Client *client = new Client();
client->doSomeThing();
delete client;
return 0;
}
输出结果:RealSubject doing work
然后看一下C语言中又该如何实现呢;C语言实现会有很多种灵活的方法。我们知道对于C++中的虚基类和Java中接口的概念,在C语言中就是结构体,然后结构体中只有函数指针,没有数据成员。
typedef struct _Subject{
void (*doWork)();
}Subject;
然后在C语言里面没有办法做函数覆盖,所以我们只需要为不同的对象指定不同的函数指针,如果我们要定义RealSubject
和 Proxy
我们可以像下面这样实现。
typedef struct _Subject{
void (*doWork)();
}Subject;
void doWork_realSub(){
printf("realSub doing work\n");
}
void doWork_proxy(){
printf("before invoke realSub doWork\n");
doWork_realSub();
printf("after invoke realSub doWork\n");
}
Subject * newRealSubject(){
Subject *realSub = (Subject *)malloc(sizeof(Subject));
realSub->doWork = doWork_realSub;
return realSub;
}
Subject *newProxy(){
Subject * proxy = (Subject *)malloc(sizeof(Subject));
proxy->doWork = doWork_proxy;
return proxy;
}
int main(int argc, const char * argv[]) {
Subject * sub = newProxy();
sub->doWork();
}
结果为:
before invoke realSub doWork
realSub doing work
after invoke realSub doWork
同样C语言是没有反射机制的。当然也就没有办法去动态的加载出一个对象。所以在C语言中谈论代理模式也是静态代理。