意图(Intent)
为其他对象提供一种代理,从而控制对这个对象的访问。
角色(Role)
类图如下:
-
Subject: 声明了RealSubject和Proxy的共同接口,这样任何可以使用RealSubject的地方都可以使用Proxy
-
RealSubject: 定义了Proxy所代理的目标对象。
-
Proxy:维护RealSubject对象的引用。实现了和RealSubject相同的接口,以便Proxy可以替换RealSubject。
虚拟代理示例
考虑下面这个例子:一个图像查看程序,列出并显示高分辨率的照片。程序必须显示所有照片的列表,但是它不需要显示实际的照片,直到用户从照片列表中选择一个照片。
下面代码展示了代表Subject角色的 Image
接口。接口有一个方法 showImage()
,具体图像类必须实现这个方法才能将图像显示在屏幕上。
/**
* Subject
*/
public interface Image {
public void showImage();
}
下面代码展示了代理的实现 —— ProxyImage
是一个虚拟代理,可以根据需要来创建和加载对象,从而节省了高分辨率图片加载到内存并显示到屏幕上的开销。
/**
* Proxy
*/
public class ImageProxy implements Image {
// Private Proxy data
private String imageFilePath;
// 对RealSubject的引用
private Image proxifiedImage;
public ImageProxy(String imageFilePath) {
this.imageFilePath = imageFilePath;
}
@Override
public void showImage() {
//当图像需要被显示的时候才创建RealSubject对象
proxifiedImage = new HighResolutionImage(imageFilePath);
//调用RealSubject的showImage()方法
proxifiedImage.showImage();
}
}
下面的代码是Image接口的实现类,代表RealSubject对象。
/**
* RealSubject
*/
public class HighResolutionImage {
public HighResolutionImage(String imageFilePath) {
loadImage(imageFilePath);
}
private void loadImage(String imageFilePath) {
//将高分辨率图像加载到内存中
//这是重量级且开销很大的操作
}
@Override
public void showImage() {
//将图像渲染到屏幕上
}
}
下面的代码展示了一个图像查看器程序。
public class ImageViewer {
public static void main(String[] args) {
//假设有一个用户选择了一个拥有3张图像的文件夹
//创建3张图片
Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");
//假设用户点击了图像列表的第一张图片
//这会导致Image对象的showImage方法被调用
//这时只会有一个图像被加载内存
highResolutionImage1.showImage();
//考虑直接创建HighResolutionImage对象
Image highResolutionImageNoProxy1 = new HighResolutionImage("sample/veryHighResPhoto1.jpeg");
Image highResolutionImageNoProxy2 = new HighResolutionImage("sample/veryHighResPhoto2.jpeg");
Image highResolutionImageNoProxy3 = new HighResolutionImage("sample/veryHighResPhoto3.jpeg");
//假设用户点击了图像列表的第二张图片
highResolutionImageNoProxy2.showImage();
//要注意的是,这种情况会导致文件夹中的所有图像都被加载进内存
//即便我们根本没有查看这个图像
//这会极大的浪费内存
}
}
代理模式的分类
按照代理对象的创建时机,我们可以将代理模式分为:静态代理和动态代理。
-
静态代理:代理对象在程序运行前,其class文件就已经存在了。上面虚拟代理的例子就是静态代理,HighResolutionImage的代理类ImageProxy是我们实现编写好的。
-
动态代理:代理对象的class文件是在程序运行时,通过反射机制动态的生成。我们可以实现Java的InvocationHandler接口,从而实现动态代理。
按照职责来划分,我们可以分为:
- 远程代理
- 虚拟代理
- Copy-on-Write代理
- 保护(Protect or Access)代理
- Cache代理
- 防火墙(Firewall)代理
- 同步化(Synchronization)代理
- 智能引用(Smart Reference)代理