引言
在现实生活中,我们经常使用代理来处理我们不想直接参与或无法直接参与的事务,例如,使用律师来代表法庭上的案件。在软件开发中,这种代理概念同样存在,被称为代理模式。代理模式允许我们通过创建一个替代对象(代理),来控制对其他对象的访问。这种模式在需要控制或增强对对象的访问时非常有用。
代理模式简介
定义与用途
代理模式(Proxy Pattern)是一种结构型设计模式,它通过提供一个替代对象(代理)来代表真实对象的操作。代理控制对原始对象的访问,可以在访问对象时添加一些附加操作,例如访问控制、延迟初始化、日志等。
实现方式
实现代理模式通常涉及以下几个关键组件:
- 主题接口(Subject): 定义了代理和真实对象的共用接口,这样在任何使用真实对象的地方都可以使用代理。
- 真实主题(Real Subject): 定义了代理所表示的真实对象。
- 代理(Proxy): 维护一个对真实主题的引用,控制对真实主题的访问,并可以在访问前后添加额外的处理。
使用场景
代理模式适用于以下场景:
- 当需要在不修改原始对象的情况下控制对对象的访问时。
- 当需要为一个对象提供不同的访问控制层时,如为远程对象提供本地代表。
- 当需要添加对象的创建和销毁的额外逻辑时,如实现延迟初始化。
例如:
- 远程代理(Remote Proxy):为远程对象(如网络服务)提供本地的代理对象。
- 虚拟代理(Virtual Proxy):用于延迟昂贵操作的对象的创建,直到真正需要该对象时。
- 保护代理(Protection Proxy):控制对原始对象的访问,适用于对象应有不同访问权限的场景。
优势与劣势
- 优势
分离任务:代理可以将复杂或耗时的工作从真实对象中分离出去。
增加安全性:通过代理控制对真实对象的访问,增加安全层。
扩展功能:在不改变真实对象代码的情况下增加额外功能。 - 劣势
增加代码复杂性:引入代理会增加系统的复杂性。
可能会导致一些延迟:尤其是在远程代理的情况下。
在Spring框架中的应用
Spring框架广泛使用了代理模式,特别是在以下方面:
Spring AOP(面向切面编程):使用代理模式来实现方法拦截和增强,实现例如事务管理、安全检查等横切关注点。
事务管理:Spring通过代理模式管理事务,为方法调用提供声明式事务支持。
Spring Security:在安全框架中,代理用于在方法调用之前和之后执行安全检查。
图片加载示例
步骤 1:创建接口
首先定义了一个 Image 接口,用于展示图片。
public interface Image {
void display();
}
步骤 2:实现具体类
创建了 RealImage 类,实现了 Image 接口。这个类代表实际加载的图片。
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("展示 " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("加载 " + fileName);
}
}
创建了 ProxyImage 类,也实现了 Image 接口。这个类是 RealImage 对象的代理,负责在需要时加载图片。
public 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();
}
}
步骤 3:使用代理对象
ProxyPatternDemo 类演示了如何使用 ProxyImage 来获取和展示 RealImage 对象。
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 图片将从磁盘加载
image.display();
System.out.println("");
// 图片不会从磁盘重新加载
image.display();
}
}
在这个示例中,ProxyImage 充当 RealImage 的代理。当第一次调用 display 方法时,ProxyImage 会创建一个 RealImage 对象并调用其 display 方法。当再次调用 display 方法时,由于 RealImage 对象已经创建,代理将直接使用已加载的对象,而不会再次从磁盘加载图片。
这种方式可以节约资源,特别是在处理大型资源(如大图片)时。通过使用代理模式,只有在实际需要时才加载资源,从而优化了性能并减少了内存占用。
代码地址
23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern