代理模式与装饰器模式
代理模式控制对象访问,装饰器模式动态扩展功能,两者都是重要的结构型模式。
代理模式
核心概念
代理对象充当真实对象的替身,控制对其访问,可在不修改原对象的情况下添加控制逻辑。
基础实现
JavaScript
class RealSubject {
request() {
return 'Real request';
}
}
class Proxy {
constructor(realSubject) {
this.realSubject = realSubject;
}
request() {
// 前置控制
if (!this.checkAccess()) {
return 'Access denied';
}
const result = this.realSubject.request();
// 后置处理
return result;
}
checkAccess() {
return true;
}
}
// 使用
const real = new RealSubject();
const proxy = new Proxy(real);
proxy.request();
ES6 Proxy
JavaScript
const target = {
name: 'Alice',
age: 25
};
const handler = {
get(obj, prop) {
console.log(`Getting ${prop}`);
return obj[prop];
},
set(obj, prop, value) {
console.log(`Setting ${prop} = ${value}`);
obj[prop] = value;
return true;
},
has(obj, prop) {
console.log(`Checking ${prop}`);
return prop in obj;
}
};
const proxy = new Proxy(target, handler);
proxy.name; // Getting name
proxy.age = 26; // Setting age = 26
'name' in proxy; // Checking name
虚拟代理(延迟加载)
JavaScript
class ImageProxy {
constructor(src) {
this.src = src;
this.image = null;
}
display() {
if (!this.image) {
this.image = new Image();
this.image.src = this.src;
console.log('Loading image...');
}
return this.image;
}
}
// 使用
const proxy = new ImageProxy('large-image.jpg');
// 图片在display()调用时才加载
proxy.display();
保护代理(访问控制)
JavaScript
const sensitiveData = {
salary: 10000,
password: 'secret'
};
const protectedProxy = new Proxy(sensitiveData, {
get(obj, prop) {
if (prop === 'password') {
throw new Error('Access denied');
}
return obj[prop];
},
set(obj, prop, value) {
if (prop === 'salary' && value < 0) {
throw new Error('Invalid salary');
}
obj[prop] = value;
return true;
}
});
protectedProxy.salary; // 10000
protectedProxy.password; // Error: Access denied
缓存代理
JavaScript
function createCacheProxy(fn) {
const cache = new Map();
return new Proxy(fn, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('Cache hit');
return cache.get(key);
}
const result = target.apply(thisArg, args);
cache.set(key, result);
return result;
}
});
}
// 使用
const expensiveCalculation = (n) => {
console.log('Calculating...');
return n * n;
};
const cachedCalc = createCacheProxy(expensiveCalculation);
cachedCalc(5); // Calculating... 25
cachedCalc(5); // Cache hit 25
装饰器模式
核心概念
动态给对象添加功能,比继承更灵活,可在运行时组合功能。
基础实现
JavaScript
class Component {
operation() {
return 'Component';
}
}
class Decorator {
constructor(component) {
this.component = component;
}
operation() {
return this.component.operation();
}
}
class ConcreteDecoratorA extends Decorator {
operation() {
return `DecoratorA(${super.operation()})`;
}
}
class ConcreteDecoratorB extends Decorator {
operation() {
return `DecoratorB(${super.operation()})`;
}
}
// 使用
const component = new Component();
const decorated = new ConcreteDecoratorB(new ConcreteDecoratorA(component));
decorated.operation(); // DecoratorB(DecoratorA(Component))
函数装饰器
JavaScript
// 日志装饰器
function withLogging(fn) {
return function (...args) {
console.log(`Calling with args:`, args);
const result = fn.apply(this, args);
console.log(`Result:`, result);
return result;
};
}
// 计时装饰器
function withTiming(fn) {
return function (...args) {
const start = performance.now();
const result = fn.apply(this, args);
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);
return result;
};
}
// 组合装饰器
function add(a, b) {
return a + b;
}
const decoratedAdd = withLogging(withTiming(add));
decoratedAdd(2, 3);
// Calling with args: [2, 3]
// Execution time: 0.1ms
// Result: 5
类装饰器(TypeScript风格)
JavaScript
// 手动实现类装饰器
function sealed(constructor) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
function logger(constructor) {
const original = constructor;
return class extends constructor {
constructor(...args) {
super(...args);
console.log(`Instance of ${original.name} created`);
}
};
}
// 方法装饰器
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
function debounce(delay) {
return function (target, key, descriptor) {
const original = descriptor.value;
let timer;
descriptor.value = function (...args) {
clearTimeout(timer);
timer = setTimeout(() => original.apply(this, args), delay);
};
return descriptor;
};
}
class Example {
@readonly
name = 'example';
@debounce(300)
onClick() {
console.log('clicked');
}
}
实际应用
数据验证代理
JavaScript
function createValidator(schema) {
return function (obj) {
return new Proxy(obj, {
set(target, key, value) {
if (schema[key]) {
const { type, min, max } = schema[key];
if (typeof value !== type) {
throw new TypeError(`${key} must be ${type}`);
}
if (min !== undefined && value < min) {
throw new RangeError(`${key} must >= ${min}`);
}
if (max !== undefined && value > max) {
throw new RangeError(`${key} must <= ${max}`);
}
}
target[key] = value;
return true;
}
});
};
}
const userSchema = {
age: { type: 'number', min: 0, max: 150 },
name: { type: 'string' }
};
const createUser = createValidator(userSchema);
const user = createUser({});
user.age = 25; // OK
user.age = -1; // RangeError
API请求装饰器
JavaScript
// 重试装饰器
function withRetry(maxRetries = 3, delay = 1000) {
return function (fn) {
return async function (...args) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn.apply(this, args);
} catch (error) {
lastError = error;
if (i < maxRetries - 1) {
await new Promise(r => setTimeout(r, delay));
}
}
}
throw lastError;
};
};
}
// 使用
const fetchWithRetry = withRetry(3, 500)(fetch);
模式对比
| 特性 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问 | 扩展功能 |
| 对象数量 | 相同接口 | 增强接口 |
| 透明性 | 完全透明 | 可叠加组合 |
| 典型应用 | 缓存、保护、虚拟代理 | 日志、计时、重试 |
代理强调控制,装饰器强调增强;代理不增加新功能,装饰器可无限叠加。
要点总结
- 代理模式:控制对象访问,可添加缓存、保护、延迟加载
- ES6 Proxy:强大的元编程能力,可拦截各种操作
- 装饰器模式:动态扩展功能,比继承更灵活
- 函数装饰器:高阶函数包装,实现横切关注点
- 实际应用:数据验证、API重试、性能监控
存放路径:articles/JS/专家/设计模式与架构思想/代理模式与装饰器模式.md
📝 发现内容有误?点击此处直接编辑