全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-16 8 分钟 ✍️ juanwangdev

代理模式与装饰器模式

代理模式控制对象访问,装饰器模式动态扩展功能,两者都是重要的结构型模式。

代理模式

核心概念

代理对象充当真实对象的替身,控制对其访问,可在不修改原对象的情况下添加控制逻辑。

基础实现

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);

模式对比

特性代理模式装饰器模式
目的控制访问扩展功能
对象数量相同接口增强接口
透明性完全透明可叠加组合
典型应用缓存、保护、虚拟代理日志、计时、重试

代理强调控制,装饰器强调增强;代理不增加新功能,装饰器可无限叠加。

要点总结

  1. 代理模式:控制对象访问,可添加缓存、保护、延迟加载
  2. ES6 Proxy:强大的元编程能力,可拦截各种操作
  3. 装饰器模式:动态扩展功能,比继承更灵活
  4. 函数装饰器:高阶函数包装,实现横切关注点
  5. 实际应用:数据验证、API重试、性能监控

存放路径:articles/JS/专家/设计模式与架构思想/代理模式与装饰器模式.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 MVC/MVVM架构
下一篇 → 依赖注入与控制反转
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库