代理模式(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();
}
}
优点:
- 附加操作:可以在访问对象时执行附加操作,如引用计数、缓存等。
- 增强功能:智能引用代理可以为真实对象提供额外的功能。
缺点:
- 增加代码复杂性:附加操作增加了代码的复杂性。
- 可能影响性能:附加操作可能带来一定的性能开销。
总结
代理模式 | 优点 | 缺点 |
---|---|---|
远程代理 | 隐藏远程调用的复杂性,提高系统的可扩展性 | 网络通信开销大,失败风险高 |
虚代理 | 延迟加载,节省资源和内存 | 代码复杂度增加,第一次访问性能开销 |
保护代理 | 控制访问,分离职责,确保安全性 | 增加代码复杂性,可能影响性能 |
智能引用 | 附加操作,增强功能 | 增加代码复杂性,可能影响性能 |
选择哪种实现方式应根据具体的需求和系统的复杂度来决定。如果需要控制远程访问,可以选择远程代理。如果需要延迟加载对象,可以选择虚代理。如果需要控制访问权限,可以选择保护代理。如果需要在访问对象时执行附加操作,可以选择智能引用代理。