代理模式(Proxy Pattern)详解
一、代理模式的概念
代理模式(Proxy Pattern)是一种结构型设计模式,它通过一个代理对象来控制对另一个对象的访问。
核心思想:
- 代理对象和被代理对象实现相同的接口。
- 代理对象在调用目标对象前后增加额外的功能(如日志、权限控制、缓存、延迟加载等)。
- 客户端只和代理对象交互,不直接访问目标对象,从而降低耦合,提高扩展性。
二、代理模式的作用
1. 控制访问权限:
- 例如,用户未登录时拦截请求,避免访问敏感资源。
2. 记录日志:
- 例如,记录用户的 API 调用信息,便于追踪。
3. 提供缓存:
- 例如,避免重复请求数据库,提高性能。
4. 远程代理:
- 例如,前端调用后端 API,代理请求数据。
5. 延迟初始化(懒加载):
- 例如,对象创建成本高时,先用代理对象占位,等需要时再创建。
三、代理模式的分类
代理模式主要分为以下几种类型:
类型 | 特点 | 示例 |
---|---|---|
远程代理(Remote Proxy) | 代理远程对象 | 前端调用后端 API,如 axios 代理请求 |
虚拟代理(Virtual Proxy) | 延迟加载,按需创建 | Vue 懒加载图片 |
保护代理(Protection Proxy) | 控制访问权限 | 用户权限控制,未登录时拦截请求 |
缓存代理(Cache Proxy) | 缓存数据,避免重复计算 | Redis 数据缓存 |
智能引用代理(Smart Reference Proxy) | 统计对象引用次数,自动释放 | Vue3 Proxy 监听对象变化 |
四、代理模式的代码实现
1. JavaScript 经典示例
🔹 直接访问 vs 代理访问
class RealSubject {request() {console.log("RealSubject: Handling request.");}
}class Proxy {constructor(realSubject) {this.realSubject = realSubject;}request() {console.log("Proxy: Checking access before forwarding the request.");this.realSubject.request();console.log("Proxy: Logging the request after execution.");}
}// 使用代理
const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);proxy.request();
🔹 输出
Proxy: Checking access before forwarding the request.
RealSubject: Handling request.
Proxy: Logging the request after execution.
🔹 解释
Proxy
代理RealSubject
,在调用request()
方法前后添加额外的逻辑(如权限检查和日志记录)。- 客户端只和代理交互,不直接访问真实对象。
2. 远程代理(API 请求代理)
场景:
- 代理前端 API 请求,自动添加
Authorization
头,避免手动传递token
。
class APIProxy {constructor(baseURL) {this.baseURL = baseURL;}async request(endpoint, options = {}) {// 自动添加身份认证 Tokenoptions.headers = {...options.headers,Authorization: `Bearer ${localStorage.getItem("token")}`};console.log(`Requesting: ${this.baseURL}${endpoint}`);const response = await fetch(`${this.baseURL}${endpoint}`, options);return response.json();}
}// 使用代理请求 API
const api = new APIProxy("https://api.example.com");api.request("/users").then(data => console.log(data));
🔹 代理的作用
- 拦截 API 请求,自动添加
Authorization
头。 - 减少重复代码,开发者无需手动添加
token
。
3. 虚拟代理(懒加载图片)
场景:
- 图片未加载时先显示占位符,等图片加载完成后再替换。
class ImageProxy {constructor(realImageURL) {this.realImageURL = realImageURL;this.imgElement = document.createElement("img");this.imgElement.src = "placeholder.jpg"; // 先显示占位图// 模拟网络请求加载真实图片setTimeout(() => {this.imgElement.src = this.realImageURL;}, 2000);}getImageElement() {return this.imgElement;}
}// 使用代理
const proxyImage = new ImageProxy("real-image.jpg");
document.body.appendChild(proxyImage.getImageElement());
🔹 代理的作用
- 图片未加载时先显示占位符,等加载完成后自动替换。
- 减少页面加载阻塞,提高用户体验。
4. 保护代理(权限控制)
场景:
- 用户未登录时,拦截敏感操作。
class User {constructor(name, role) {this.name = name;this.role = role;}
}class AdminAccessProxy {constructor(user) {this.user = user;}deleteData() {if (this.user.role !== "admin") {console.log("Access Denied: Only admin can delete data.");return;}console.log("Data deleted successfully.");}
}// 创建用户
const normalUser = new User("John", "guest");
const adminUser = new User("Alice", "admin");// 代理对象
const proxy1 = new AdminAccessProxy(normalUser);
proxy1.deleteData(); // 拒绝访问const proxy2 = new AdminAccessProxy(adminUser);
proxy2.deleteData(); // 成功删除
🔹 代理的作用
- 控制权限:只有
admin
用户才能执行删除操作。 - 安全性提升,防止未授权用户访问敏感功能。
五、代理模式 vs 装饰器模式
对比项 | 代理模式(Proxy) | 装饰器模式(Decorator) |
---|---|---|
核心作用 | 控制访问,拦截请求 | 动态增强对象功能 |
是否继承原对象 | 通常实现相同接口 | 直接增强已有对象 |
典型应用 | API 请求代理、权限控制、缓存 | Vue3 reactive() 、日志增强 |
对象的创建 | 代理对象可以创建或管理真实对象 | 直接包装对象,增强功能 |
六、代理模式的优缺点
✅ 优点
✔ 解耦客户端和目标对象,增强扩展性。
✔ 权限控制(如 API 访问权限)。
✔ 延迟初始化,提高性能(如图片懒加载)。
✔ 日志、缓存、监控,增强功能。
❌ 缺点
❌ 增加系统复杂度,需要额外的代理类。
❌ 可能带来性能开销(如远程代理涉及网络通信)。
七、总结
- 代理模式通过一个代理对象控制对目标对象的访问,可以在访问前后添加额外逻辑。
- 代理模式适用于:
- API 请求代理(自动添加
token
)。 - 懒加载(图片、数据)。
- 权限控制(用户角色判断)。
- 缓存代理(避免重复计算)。
- API 请求代理(自动添加
- 常见的代理模式:
- 远程代理(API 请求代理)。
- 虚拟代理(懒加载)。
- 保护代理(权限控制)。
🚀 代理模式是前端、后端、运维等多个领域的重要设计模式,合理使用可提高代码质量!