Bootstrap

java设计模式:03-07-代理模式

代理模式(Proxy Pattern)

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过创建一个代理对象来控制对实际对象的访问,通常用于在访问实际对象时增加额外的功能或进行额外的处理。

代理模式的应用场景

  • 远程代理(Remote Proxy):为一个对象在不同地址空间提供局部代表。远程代理可以隐藏一个对象存在于不同地址空间的事实。
  • 虚代理(Virtual Proxy):根据需要创建开销较大的对象。虚代理可以缓存一个真实对象的实例,并在需要时创建它。
  • 保护代理(Protection Proxy):控制对原始对象的访问。保护代理可以在访问真实对象之前进行权限检查。
  • 智能引用(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作,如计算一个对象被引用的次数。

代理模式的实现方式

1. 静态代理

思想:通过代理类实现与目标类相同的接口,并在代理类的方法中调用目标类的方法。

实现方式

// 抽象接口
interface Subject {
    void request();
}

// 真实对象
class RealSubject implements Subject {
    public void request() {
        System.out.println("RealSubject request");
    }
}

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

    public Proxy() {
        this.realSubject = new RealSubject();
    }

    public void request() {
        System.out.println("Proxy before request");
        realSubject.request();
        System.out.println("Proxy after request");
    }
}

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

优点

  • 控制访问:代理可以在调用真实对象前后进行额外处理,增加控制逻辑。
  • 分离职责:将真实对象的核心功能和代理对象的控制逻辑分离开来,符合单一职责原则。

缺点

  • 代码冗余:为每个真实对象都要创建一个代理类,导致代码量增加。
  • 灵活性差:静态代理需要为每个接口或类创建代理类,增加维护成本。
2. 动态代理

思想:在运行时创建代理对象,而不是在编译时创建。Java中的动态代理可以使用 java.lang.reflect.Proxy 类来实现。

实现方式

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象接口
interface Subject {
    void request();
}

// 真实对象
class RealSubject implements Subject {
    public void request() {
        System.out.println("RealSubject request");
    }
}

// 动态代理处理器
class ProxyHandler implements InvocationHandler {
    private Object realSubject;

    public ProxyHandler(Object realSubject) {
        this.realSubject = realSubject;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy before request");
        Object result = method.invoke(realSubject, args);
        System.out.println("Proxy after request");
        return result;
    }
}

// 客户端代码
public class DynamicProxyPattern {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(
            realSubject.getClass().getClassLoader(),
            realSubject.getClass().getInterfaces(),
            new ProxyHandler(realSubject)
        );
        proxy.request();
    }
}

优点

  • 减少代码量:不需要为每个真实对象都创建代理类,减少代码冗余。
  • 灵活性高:可以在运行时动态创建代理对象,增强系统的灵活性和可扩展性。

缺点

  • 性能开销:反射机制的使用会导致一定的性能开销,特别是在大量调用时。
  • 调试困难:由于代理对象是在运行时动态生成的,调试和排查问题较为困难。

总结

实现方式优点缺点
静态代理控制访问,分离职责代码冗余,灵活性差
动态代理减少代码量,灵活性高性能开销,调试困难

选择哪种实现方式应根据具体的需求和系统的复杂度来决定。如果系统中代理类数量较少,可以选择静态代理。如果需要动态生成代理对象并且系统的灵活性要求较高,可以选择动态代理。

应用场景实现

代理模式在不同的应用场景中可以采用不同的实现方式。下面分别实现远程代理、虚代理、保护代理和智能引用的例子,并说明各个方式的思想和优劣。

1. 远程代理 (Remote Proxy)

思想:远程代理用于隐藏一个对象存在于不同地址空间的事实。远程代理可以用于本地客户端与远程服务器之间的通信。

实现方式

// 抽象接口
interface Subject {
    void request();
}

// 远程服务接口(假设这是远程服务)
interface RemoteService {
    void remoteRequest();
}

// 远程服务实现(模拟远程服务)
class RemoteServiceImpl implements RemoteService {
    public void remoteRequest() {
        System.out.println("Remote service request");
    }
}

// 远程代理
class RemoteProxy implements Subject {
    private RemoteService remoteService;

    public RemoteProxy(RemoteService remoteService) {
        this.remoteService = remoteService;
    }

    public void request() {
        System.out.println("Proxy: forwarding request to remote service...");
        remoteService.remoteRequest();
    }
}

// 客户端代码
public class RemoteProxyPattern {
    public static void main(String[] args) {
        RemoteService remoteService = new RemoteServiceImpl();
        Subject proxy = new RemoteProxy(remoteService);
        proxy.request();
    }
}

优点

  • 隐藏复杂性:客户端不需要了解远程调用的细节,只需通过代理进行操作。
  • 提高系统的可扩展性:可以轻松地添加新的远程服务。

缺点

  • 性能开销:网络通信的开销可能较大,影响性能。
  • 失败风险:网络的不稳定性可能导致远程调用失败。

2. 虚代理 (Virtual Proxy)

思想:虚代理用于根据需要创建开销较大的对象。虚代理可以延迟对象的创建,节省资源。

实现方式

// 抽象接口
interface Image {
    void display();
}

// 真实对象
class RealImage implements Image {
    private String filename;

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

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

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

// 虚代理
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();
    }
}

// 客户端代码
public class VirtualProxyPattern {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.jpg");
        // Image will be loaded from disk
        image.display();
        // Image will not be loaded from disk
        image.display();
    }
}

优点

  • 延迟加载:延迟创建开销较大的对象,节省资源。
  • 节省内存:避免不必要的对象创建,节省内存。

缺点

  • 代码复杂度增加:引入代理模式后,代码变得更复杂。
  • 第一次访问性能开销:第一次访问时仍需创建真实对象,有一定的性能开销。

3. 保护代理 (Protection Proxy)

思想:保护代理用于控制对真实对象的访问。保护代理可以在访问真实对象之前进行权限检查。

实现方式

// 抽象接口
interface Document {
    void display();
}

// 真实对象
class RealDocument implements Document {
    private String content;

    public RealDocument(String content) {
        this.content = content;
    }

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

// 保护代理
class ProtectionProxy implements Document {
    private RealDocument realDocument;
    private String userRole;

    public ProtectionProxy(String userRole, String content) {
        this.userRole = userRole;
        this.realDocument = new RealDocument(content);
    }

    public void display() {
        if ("admin".equals(userRole)) {
            realDocument.display();
        } else {
            System.out.println("Access denied. Insufficient permissions.");
        }
    }
}

// 客户端代码
public class ProtectionProxyPattern {
    public static void main(String[] args) {
        Document document = new ProtectionProxy("admin", "Sensitive content");
        document.display();

        Document document2 = new ProtectionProxy("user", "Sensitive content");
        document2.display();
    }
}

优点

  • 控制访问:可以在访问真实对象之前进行权限检查,确保安全性。
  • 分离职责:将权限检查和核心功能分离开来,符合单一职责原则。

缺点

  • 增加代码复杂性:引入权限检查逻辑后,代码变得更复杂。
  • 可能影响性能:权限检查可能带来一定的性能开销。

4. 智能引用 (Smart Reference)

思想:智能引用在访问对象时执行一些附加操作,如计算对象被引用的次数或进行垃圾回收等。

实现方式

// 抽象接口
interface Subject {
    void request();
}

// 真实对象
class RealSubject implements Subject {
    public void request() {
        System.out.println("RealSubject request");
    }
}

// 智能引用代理
class SmartReferenceProxy implements Subject {
    private RealSubject realSubject;
    private int referenceCount;

    public SmartReferenceProxy() {
        this.realSubject = new RealSubject();
        this.referenceCount = 0;
    }

    public void request() {
        referenceCount++;
        System.out.println("Reference count: " + referenceCount);
        realSubject.request();
    }
}

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

优点

  • 附加操作:可以在访问对象时执行附加操作,如引用计数、缓存等。
  • 增强功能:智能引用代理可以为真实对象提供额外的功能。

缺点

  • 增加代码复杂性:附加操作增加了代码的复杂性。
  • 可能影响性能:附加操作可能带来一定的性能开销。

总结

代理模式优点缺点
远程代理隐藏远程调用的复杂性,提高系统的可扩展性网络通信开销大,失败风险高
虚代理延迟加载,节省资源和内存代码复杂度增加,第一次访问性能开销
保护代理控制访问,分离职责,确保安全性增加代码复杂性,可能影响性能
智能引用附加操作,增强功能增加代码复杂性,可能影响性能

选择哪种实现方式应根据具体的需求和系统的复杂度来决定。如果需要控制远程访问,可以选择远程代理。如果需要延迟加载对象,可以选择虚代理。如果需要控制访问权限,可以选择保护代理。如果需要在访问对象时执行附加操作,可以选择智能引用代理。

;