什么是代理模式?

262 阅读3分钟

意图(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接口,从而实现动态代理。

按照职责来划分,我们可以分为:

  1. 远程代理
  2. 虚拟代理
  3. Copy-on-Write代理
  4. 保护(Protect or Access)代理
  5. Cache代理
  6. 防火墙(Firewall)代理
  7. 同步化(Synchronization)代理
  8. 智能引用(Smart Reference)代理