设计模式 - 代理模式(Proxy)

定义

为其他对象提供一个代理,以控制对这个对象的访问。

类型

结构性模式

使用场景

以下情况适用 Proxy 模式:

  1. 远程代理 (Remote Proxy): 为一个对象在不同的地址空间提供局部代表。例如在分布式对象调用的场景,使用一个本地对象代理一个远程地址空间中的远程对象,对本地对象的方法调用可以引起远程对象中的方法调用;
  2. 虚代理 (Virtual Proxy): 根据需要创建开销很大的对象;
  3. 保护代理 (Protection Proxy): 控制对原始对象的访问。保护代理用于对象应该有不同的访问权限时;
  4. 智能指引 (Smart Reference): 取代简单指针,在访问对象时增加了一些附加操作。典型用途包括:(1) 对指向实际引用的对象计数,这样当该对象没有引用时可以自动释放;(2) 当第一次引用一个持久对象时,将它装入内存;(3) 在访问一个持久对象前,检查它是否已经锁定,以确保其他对象不能改变它。

结构

代理模式结构

参与者

代理(Proxy):

  • 保存一个引用使得代理可以访问实体;若 RealSubjet和 Subject 接口相同,Proxy 会引用Subject;
  • 提供一个与 Subject 接口相同的接口,这样代理可以用它来代替实体;
  • 控制对实体的存取,并可能负责创建和删除它;
  • 其他功能依赖于代理的类型:
    • 远程代理(Remote Proxy)负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求;
    • 虚代理(Virtual Proxy)可以缓存实体的附加信息,以便延迟对它的访问;
    • 保护代理(Protection Proxy)检查调用者是否具实现一个请求所必须的访问权限;

抽象主题(Subject)

定义 RealSubjet 和 Proxy 的共用接口,这样就在任何使用 RealSubject 的地方可以使用 Proxy;

真实主题(RealSubject)

定义 Proxy 所代表的实体。

优点

  1. 协调调用者和被调用者,降低了系统的耦合度;
  2. 代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用。

缺点

  1. 由于在客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢;
  2. 实现代理模式需要额外的工作,从而增加了系统实现的复杂度。

常用代理模式实现

1. 静态代理(Static Proxy): 代理和被代理对象在代理之前都是确定的,他们都实现相同的接口或者继承相同的抽象类。

抽象主题(Subject)

public interface Subject {
    void execute();
}

真实主题(RealSubject)

public class RealSubject implements Subject {
    @Override
    public void execute() {
        System.out.println("RealSubject execute...");
    }
}

代理类(Proxy)

public class Proxy implements Subject {
    private Subject subject;

    public Proxy(Subject s) {
        this.subject = s;
    }

    @Override
    public void execute() {
        this.subject.execute();
    }
}

静态代理的优点是可以做到在不对目标对象进行修改的前提下对目标对象进行功能的扩展和拦截;缺点是因为代理对象需要实现与目标对象一样的接口,会导致代理类十分繁多不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。

2. 动态代理模式:

动态代理是指动态的在内存中构建代理对象(需要我们制定要代理的目标对象实现的接口类型)。在 Java 中,通过使用反射技术,一个代理类可以为任意类提供代理。代理类必须实现 InvocationHandler 接口,对代理实例调用方法时,将对方法调用进行编码并将其指派到它的代理程序的 invoke 方法。invoke 方法运用反射技术,通过 java.lang.reflect.Proxy 类中提供的静态方法 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序,也就是代理类实现的 InvocationHandler 接口中的 invoke 方法中。

抽象主题(Subject)

public interface Subject {
    void execute();
}

真实主题(RealSubject)

public class RealSubject implements Subject {
    @Override
    public void execute() {
        System.out.println("RealSubject execute...");
    }
}

代理类(Proxy)

public class CustomProxy implements InvocationHandler {
    private Object object;

    public CustomProxy(Object obj) {
        this.object = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy execute start");
        Object resultObject = method.invoke(object, args);
        System.out.println("proxy execute end");
        return resultObject;
    }
}

客户端(Client)

public class Client {
    public static void main(String[] args) {
        new Client().test();
    }

    private void test() {
        Subject subject = new RealSubject();
        CustomProxy customProxy = new CustomProxy(subject);
        subject = (Subject) Proxy.newProxyInstance(Client.class
            .getClassLoader(), subject.getClass().getInterfaces(), customProxy);
        subject.execute();
    }
}

参考代码

https://github.com/xueyufish/design-pattern-java/tree/master/proxy