Bootstrap

十二、结构型(代理模式)

代理模式(Proxy Pattern)

概念
代理模式(Proxy Pattern)是一种结构型设计模式,代理对象在不改变目标对象功能的前提下,通过控制对目标对象的访问,来实现延迟加载、权限控制、日志记录等功能。代理模式可以通过提供一个中介对象,来控制客户端和实际目标对象之间的交互。


应用场景

  1. 远程代理(Remote Proxy):为位于不同地址空间的对象提供代理,解决远程调用问题。典型的例子是RMI(Remote Method Invocation),通过代理对象进行远程方法调用。

  2. 虚拟代理(Virtual Proxy):为某些需要较高开销的对象提供代理,延迟其加载和创建。比如,大型图片或文档的加载可以通过虚拟代理来进行,只有当真正需要时才创建实际对象。

  3. 保护代理(Protection Proxy):用于控制对对象的访问权限。代理对象检查调用者是否具有访问权限,只有在权限允许时,才能访问目标对象。

  4. 智能引用代理(Smart Reference Proxy):在访问对象时加入一些附加操作,如统计实际对象的引用次数、记录日志、检查锁等。

  5. 缓存代理(Caching Proxy):代理可以缓存某些计算结果或操作结果,使得后续的相同请求不需要重复计算,提升系统性能。


注意点

  • 代理类和实际类必须实现相同的接口:代理类的设计应与目标类保持一致,使得客户端在使用代理类时与使用实际类没有差异。
  • 性能开销:尽管代理模式可以增加功能,但也可能增加系统开销,尤其是远程代理涉及到网络传输时。
  • 适合复杂的控制场景:代理模式尤其适合在需要控制访问、管理复杂操作、或添加额外处理逻辑的场景中使用。

核心要素

  1. Subject(抽象主题角色):定义代理类和目标类的共同接口,使得代理类和目标类可以被客户端通过相同的方式调用。
  2. RealSubject(真实主题角色):定义目标对象,实现真实的业务逻辑。
  3. Proxy(代理角色):负责控制对真实主题对象的访问,通常会引用一个真实主题对象,并通过实现抽象主题接口来代理其操作。

Java代码完整示例

代码示例:静态代理

// 抽象接口,定义真实主题和代理共同的行为
interface Subject {
    void request();
}

// 真实主题,实现实际的业务逻辑
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实主题执行请求");
    }
}

// 代理类,控制对真实主题的访问
class Proxy implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        // 代理可以在调用真实对象之前执行一些额外操作
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        System.out.println("代理:在调用真实对象前的额外操作");
        realSubject.request();
        System.out.println("代理:在调用真实对象后的额外操作");
    }
}

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

输出结果

代理:在调用真实对象前的额外操作
真实主题执行请求
代理:在调用真实对象后的额外操作

各种变形用法完整示例

  1. 虚拟代理(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("加载图片: " + fileName);
        }
    
        @Override
        public void display() {
            System.out.println("显示图片: " + 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 VirtualProxyClient {
        public static void main(String[] args) {
            Image image = new ProxyImage("test.jpg");
    
            // 图片首次显示时加载
            image.display();
            System.out.println("");
    
            // 第二次调用时不需要加载
            image.display();
        }
    }
    

    输出结果

    加载图片: test.jpg
    显示图片: test.jpg
    
    显示图片: test.jpg
    
  2. 动态代理(Dynamic Proxy)
    动态代理是在运行时创建代理类,而不是在编译时创建。在Java中,可以使用java.lang.reflect.Proxy类来实现动态代理,代理类可以动态代理接口定义的所有方法。

    代码示例

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 定义抽象接口
    interface Service {
        void operation();
    }
    
    // 实现接口的真实类
    class RealService implements Service {
        @Override
        public void operation() {
            System.out.println("执行实际操作");
        }
    }
    
    // 动态代理处理器
    class DynamicProxyHandler implements InvocationHandler {
        private Object target;
    
        public DynamicProxyHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("动态代理:在方法执行前");
            Object result = method.invoke(target, args);
            System.out.println("动态代理:在方法执行后");
            return result;
        }
    }
    
    // 客户端
    public class DynamicProxyClient {
        public static void main(String[] args) {
            RealService realService = new RealService();
            Service proxyInstance = (Service) Proxy.newProxyInstance(
                realService.getClass().getClassLoader(),
                realService.getClass().getInterfaces(),
                new DynamicProxyHandler(realService)
            );
    
            proxyInstance.operation();
        }
    }
    

    输出结果

    动态代理:在方法执行前
    执行实际操作
    动态代理:在方法执行后
    
  3. 保护代理(Protection Proxy)
    保护代理用于控制访问权限,可以通过代理来检查调用者是否具备权限,只有在通过检查时,才能调用目标对象的方法。

    代码示例

    interface BankAccount {
        void deposit(double amount);
        void withdraw(double amount);
    }
    
    class RealBankAccount implements BankAccount {
        private double balance;
    
        public RealBankAccount(double balance) {
            this.balance = balance;
        }
    
        @Override
        public void deposit(double amount) {
            balance += amount;
            System.out.println("存款成功,当前余额: " + balance);
        }
    
        @Override
        public void withdraw(double amount) {
            if (balance >= amount) {
                balance -= amount;
                System.out.println("取款成功,当前余额: " + balance);
            } else {
                System.out.println("余额不足,取款失败");
            }
        }
    }
    
    // 保护代理类
    class ProtectionProxy implements BankAccount {
        private RealBankAccount realBankAccount;
        private String userRole;
    
        public ProtectionProxy(String userRole, double initialBalance) {
            this.userRole = userRole;
            realBankAccount = new RealBankAccount(initialBalance);
        }
    
        @Override
        public void deposit(double amount) {
            realBankAccount.deposit(amount);
        }
    
        @Override
        public void withdraw(double amount) {
            if ("ADMIN".equals(userRole)) {
                realBankAccount.withdraw(amount);
            } else {
                System.out.println("无权限取款");
            }
        }
    }
    
    // 客户端
    public class ProtectionProxyClient {
        public static void main(String[] args) {
            BankAccount adminAccount = new ProtectionProxy("ADMIN", 1000);
            adminAccount.withdraw(500);
    
            BankAccount userAccount = new ProtectionProxy("USER", 1000);
            userAccount.withdraw(500);
        }
    }
    

    输出结果

    取款成功,当前余额: 500.0
    无权限取款
    

总结
代理模式通过引入代理类为目标对象提供额外的功能或控制。它适用于延迟加载、权限控制、日志记录、远程调用等场景。代理模式可以分为静态代理和动态代理,并且具有多种变体,如虚拟代理、保护代理、远程代理等。

;