代理模式(Proxy Pattern) 是一种结构型设计模式,其目的是通过创建一个代理对象,控制对其他对象的访问。代理对象充当了客户端与目标对象之间的中介,可以在访问目标对象之前或之后执行一些操作,如记录日志、控制访问权限等。
通俗地说,就像我们请一个代理人代表我们做某件事情,代理模式也是通过引入一个代理对象来间接访问目标对象,从而控制和管理对目标对象的访问。
想象我们是非常忙碌的公司老板,每天都有很多会议、电话、文件需要处理。为了能更有效地管理时间,我们雇佣了一位秘书来帮助我们处理一些日常事务。
这位秘书就像是我们的代理,她可以代表我们处理一些事情,比如安排会议、回复邮件、接听电话等。当有人想要和我们预约会议时,他们通常会先联系秘书,而不是直接找我们。秘书在必要时会筛选那些重要的事项,然后将它们交给我们,而我们则可以专注于最重要的工作。并且有些准备工作或者有些收尾工作,秘书也会自己完成,不会来麻烦我们。
在这个例子中,我们就是被代理的对象 ,而秘书就是代理对象。通过秘书这个代理,我们可以更有效地管理我们的时间和工作,而不必自己处理所有的琐事。
主要角色:
- 抽象主题(Subject): 定义了目标对象和代理对象共同实现的接口。
- 真实主题(Real Subject): 实现了抽象主题接口,是目标对象。
- 代理(Proxy): 实现了抽象主题接口,包含了对真实主题的引用,可以在调用真实主题前后执行一些额外的操作。
示例:
举一个简单的图像加载器的例子,真实主题是图像加载器,代理对象可以在加载图像前后记录日志。
java
package com.luke.designpatterns.proxyPattern;
// 抽象主题
interface Image {
void display();
}
// 真实主题
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
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);
}
// 在显示前后可以执行一些额外的操作
System.out.println("代理准备加载图片: " + filename);
realImage.display();
System.out.println("代理加载图片完毕: " + filename);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Image image1 = new ProxyImage("图片.jpg");
// Image image2 = new ProxyImage("图片2.png");
System.out.println("--------------------");
// 图像将在调用 display() 时被加载和显示
image1.display();
// image2.display();
System.out.println("--------------------");
// 由于代理在第一次调用时已经加载过了真实对象,再次调用时直接显示
image1.display();
}
}
在这个例子中,Image
是抽象主题接口,RealImage
是真实主题,负责加载和显示图像。ProxyImage
是代理,包含对真实主题的引用,在调用真实主题的 display()
方法前后执行额外的操作,例如记录日志。客户端代码通过代理对象访问真实主题,代理对象在需要时创建和管理真实主题,从而控制对真实主题的访问。代理模式在需要控制对对象访问的场景中非常有用,例如延迟加载、访问控制、日志记录等。
适用场景
代理模式在软件开发中有许多实际可用的场景,以下是其中一些常见的:
- 网络请求代理:在应用程序中进行网络请求时,可以使用代理模式来处理网络请求的发送和接收。代理可以负责处理网络连接、数据序列化、错误处理等任务,从而解耦应用程序与网络通信的细节。
- 安全代理:在需要控制对敏感资源或操作访问权限时,可以使用安全代理。安全代理可以验证用户的身份和权限,确保只有经过授权的用户才能访问资源或执行操作。
- 对象关联代理:当需要管理对象之间的关联关系时,可以使用对象关联代理。代理对象可以在访问目标对象之前或之后执行一些额外的逻辑,如延迟加载、缓存等。
- 日志记录代理:在应用程序中加入日志记录代理,可以记录方法调用、参数、返回值等信息,用于调试、监控和审计。日志记录代理可以帮助开发人员追踪和分析应用程序的行为。
- 缓存代理:在需要频繁访问资源或执行计算密集型操作时,可以使用缓存代理来提高系统性能。代理可以缓存结果,避免重复计算或查询,从而加快响应速度。
- 远程代理:在分布式系统中,客户端可能需要与远程服务器进行通信。远程代理可以隐藏网络通信的细节,使客户端代码更加简洁和易于维护。
- 动态代理:使用动态代理可以在运行时动态创建代理对象,而不需要预先定义接口或实现类。这种灵活性使得动态代理特别适用于实现框架、AOP(面向切面编程)等场景。