代理模式是一种结构型设计模式,其主要目的是为其他对象提供一种代理以控制对这个对象的访问。代理类通常充当客户端和目标对象之间的中介,可以在访问目标对象时添加额外的功能或控制访问权限。
问题
为什么要控制对于某个对象的访问呢?
举个例子: 有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。
你可以实现延迟初始化: 在实际有需要时再创建该对象。 对象的所有客户端都要执行延迟初始代码。 不幸的是, 这很可能会带来很多重复代码。
在理想情况下, 我们希望将代码直接放入对象的类中, 但这并非总是能实现: 比如类可能是第三方封闭库的一部分。
解决方案
代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。
代理将自己伪装成数据库对象, 可在客户端或实际数据库对象不知情的情况下处理延迟初始化和缓存查询结果的工作。
这有什么好处呢? 如果需要在类的主要业务逻辑前后执行一些工作, 你无需修改类就能完成这项工作。 由于代理实现的接口与原类相同, 因此你可将其传递给任何一个使用实际服务对象的客户端。
代理模式结构
- Subject(抽象主题):定义了目标对象和代理对象的共同接口,这样一来在任何使用目标对象的地方都可以使用代理对象。
- RealSubject(真实主题):实现了抽象主题接口,是代理模式中所要代理的真实对象。
- Proxy(代理):维护了一个指向真实主题对象的引用,同时提供了与真实主题对象相同的接口,以便在任何时刻都可以替代真实主题。
示例
本例演示使用代理模式代理文件读取并进行权限控制。
在这个示例中,FileReader是抽象主题接口,RealFileReader是真实主题类,FileReaderProxy是代理类。客户端在Main类中创建代理对象,代理对象在调用真实主题前会进行用户权限验证,实现了保护代理的功能。
// Subject
interface FileReader {
void readFile();
}
// RealSubject
class RealFileReader implements FileReader {
private String filename;
public RealFileReader(String filename) {
this.filename = filename;
}
public void readFile() {
System.out.println("Reading file: " + filename);
}
}
// Proxy
class FileReaderProxy implements FileReader {
private RealFileReader realFileReader;
private String username;
public FileReaderProxy(String filename, String username) {
this.realFileReader = new RealFileReader(filename);
this.username = username;
}
public void readFile() {
if (isValidUser()) {
realFileReader.readFile();
} else {
System.out.println("Access denied for user: " + username);
}
}
private boolean isValidUser() {
// Simulate user authentication logic
return "admin".equals(username);
}
}
// Client
public class Main {
public static void main(String[] args) {
FileReader fileReader = new FileReaderProxy("example.txt", "admin");
fileReader.readFile();
FileReader invalidUserFileReader = new FileReaderProxy("example.txt", "user");
invalidUserFileReader.readFile();
}
}
优点
- 控制访问:代理可以控制对真实对象的访问,实现权限控制。
- 降低耦合:通过代理,客户端与真实对象之间的耦合降低,可以更灵活地对目标对象进行管理。
- 延迟加载:代理可以延迟加载真实对象,提高系统的性能。
- 增加额外功能:代理可以在调用真实对象的基础上,增加一些额外的功能,例如记录日志、性能监控等。
缺点
- 复杂性增加:引入代理类增加了系统的复杂性。
- 性能损耗:在某些情况下,由于增加了代理层,可能导致性能损耗。
适用场景
- 远程代理:当对象位于不同的地址空间,需要通过网络进行访问时。
- 虚拟代理:用于控制访问大对象的开销,例如延迟加载。
- 保护代理:用于控制对对象的访问权限,实现权限控制。
- 缓存代理:为耗时的操作提供缓存,以提高系统性能。
- 日志记录代理:记录对对象的访问日志。