1. 代理模式的概念
代理模式(Proxy Pattern)是一种结构型设计模式,允许代理对象充当另一个对象的接口,以控制对该对象的访问。
1.1 核心思想
代理模式为其他对象提供一个代理,以控制对这个对象的访问。代理类充当客户端和实际对象之间的中介,它可以拦截对实际对象的访问并允许在此之前或之后执行额外的操作。
1.2 组成要素:
- 抽象接口(接口或抽象类): 定义代理类和实际对象的共同接口。
- 实际对象(Real Subject): 实际执行工作的对象。
- 代理对象(Proxy): 持有对实际对象的引用,提供与实际对象相同的接口,以便于客户端访问。
1.3 不同类型的代理模式
- 远程代理(Remote Proxy): 在不同的地址空间中代表一个对象,隐藏其存在于不同地址空间的事实。
- 虚拟代理(Virtual Proxy): 根据需要创建开销大的对象,用于延迟对象的实例化。
- 保护代理(Protection Proxy): 控制对对象的访问权限,根据访问权限决定是否提供对象的访问。
- 智能代理(Smart Proxy): 对调用对象进行一些额外的操作,如缓存、记录日志、延迟加载等。
1.4 优点
- 控制访问: 可以在实际对象的访问上增加控制。
- 增强功能: 代理对象可以在不改变实际对象的情况下,提供额外的功能。
- 减少耦合: 客户端无需直接访问实际对象,降低了系统的耦合度。
1.5 适用场景
- 远程代理适用于需要处理远程对象的访问。
- 虚拟代理适用于对象创建成本高昂的情况,可以延迟对象的实例化。
- 其他类型的代理适用于对对象的访问控制和功能增强。
代理模式可通过代理对象控制对实际对象的访问,并在其基础上提供额外的功能。这种模式使得系统更灵活、可扩展,并且有助于提高系统的安全性和性能。
2. 代理模式举例
2.1 虚拟代理的应用场景:延迟加载图片的功能
以下是一个简单的 Java 代理模式的示例,演示了虚拟代理的应用场景,延迟加载图片的功能。
// 图片接口
interface Image {
void display();
}
// 实际图片类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
private void loadFromDisk() {
System.out.println("Loading " + filename + " from disk");
}
}
// 代理图片类,用于控制图片加载
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();
}
}
// 示例使用代理加载图片
public class ProxyPatternExample {
public static void main(String[] args) {
// 使用代理对象,实际图片并没有立即加载
Image image = new ProxyImage("sampleImage.jpg");
// 当需要显示时才加载实际图片
image.display();
// 此时实际图片已经加载,再次调用时无需重新加载
image.display();
}
}
在上述示例中,ProxyImage 作为代理对象,控制了图片的加载。在第一次调用 display() 方法时,才会加载真正的图片对象 RealImage,在之后的调用中,直接使用已加载的图片而不会重新加载。这体现了虚拟代理延迟加载的特点。
2.2 保护代理的应用场景:实现对特定资源的访问控制
当用户只有特定权限时,可以使用代理模式来实现对特定资源的访问控制。下面是一个简单的 Java 示例,演示了保护代理的应用场景。
// 资源接口
interface Resource {
void accessResource();
}
// 实际资源类
class RealResource implements Resource {
private String name;
public RealResource(String name) {
this.name = name;
}
@Override
public void accessResource() {
System.out.println("Accessing " + name);
}
}
// 代理资源类,用于控制资源访问权限
class ProxyResource implements Resource {
private RealResource realResource;
private String name;
private boolean isAdmin;
public ProxyResource(String name, boolean isAdmin) {
this.name = name;
this.isAdmin = isAdmin;
}
@Override
public void accessResource() {
if (isAdmin) {
if (realResource == null) {
realResource = new RealResource(name);
}
realResource.accessResource();
} else {
System.out.println("Access denied for " + name);
}
}
}
// 示例使用代理控制资源访问权限
public class ProxyPatternExample {
public static void main(String[] args) {
// 使用代理对象,模拟普通用户权限
Resource resource = new ProxyResource("Admin Dashboard", false);
resource.accessResource(); // 用户没有权限
// 使用代理对象,模拟管理员权限
Resource adminResource = new ProxyResource("Admin Dashboard", true);
adminResource.accessResource(); // 管理员可以访问资源
}
}
在这个例子中,ProxyResource 作为代理对象,通过 isAdmin 标志控制资源的访问权限。当用户没有足够的权限时,代理会拒绝访问资源;而当用户有足够权限时,代理将允许访问资源。这展示了保护代理的功能,即控制对资源的访问权限。
2.3 日志记录代理的应用场景
当需要记录方法调用日志时,可以使用代理模式实现。下面是一个 Java 示例,演示了日志记录代理的应用场景。
// 接口
interface SomeInterface {
void someMethod();
}
// 实际对象
class SomeClass implements SomeInterface {
@Override
public void someMethod() {
System.out.println("Real object - Performing some method");
}
}
// 代理对象,用于记录方法调用日志
class LoggingProxy implements SomeInterface {
private SomeInterface realObject;
public LoggingProxy(SomeInterface realObject) {
this.realObject = realObject;
}
@Override
public void someMethod() {
System.out.println("Logging: Method someMethod is called");
realObject.someMethod();
System.out.println("Logging: Method someMethod is finished");
}
}
// 示例使用代理记录方法调用日志
public class ProxyPatternExample {
public static void main(String[] args) {
SomeInterface realObject = new SomeClass();
SomeInterface proxy = new LoggingProxy(realObject);
proxy.someMethod(); // 记录方法调用日志
}
}
在这个示例中,LoggingProxy 作为代理对象,对 SomeClass 的方法调用进行了日志记录。LoggingProxy 在 someMethod() 方法调用前后分别输出日志,从而记录了方法调用的开始和结束。