通俗易懂设计模式(代理模式)

84 阅读5分钟

代理模式(Proxy Pattern)是一种结构型设计模式,它允许在访问对象时引入一个代理对象,以控制对原对象的访问。代理模式的主要目的是在访问对象时添加一些额外的处理逻辑,例如访问控制、性能优化、远程调用等。

代理模式的主要组成部分包括:

  1. 抽象主题(Subject):定义了一个接口,用于访问和管理真实主题对象。
  2. 真实主题(RealSubject):实现了抽象主题接口,并为内部状态提供了存储空间。
  3. 代理(Proxy):实现了抽象主题接口,并持有一个真实主题对象的引用。代理对象在访问真实主题对象之前或之后可以添加一些额外的处理逻辑。

代理模式的优点:

  1. 访问控制:代理模式可以在访问真实主题对象之前进行访问控制,确保只有满足特定条件的请求才能访问真实主题对象。
  2. 性能优化:代理模式可以在访问真实主题对象之前或之后添加一些额外的处理逻辑,例如缓存、懒加载等,从而提高系统性能。
  3. 远程调用:代理模式可以在访问真实主题对象之前或之后添加一些额外的处理逻辑,例如远程调用、序列化等,从而实现跨平台、跨语言的远程调用。

Java 实现代理模式的示例代码:

// 抽象主题
public interface Subject {
    void request();
}

// 真实主题
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: request");
    }
}

// 代理
public class Proxy implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        System.out.println("Proxy: before request");
        realSubject.request();
        System.out.println("Proxy: after request");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

在这个示例中,我们定义了一个抽象主题接口 Subject,它包含了一个 request() 方法。接着,我们定义了一个真实主题类 RealSubject,它实现了 Subject 接口,并为内部状态提供了存储空间。然后,我们定义了一个代理类 Proxy,它实现了 Subject 接口,并持有一个真实主题对象的引用。在代理类的 request() 方法中,我们在访问真实主题对象之前和之后添加了一些额外的处理逻辑。

在客户端代码中,我们创建了一个代理对象,并通过它访问真实主题对象的功能。通过这个代理对象,我们可以在访问真实主题对象之前和之后添加一些额外的处理逻辑,例如访问控制、性能优化、远程调用等。这样,我们就将代理对象的创建和管理过程封装在了代理类中,使得真实主题对象的访问变得更加灵活和可控。

场景举例

场景一:远程代理

在分布式系统中,远程代理是一种常见的代理模式应用。当一个对象需要在不同的地址空间(例如,在不同的机器上)被访问时,可以使用远程代理。远程代理作为客户端和被访问对象之间的中介,使得客户端可以像访问本地对象一样访问远程对象。

以Java RMI(Remote Method Invocation)为例,我们可以使用远程代理模式实现分布式系统中的对象访问。

首先,我们定义一个简单的远程接口HelloService

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloService extends Remote {
    String sayHello(String name) throws RemoteException;
}

接下来,我们实现这个接口,并将其导出为远程对象:

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
    public HelloServiceImpl() throws RemoteException {
    }

    @Override
    public String sayHello(String name) throws RemoteException {
        return "Hello, " + name;
    }

    public static void main(String[] args) {
        try {
            HelloServiceImpl helloService = new HelloServiceImpl();
            Registry registry = LocateRegistry.createRegistry(1099);
            registry.rebind("HelloService", helloService);
            System.out.println("Server started.");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

在客户端,我们使用Naming.lookup()方法查找远程对象,并通过代理调用其方法:

import java.rmi.Naming;

public class Client {
    public static void main(String[] args) {
        try {
            HelloService helloService = (HelloService) Naming.lookup("rmi://localhost:1099/HelloService");
            String result = helloService.sayHello("Client");
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们使用远程代理模式实现了客户端对远程对象的访问。客户端并不知道实际的对象是在本地还是远程,它只需要通过代理调用方法即可。

场景二:虚拟代理

虚拟代理是一种懒加载策略,它在需要的时候才创建被代理对象。这种策略可以减少系统资源的消耗,特别是在处理大量对象时。

以图片加载为例,我们可以使用虚拟代理模式实现图片的延迟加载。

首先,我们定义一个Image接口,用于表示图片:

public interface Image {
    void display();
}

接下来,我们实现一个简单的图片类RealImage,它会在构造函数中加载图片:

import java.io.FileInputStream;
import java.io.IOException;

public class RealImage implements Image {
    private String fileName;

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

    private void loadFromDisk() {
        System.out.println("Loading image from disk: " + fileName);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

然后,我们创建一个ProxyImage类,它实现了Image接口,并在需要时才创建实际的图片对象:

public class ProxyImage implements Image {
    private String fileName;
    private RealImage realImage;

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

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

在客户端,我们使用ProxyImage代理来加载和显示图片:

public class Client {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("image1.jpg");
        Image image2 = new ProxyImage("image2.jpg");

        image1.display(); // 加载并显示 image1.jpg
        image2.display(); // 加载并显示 image2.jpg

        image1.display(); // 直接显示 image1.jpg(已经加载过)
    }
}

在这个例子中,我们使用虚拟代理模式实现了图片的延迟加载。只有在实际需要显示图片时,才会加载图片文件。这样可以减少系统资源的消耗,特别是在处理大量图片时。