设计模式之代理模式

什么是代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

翻译过来就是:我想去做一件事情,但是我不能直接完成,或者如果我自己亲力亲为会非常耗费时间和精力,这个时候就需要中介(即代理)帮我完成完成这件事情。类比一下买车,你不能直接去汽车厂买车,直接去和汽车厂沟通费时费力,这个时候就需要一个4S店做中间商,去汽车厂帮你买车,我只需要负责选车,4S店就已经帮我做好了找车源,谈价格,办理执照等一系列工作,我只需要交钱就好了,这样就为客户省下了不少麻烦。


代理模式的结构

代理模式的主要角色如下。
抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

alt

类比刚刚的买车的实例:

  • 抽象主题——>买车这个概念
  • 真实主题——>买车的具体操作
  • 代理——>4S店
  • 客户——>买车的客户

4S店在帮客户买车这个操作的时候,肯定不可能用爱发电免费帮你买车,都是要恰饭的,那么这个时候代理就需要收取中介费,同时他需要和汽车厂和客户都签订合同之类的。那么在代理执行买车这个操作的时候,在买车前和买车后肯定需要执行他自己的一些操作。这就对应了图片中的preRequest()和postRequest()方法。

因为代理的职责是实现真实主题的功能,因此代理需要先继承Subject,翻译一下就是:4S店需要先可以实现买车这个操作才能去买车,不然就是诈骗行为。同时真实主题也需要继承抽象主题,可以理解为把买车这个概念性的操作具体化,就是抽象到具象。


代理的类型

我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。

静态代理

如上图所示,我们的客户在买车的时候,需要先new一个Proxy,然后Proxy执行从RealSubject里写好的Request方法,代理类重写Request方法,在调用RealSubject的Request之前调用preRequest方法,同理在这之后调用postRequest方法。也就是用户先new一个4S店,4S店的Request方法就是买车,在买车之前先要和汽车厂洽谈好,买车之后需要收取客户的中介费,这样,我们一个完整的买车操作就做好啦!

那么这个时候就有一个问题,我们同一个客户想要买很多车,那么这意味着我们需要new很多个4S店,我们得为每一个服务都得创建代理类,这样工作量大,并且非常占用资源,不易于管理。

这个时候,动态代理就出来了。

动态代理

在动态代理模式中,我们不需要手动创建Proxy,而是创建一个ProxyHandler,也就是动态处理器。我们只需要把我们的真实主题丢进去,就可以动态的创建代理类,并且动态的执行代理类的方法。


动态代理的实现(Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main.java.proxy.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyHandler implements InvocationHandler {

private Object object;

public ProxyHandler(final Object object) {
this.object = object;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----执行前的操作----");
Object result = method.invoke(object, args);
System.out.println("----执行后的操作----");
return result;
}
}

我们可以看到,动态代理类首先继承了InvocationHandler这个接口,实现这个接口我们需要重写invoke方法。在有参构造里我们将真实对象传参进入动态代理类,执行invoke(object, args),也就是通过反射机制,执行object里定义好的方法(动态代理有兴趣自己去了解),这样我们就实现了无论丢什么object我们都可以执行他里面定义好的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main.java.proxy.test;

import main.java.proxy.BuyHouse;
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.DynamicProxyHandler;

import java.lang.reflect.Proxy;

public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}

这是一个买房的例子,在里面我们首先调用静态类Proxy的newProxyInstance方法。里面有三个参数:

  • 一个类加载器(class loader)。也就是真实主题的类加载器。
  • 一个Class对象数组,每个元素都是需要实现的接口。
  • 一个调用处理器。也就是我们的的动态代理类。

总结

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。

至于动态代理的具体应用,等我彻底搞明白AOP和jdk再来填坑

文章目录
  1. 1. 什么是代理模式
  2. 2. 代理模式的结构
  3. 3. 代理的类型
    1. 3.1. 静态代理
    2. 3.2. 动态代理
  4. 4. 动态代理的实现(Java)
  5. 5. 总结
|