代理模式(Java实现)
代理模式属于经典的 GoF 设计模式,能够提供一个代表对象,让该对象来管理用户对目标对象的访问,这相当于把目标对象给控制住,不让用户直接访问该对象,而是要通过这个代表对象来访问。
从最为通用的形式来看,代理类会提供一个给用户使用的接口。同时,它还扮演包装器或代理人的角色,把真正向用户提供服务的那个对象包裹起来。于是,用户就需要通过代理类提供的接口来操作了,真正实现该接口的对象隐藏在代理类的后面,不被用户所见。
代理类在用户与真正的实现类之间充当中间人,让用户能够像直接访问那个类一样通过代理类方便地与之通信。
运用代理模式不仅可以控制用户对实际对象的访问操作,而且还让我们能够在用户访问那个对象的时候执行其他一些逻辑。
代理模式在 JDK 中就有所体现。最为明显的一处,就是在 java.lang.reflect 包中的 Proxy 类,这个包位于 java.base 模块里。Proxy 类提供了静态方法,让我们能够针对接口创建代理对象,把用户通过代理对象而执行的接口方法调用操作派发到指定的调用处理程序(InvocationHandler)上。
【实例】 VehicleProxy 代理类的实例能够像它所代理的真实车辆一样运作。

图 1 用 UML 类图描述代理类如何对实际车辆做代理
这样设计让我们能够在现有的 VehicleReal 之外继续添加其他一些车辆,并把那些车辆也交给代理类 VehicleProxy 去控制。
【实例 2】 VehicleProxy 代理类里有指向实际车辆的引用。
在需要替换实现代码或者需要通过中间方进行网络通信的场景中,代理模式让我们能够对相关的操作加以记录,并促使我们遵守接口隔离原则(ISP)以及其他几条 SOLID 原则。
如果应用程序需要执行 I/O 操作,而这些操作的具体执行方式需要经常切换,那么可以考虑运用代理模式。
从最为通用的形式来看,代理类会提供一个给用户使用的接口。同时,它还扮演包装器或代理人的角色,把真正向用户提供服务的那个对象包裹起来。于是,用户就需要通过代理类提供的接口来操作了,真正实现该接口的对象隐藏在代理类的后面,不被用户所见。
代理类在用户与真正的实现类之间充当中间人,让用户能够像直接访问那个类一样通过代理类方便地与之通信。
运用代理模式不仅可以控制用户对实际对象的访问操作,而且还让我们能够在用户访问那个对象的时候执行其他一些逻辑。
代理模式在 JDK 中就有所体现。最为明显的一处,就是在 java.lang.reflect 包中的 Proxy 类,这个包位于 java.base 模块里。Proxy 类提供了静态方法,让我们能够针对接口创建代理对象,把用户通过代理对象而执行的接口方法调用操作派发到指定的调用处理程序(InvocationHandler)上。
代理模式范例
我们以远程操控车辆为例。控制器(或者说,遥控器)本身是代理模式之中的代理类,它提供一套与目标车辆完全相同的功能,而且还管理着用户与真实的 Vehicle 实例(也就是目标车辆)之间的联系。【实例】 VehicleProxy 代理类的实例能够像它所代理的真实车辆一样运作。
public static void main(String[] args) { System.out.println("Pattern Proxy, remote vehicle controller"); Vehicle vehicle = new VehicleProxy(); vehicle.move(); vehicle.move(); }程序输出结果如下:
Pattern Proxy, remote vehicle controller
VehicleProxy, real vehicle connected
VehicleReal, move
VehicleReal, move

图 1 用 UML 类图描述代理类如何对实际车辆做代理
这样设计让我们能够在现有的 VehicleReal 之外继续添加其他一些车辆,并把那些车辆也交给代理类 VehicleProxy 去控制。
【实例 2】 VehicleProxy 代理类里有指向实际车辆的引用。
class VehicleProxy implements Vehicle { private Vehicle vehicleReal; @Override public void move() { if (vehicleReal == null) { System.out.println("VehicleProxy, real vehicle connected"); vehicleReal = new VehicleReal(); } vehicleReal.move(); } }
总结
从源代码的角度来看,代理模式有很多好处,例如,它让我们能够在程序运行期间替换某个接口的实现代码。这个模式不仅能够完全控制用户对实际对象的访问操作,而且还允许我们像实例 2 那样做惰性初始化。在需要替换实现代码或者需要通过中间方进行网络通信的场景中,代理模式让我们能够对相关的操作加以记录,并促使我们遵守接口隔离原则(ISP)以及其他几条 SOLID 原则。
如果应用程序需要执行 I/O 操作,而这些操作的具体执行方式需要经常切换,那么可以考虑运用代理模式。