设计模式(12/23) - 代理模式

112 阅读4分钟

代理模式

1 概述

  • 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过引入代理对象来间接访问目标对象,从而增强了对目标对象的控制。
  • 代理模式可以分为多种类型,例如静态代理、动态代理、远程代理、虚拟代理、保护代理等,具体类型取决于代理对象的用途。
  • 静态代理:工程师编辑代理类代码,实现代理模式;在编译期就生成了代理类。
  • 基于 JDK 实现动态代理:通过 jdk 提供的工具方法 Proxy.newProxyInstance 动态构建全新的代理类(继承 Proxy 类,并持有 InvocationHandler 接口引用)字节码文件并实例化对象返回。(jdk 动态代理是由 java 内部的反射机制来实例化代理对象,并代理的调用委托类方法)。

2 优缺点及应用场景

2.1 优点

  • 1)控制访问:代理模式可以在不修改目标对象的前提下,控制对目标对象的访问。
  • 2)增强功能:代理对象可以在调用目标对象之前或之后,添加额外的功能。
  • 3)延迟实例化:通过使用虚拟代理,可以在需要时才创建目标对象,实现延迟实例化。

2.2 缺点

  • 1)增加系统复杂性:代理模式引入了额外的代理对象,增加了系统的复杂性。
  • 2)可能影响性能:由于增加了一层间接访问,代理模式可能会影响系统的性能。

2.3 应用场景

  • 1)Android 的 Binder、AIDL 等。
  • 2)AOP:使用代理模式来实现面向切面编程。
  • 3)远程代理:为一个位于不同地址空间的对象提供局部代表,以控制对这个远程对象的访问。
  • 4)虚拟代理:根据需要创建开销很大的对象,通过代理延迟实例化。
  • 5)保护代理:控制对目标对象的访问,提供不同级别的使用权限。
  • 6)智能指引:在访问对象时执行一些附加操作,例如日志记录、性能统计等。

3 结构

  • 1)抽象主题(Subject):定义目标对象和代理对象的接口。
  • 2)真实主题(RealSubject):实现抽象主题的类,定义了代理对象所代表的真实对象。
  • 3)代理(Proxy):实现抽象主题的类,包含对真实主题实例的引用,并通过调用真实主题的方法来实现控制访问。

4 实现

4.1 UML 类图

代理模式.jpg

4.2 代码示例

4.2.1 静态代理代码示例
// 创建一个接口
interface Image {
  void display();
}

// 创建实现接口的实体类:真实图像类
class RealImage implements Image {
  private String fileName;

  public RealImage(String fileName) {
    this.fileName = fileName;
    loadFromDisk(fileName);
  }

  @Override
  public void display() {
    System.out.println("Displaying " + fileName);
  }

  private void loadFromDisk(String fileName) {
    System.out.println("Loading " + fileName);
  }
}

// 创建实现接口的实体类:代理图像类
class ProxyImage implements Image {
  private RealImage realImage;
  private String fileName;

  public ProxyImage(String fileName) {
    this.fileName = fileName;
  }

  @Override
  public void display() {
    if (realImage == null) {
      realImage = new RealImage(fileName);
    }
    realImage.display();
  }
}

// 使用示例
public class ProxyPatternDemo {
  public static void main(String[] args) {
    // 当被请求时,使用 ProxyImage 来获取 RealImage 类的对象
    Image image = new ProxyImage("test_10mb.jpg");

    // 图像将从磁盘加载
    image.display();
    System.out.println("");
    // 图像不需要从磁盘加载
    image.display();
  }
}
  • 执行程序,输出结果:
Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg
4.2.2 基于 JDK 动态代理代码示例
// 定义接口
interface Image {
  void display();
}

// 创建实现接口的实体类:真实图像类
class RealImage implements Image {
  private String fileName;

  public RealImage(String fileName) {
    this.fileName = fileName;
    loadFromDisk(fileName);
  }

  @Override
  public void display() {
    System.out.println("Displaying " + fileName);
  }

  private void loadFromDisk(String fileName) {
    System.out.println("Loading " + fileName);
  }
}

// 创建动态代理处理器
class ImageInvocationHandler implements InvocationHandler {
  private RealImage realImage;
  private String fileName;

  public ImageInvocationHandler(String fileName) {
    this.fileName = fileName;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (realImage == null) {
      realImage = new RealImage(fileName);
    }
    return method.invoke(realImage, args);
  }
}

// 使用示例
public class ProxyPatternDemo {
  public static void main(String[] args) {
    // 当被请求时,使用 Proxy 创建动态代理对象
    Image image = (Image) Proxy.newProxyInstance(
        Image.class.getClassLoader(),
        new Class[]{Image.class},
        new ImageInvocationHandler("test_10mb.jpg")
    );

    // 图像将从磁盘加载
    image.display();
    System.out.println("");
    // 图像不需要从磁盘加载
    image.display();
  }
}
  • 执行程序,输出结果:
Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

5 总结

  • 代理模式通过引入代理对象来控制对目标对象的访问,提供了一种间接访问目标对象的方式。代理模式可以增强对目标对象的控制,添加额外功能,并实现延迟实例化。尽管代理模式增加了系统的复杂性,但它在控制访问、增强功能和延迟实例化等方面具有重要的应用价值。在实际应用中,需要根据具体需求选择合适的代理类型,以充分发挥代理模式的优势。