代理模式核心概念
- 代理模式 (Proxy Pattern):
- 是一种结构型设计模式,核心思想是为一个对象提供一个代理对象,并由代理对象控制对原对象的访问;
- 代理对象可以在不修改原对象的前提下,对原对象的访问进行拦截、增强或控制 (比如权限校验、缓存、懒加载等);
- 代理模式分为几种常见类型:
- 静态代理:代理类在编译期就确定,和原对象一一对应;
- 动态代理:代理类在运行时动态生成 (如 JS/TS 中的 Proxy 对象);
- 保护代理:控制对原对象的访问权限;
- 虚拟代理:延迟创建开销大的原对象 (懒加载);
- 类图:
- 实现代码:
// 1. 抽象主题(Subject):定义统一接口 interface ImageSubject { // 核心方法:显示图片 display(): void; } // 2. 真实主题(RealSubject):实际处理图片加载的类 class RealImage implements ImageSubject { private imageUrl: string; // 构造函数:加载图片(模拟耗时操作) constructor(url: string) { this.imageUrl = url; this.loadImageFromServer(); // 实例化时就会加载图片(开销大) } // 模拟从服务器加载图片(耗时操作) private loadImageFromServer(): void { console.log(`[RealImage] 正在加载图片:${this.imageUrl}`); // 模拟网络延迟 setTimeout(() => { console.log(`[RealImage] 图片 ${this.imageUrl} 加载完成`); }, 1000); } // 实现接口:显示图片 display(): void { console.log(`[RealImage] 显示图片:${this.imageUrl}`); } } // 3. 代理(Proxy):控制 RealImage 的访问,添加额外逻辑 class ImageProxy implements ImageSubject { private imageUrl: string; // 延迟初始化 RealImage(核心:只有需要时才创建) private realImage: RealImage | null = null; constructor(url: string) { this.imageUrl = url; } // 实现接口:显示图片(代理逻辑) display(): void { // 1. 代理的前置增强:显示占位符 console.log(`[ImageProxy] 显示占位符(加载中...)`); // 2. 懒加载:只有首次调用 display 时才创建 RealImage if (!this.realImage) { this.realImage = new RealImage(this.imageUrl); } // 3. 调用真实主题的核心方法 this.realImage.display(); // 4. 代理的后置增强:记录访问日志 console.log(`[ImageProxy] 记录图片 ${this.imageUrl} 的访问日志`); } } // ---------------------- 测试代码 ---------------------- // 使用代理访问真实图片(而非直接实例化 RealImage) const imageProxy = new ImageProxy("https://example.com/photo.jpg"); // 首次调用 display:触发 RealImage 的创建和加载 console.log("=== 首次调用 display ==="); imageProxy.display(); // 第二次调用 display:复用已创建的 RealImage(不再重复加载) console.log("\n=== 第二次调用 display ==="); imageProxy.display();
常用使用场景
虚拟代理:懒加载(最常用)
-
场景:图片、大组件 (如富文本编辑器、图表) 的懒加载,只有当元素进入视口 / 被触发时才加载资源,减少初始加载开销;
-
示例:上述代码中的图片懒加载,或 React/Vue 中路由组件的懒加载 (React.lazy() 本质就是虚拟代理);
保护代理:权限控制
-
场景:控制对敏感方法 / 数据的访问,比如:
- 管理后台中,不同权限的用户只能调用对应接口;
- 限制普通用户修改全局配置,只有管理员可操作;
-
实现代码:
// 真实的配置管理类 class ConfigManager { updateConfig(key: string, value: any) { console.log(`更新配置:${key}=${value}`); } } // 权限代理 class ConfigProxy { private realManager = new ConfigManager(); private userRole: string; constructor(role: string) { this.userRole = role; } updateConfig(key: string, value: any) { if (this.userRole === "admin") { this.realManager.updateConfig(key, value); // 管理员允许访问 } else { console.error("无权限:普通用户无法修改配置"); } } } // 测试 const adminProxy = new ConfigProxy("admin"); adminProxy.updateConfig("theme", "dark"); // 正常执行 const userProxy = new ConfigProxy("user"); userProxy.updateConfig("theme", "dark"); // 报错:无权限
缓存代理:复用计算 / 请求结果
-
场景:对耗时的计算、重复的接口请求做缓存,避免重复执行:
- 复杂的列表筛选 / 排序计算;
- 无状态的 GET 请求 (如获取商品详情);
-
实现代码:
// 真实的计算类 class Calculator { // 模拟耗时计算:求和 sum(arr: number[]): number { console.log("执行耗时计算..."); return arr.reduce((a, b) => a + b, 0); } } // 缓存代理 class CalculatorProxy { private realCalc = new Calculator(); private cache = new Map<string, number>(); // 缓存结果 sum(arr: number[]): number { const key = arr.join(","); // 生成缓存 key if (this.cache.has(key)) { console.log("使用缓存结果"); return this.cache.get(key)!; } const result = this.realCalc.sum(arr); this.cache.set(key, result); // 缓存结果 return result; } } // 测试 const proxy = new CalculatorProxy(); console.log(proxy.sum([1,2,3])); // 执行计算,输出 6 console.log(proxy.sum([1,2,3])); // 使用缓存,输出 6
原生 Proxy:对象拦截(动态代理)
-
场景:Vue3 的响应式系统 (reactive)、数据校验、日志记录、防抖 / 节流等;
-
实现代码:
// 代理对象,拦截属性的读取和修改 const reactive = (target: object) => { return new Proxy(target, { get(target, key) { console.log(`读取属性 ${key.toString()}`); return Reflect.get(target, key); }, set(target, key, value) { console.log(`修改属性 ${key.toString()} 为 ${value}`); return Reflect.set(target, key, value); }, }); }; // 测试 const user = reactive({ name: "张三", age: 20 }); user.name; // 输出:读取属性 name user.age = 21; // 输出:修改属性 age 为 21
代理模式优缺点
优点
-
职责清晰:真实对象专注于业务逻辑;
-
高扩展性:无需修改真实对象即可增加功能;
-
智能化:可以实现智能访问控制;
缺点
-
增加复杂度:引入额外代理类;
-
性能影响:可能增加响应延迟;
-
可能造成混淆:真实对象和代理对象的关系;
装饰器模式(结构型模式)
上一篇