代理模式(Proxy Pattern)
概念
代理模式(Proxy Pattern)是一种结构型设计模式,代理对象在不改变目标对象功能的前提下,通过控制对目标对象的访问,来实现延迟加载、权限控制、日志记录等功能。代理模式可以通过提供一个中介对象,来控制客户端和实际目标对象之间的交互。
应用场景
-
远程代理(Remote Proxy):为位于不同地址空间的对象提供代理,解决远程调用问题。典型的例子是RMI(Remote Method Invocation),通过代理对象进行远程方法调用。
-
虚拟代理(Virtual Proxy):为某些需要较高开销的对象提供代理,延迟其加载和创建。比如,大型图片或文档的加载可以通过虚拟代理来进行,只有当真正需要时才创建实际对象。
-
保护代理(Protection Proxy):用于控制对对象的访问权限。代理对象检查调用者是否具有访问权限,只有在权限允许时,才能访问目标对象。
-
智能引用代理(Smart Reference Proxy):在访问对象时加入一些附加操作,如统计实际对象的引用次数、记录日志、检查锁等。
-
缓存代理(Caching Proxy):代理可以缓存某些计算结果或操作结果,使得后续的相同请求不需要重复计算,提升系统性能。
注意点
- 代理类和实际类必须实现相同的接口:代理类的设计应与目标类保持一致,使得客户端在使用代理类时与使用实际类没有差异。
- 性能开销:尽管代理模式可以增加功能,但也可能增加系统开销,尤其是远程代理涉及到网络传输时。
- 适合复杂的控制场景:代理模式尤其适合在需要控制访问、管理复杂操作、或添加额外处理逻辑的场景中使用。
核心要素
- Subject(抽象主题角色):定义代理类和目标类的共同接口,使得代理类和目标类可以被客户端通过相同的方式调用。
- RealSubject(真实主题角色):定义目标对象,实现真实的业务逻辑。
- 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();
}
}
输出结果:
代理:在调用真实对象前的额外操作
真实主题执行请求
代理:在调用真实对象后的额外操作
各种变形用法完整示例
-
虚拟代理(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
-
动态代理(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(); } }
输出结果:
动态代理:在方法执行前 执行实际操作 动态代理:在方法执行后
-
保护代理(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 无权限取款
总结
代理模式通过引入代理类为目标对象提供额外的功能或控制。它适用于延迟加载、权限控制、日志记录、远程调用等场景。代理模式可以分为静态代理和动态代理,并且具有多种变体,如虚拟代理、保护代理、远程代理等。