代理模式是一种结构型设计模式,它通过引入“代理对象”来控制原始对象的访问。这个代理对象与我们要访问的原始对象实现相同的接口,然后客户端就可以通过代理对象访问原始对象。那么我们就可以实现在不修改原始对象电的情况下增加额外的功能。比如,实现权限控制、日志记录或者延迟加载等。
下面是一个代理模式的UML类图:

image.png
在上面的类图中:

  • Subject:是一个抽象主题,它定义了我们真实要访问的主题和代理对象的公共接口,客户端直接和这个接口交互。
  • RealSubject: 它是客户端实际上要操作的对象,它也实现了Subject接口。
  • Proxy:是代理对象,它实现了Subject的接口,内部持有RealSubjet的引用,它负责控制对真实主体对象的访问,并且还可以增加额外的操作
  • Client:是客户端,它只和Subject接口交互,无需知道真实主体的存在。

代理模式的关键是代理对象与真实对象实现相同的接口,从而实现对真实对象的透明访问,同时可以在访问过程中增加额外功能。

举一个具体的例子。比如在分布式系统中,客户端需要调用远程服务器上的服务(如微服务接口),直接调用会涉及网络通信、序列化 / 反序列化等复杂逻辑。使用代理模式可以将这些细节封装在代理对象中,让客户端像调用本地方法一样使用远程服务。

下面的代码模拟了一个电商系统中,客户端需要调用远程库存服务查询商品库存,代理对象负责处理网络通信细节:

首先创建一个代表主题的库存服务接口:

1
2
3
4
5
6
// 抽象主题:库存服务接口
public interface InventoryService {
int getStock(String productId); // 查询库存
boolean reduceStock(String productId, int quantity); // 扣减库存
}

然后创建一个代理丢下实现这个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 代理类:远程服务代理(客户端)
public class RemoteInventoryProxy implements InventoryService {
// 代理对象封装远程调用的细节
@Override
public int getStock(String productId) {
System.out.println("[代理] 开始远程调用:查询库存,商品ID=" + productId);

// 1. 处理网络通信(模拟)
System.out.println("[代理] 建立网络连接,发送请求...");

// 2. 调用远程服务(实际中通过网络调用,这里简化为直接调用)
InventoryService remoteService = new RemoteInventoryServiceImpl();
int stock = remoteService.getStock(productId);

// 3. 处理响应(模拟)
System.out.println("[代理] 接收响应,关闭连接");

return stock;
}

@Override
public boolean reduceStock(String productId, int quantity) {
System.out.println("[代理] 开始远程调用:扣减库存,商品ID=" + productId + ",数量=" + quantity);

// 封装网络调用细节
System.out.println("[代理] 建立网络连接,发送请求...");
InventoryService remoteService = new RemoteInventoryServiceImpl();
boolean result = remoteService.reduceStock(productId, quantity);
System.out.println("[代理] 接收响应,关闭连接");

return result;
}
}

真实的库存服务对象也实现这个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 真实主题:远程库存服务的实际实现(运行在服务器端)
public class RemoteInventoryServiceImpl implements InventoryService {
@Override
public int getStock(String productId) {
// 实际的库存查询逻辑(如查询数据库)
System.out.println("[服务器] 查询商品 " + productId + " 的库存");
return 150; // 模拟库存数据
}

@Override
public boolean reduceStock(String productId, int quantity) {
// 实际的库存扣减逻辑
System.out.println("[服务器] 扣减商品 " + productId + " 的库存:" + quantity);
return true; // 模拟扣减成功
}
}

下面的代码演示了客户端调用的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 客户端代码:调用库存服务(通过代理)
public class InventoryClient {
public static void main(String[] args) {
// 客户端通过代理访问远程服务,无需关心网络细节
InventoryService inventoryService = new RemoteInventoryProxy();

// 查询商品库存
int stock = inventoryService.getStock("product_123");
System.out.println("商品库存:" + stock); // 输出:商品库存:150

// 扣减库存
boolean success = inventoryService.reduceStock("product_123", 10);
System.out.println("扣减库存是否成功:" + success); // 输出:扣减库存是否成功:true
}
}

代理模式的优势如下:

  • 透明访问:客户端无需知道原始对象的存在,代理对象对客户端透明。
  • 功能增强:在不修改原始对象的情况下,通过代理添加日志、缓存、权限校验等功能。
  • 资源优化:例如使用 “虚拟代理” 延迟加载大对象,只有在真正需要时才初始化。