代理模式详解:从送花场景到实际应用
什么是代理模式?
代理模式(Proxy Pattern)是一种结构型设计模式,它提供了一个代理对象来控制对另一个对象的访问。代理对象充当客户端和目标对象之间的中介,可以在访问目标对象前后执行额外的操作。
核心概念
代理模式包含三个主要角色:
- 目标对象(Real Subject):被代理的原始对象
- 代理对象(Proxy):控制对目标对象访问的代理
- 客户端(Client):使用代理对象的代码
送花场景中的代理模式
让我们通过一个生动的例子来理解代理模式:
场景描述
- 小帅:想要送花的人(客户端)
- 小美:收花的人(目标对象)
- 小红:小美的朋友,作为代理(代理对象)
代码实现
javascript
// 目标对象 - 小美
class XiaoMei {
constructor() {
this.name = '小美';
this.xq = 60; // 心情值
}
receiveFlower(sender) {
console.log(`小美收到了${sender.name}送的花`);
if (this.xq > 80) {
console.log('让我们在一起吧!');
return 'accept';
} else {
console.log('抱歉,我们不合适。');
return 'reject';
}
}
}
// 代理对象 - 小红
class XiaoHong {
constructor(target) {
this.name = '小红';
this.target = target; // 被代理的对象
}
receiveFlower(sender) {
console.log(`小红收到了${sender.name}送的花`);
console.log('小红作为代理,正在处理...');
// 代理可以修改目标对象的状态
setTimeout(() => {
console.log('小红偷偷提高了小美的心情值...');
this.target.xq = 100;
// 代理决定是否转发请求
if (this.target.xq > 80) {
console.log('小红决定转发给小美...');
this.target.receiveFlower(sender);
}
}, 2000);
}
}
// 客户端 - 小帅
class XiaoShuai {
constructor() {
this.name = '小帅';
}
sendFlower(target) {
console.log(`${this.name}想要送花...`);
target.receiveFlower(this);
}
}
代理模式的优势
1. 控制访问
代理可以控制对目标对象的访问,决定是否转发请求。
2. 增强功能
代理可以在不修改目标对象的情况下,添加额外的功能。
3. 延迟加载
代理可以实现延迟加载,只在需要时才创建目标对象。
4. 缓存
代理可以缓存结果,避免重复计算。
代理模式的类型
1. 虚拟代理(Virtual Proxy)
延迟创建昂贵的对象。
javascript
class ImageProxy {
constructor(src) {
this.src = src;
this.image = null;
}
display() {
if (!this.image) {
this.image = new Image(this.src);
}
return this.image;
}
}
2. 保护代理(Protection Proxy)
控制对敏感对象的访问。
javascript
class BankAccountProxy {
constructor(account, user) {
this.account = account;
this.user = user;
}
withdraw(amount) {
if (this.user.hasPermission('withdraw')) {
return this.account.withdraw(amount);
} else {
throw new Error('没有权限取款');
}
}
}
3. 远程代理(Remote Proxy)
为远程对象提供本地接口。
javascript
class RemoteServiceProxy {
constructor(url) {
this.url = url;
}
async getData() {
const response = await fetch(this.url);
return response.json();
}
}
4. 智能引用代理(Smart Reference Proxy)
在访问对象时执行额外操作。
javascript
class SmartReferenceProxy {
constructor(target) {
this.target = target;
this.accessCount = 0;
}
getData() {
this.accessCount++;
console.log(`访问次数: ${this.accessCount}`);
return this.target.getData();
}
}
实际应用场景
1. 网络请求代理
javascript
class ApiProxy {
constructor(api) {
this.api = api;
this.cache = new Map();
}
async get(url) {
if (this.cache.has(url)) {
console.log('从缓存返回数据');
return this.cache.get(url);
}
const data = await this.api.get(url);
this.cache.set(url, data);
return data;
}
}
2. 权限控制代理
javascript
class PermissionProxy {
constructor(service, user) {
this.service = service;
this.user = user;
}
delete(id) {
if (this.user.role === 'admin') {
return this.service.delete(id);
} else {
throw new Error('权限不足');
}
}
}
3. 日志记录代理
javascript
class LoggingProxy {
constructor(target) {
this.target = target;
}
method1() {
console.log('调用 method1');
const result = this.target.method1();
console.log('method1 执行完成');
return result;
}
}
代理模式 vs 装饰器模式
特性 | 代理模式 | 装饰器模式 |
---|---|---|
目的 | 控制访问 | 增强功能 |
关系 | 一对一 | 一对多 |
生命周期 | 通常长期 | 可以动态添加/移除 |
最佳实践
1. 保持接口一致
代理对象应该与目标对象实现相同的接口。
2. 透明性
客户端应该能够像使用目标对象一样使用代理对象。
3. 职责单一
代理应该只负责控制访问,不应该包含业务逻辑。
4. 性能考虑
代理可能会影响性能,需要权衡利弊。
总结
代理模式是一种强大的设计模式,它通过提供代理对象来控制对目标对象的访问。在我们的送花场景中,小红作为代理,不仅控制了送花的过程,还能够在合适的时机修改小美的状态,最终影响送花的结果。
代理模式在实际开发中有广泛的应用,从网络请求缓存到权限控制,从延迟加载到日志记录,都能看到代理模式的身影。掌握代理模式,能够让我们写出更加灵活、可维护的代码。