Proxy Design Pattern - tenji/ks GitHub Wiki

代理模式

代理模式是一种结构设计模式(structural design pattern),可让你为另一个对象提供替代品(substitute)或占位符(placeholder)。代理对象控制对原始对象的访问,允许你在请求到达原始对象之前或之后执行某些操作。

零、引申问题

阶段一:

项目中刚好遇到以下情况:你有一个巨大的对象,它消耗大量的系统资源。你有时需要它,但并非总是需要。

于是乎,我们可以用懒加载来解决这个问题:仅在实际需要时创建该对象。该对象的所有客户端都需要执行一些延迟的初始化代码。

阶段二:

但是又发现一个问题:这可能会导致大量代码重复

因此,在理想的情况下,我们将此代码直接放入对象的类中来解决代码重复的问题。

阶段三:

但是还有一个问题:该类可能是封闭的第三方库的一部分。

这个时候,使用 Proxy Pattern 可能是一个不错的选择。

一、结构

1.1 类图(静态结构)

proxy-pattern-class-structure

代理类 Proxy 组合的是接口 ServiceInterface 的实现类 Service

1.2 时序图(动态结构)

proxy-pattern-seq-structure

二、代码示例

proxy-pattern-demo-structure

Image 类(Subject)

// Subject
interface Image {
    void display();
}

RealImage 类(RealSubject)

// RealSubject
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }

    private void loadImageFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

ProxyImage 类(Proxy)

// Proxy
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

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

    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

Driver Program

// Client code
public class Client {
    public static void main(String[] args) {
        Image image = new ProxyImage("example.jpg");

        // Image will be loaded from disk only when display() is called
        image.display();

        // Image will not be loaded again, as it has been cached in the Proxy
        image.display();
    }
}

三、优点

  • 可以隐藏委托类(Service Object)的实现,你可以在客户不知情的情况下控制委托类(Control The Service Object);
  • 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理;
  • 符合开/闭原则。你可以引入新代理,而无需更改服务或客户端。

四、缺点

  • 复杂度增加:由于需要引入很多新类,代码可能会变得更加复杂;
  • 降低性能:服务的响应可能会延迟,因为中间多了代理类。

五、使用场景

  • 延迟初始化(虚拟代理)。这是当你拥有一个重量级服务对象时,即使您只是偶尔需要它,但该对象始终处于运行状态,从而浪费了系统资源。
  • 访问控制(保护代理)。
  • 远程服务的本地执行(远程代理)。
  • 记录请求(记录代理)。
  • 缓存请求结果(缓存代理)。当你需要缓存客户端请求的结果并管理此缓存的生命周期时,特别是当结果非常大时。

六、Proxy Pattern vs Decorator Pattern

相同点:

  • 两种模式都建立在组合原则之上,其中一个对象应该将一些工作委托给另一个对象。

不同点:

从 Proxy 和 Decorator 的类图结构来看,Proxy 依赖的是具体的实现类 Service;而 Decorator 依赖的是接口 ServiceInterface,也就是说客户在使用 Decorator Pattern 的时候,必须指定具体实现类,但是在使用 Proxy Pattern 的时候不需要。

GoF 认为 Proxy 和 Decorator 的区别在于 Proxy 对客户端进行了限制。Decorator 没有。Proxy 可以通过控制对功能的访问来限制客户端的操作;或者它可能通过执行客户不可见和未知的操作来限制客户所知道的内容。装饰器则相反:它以客户端可见的方式增强其委托的功能。

我们可以说 Proxy 是一个黑盒子,而 Decorator 是一个白盒子

更多关于 Decorator Pattern,Decorator Design Pattern

∞、参考链接