开发中常用的设计模式 用法及注意事项

常见的设计模式 :单例模式、工厂模式、观察者模式、发布-订阅模式、装饰器模式、策略模式、代理模式、模块模式等
React中的高阶组件(装饰器模式)、Vue 的事件总线(发布-订阅模式)

一、 单例模式 (Singleton)

用途 :确保一个类只有一个实例,并提供全局访问点(如全局状态管理、配置对象)。

javascript 复制代码
class Logger {
  constructor() {
    if (!Logger.instance) {
      this.logs = [];
      Logger.instance = this;
    }
    return Logger.instance;
  }

  log(message) {
    this.logs.push(message);
    console.log(message);
  }
}
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2); // true

注意事项 :

避免滥用,过度使用单例可能导致全局状态污染。

在模块化系统中(如 ES6 Modules),直接用 export 导出实例更简洁。

二、 工厂模式 (Factory)

用途 :封装对象的创建逻辑,根据输入参数返回不同类的实例(如创建 UI 组件、不同数据源连接)。

javascript 复制代码
class Button {
  render() {
    console.log("Base Button");
  }
}

class PrimaryButton extends Button {
  render() {
    console.log("Primary Button");
  }
}

class SecondaryButton extends Button {
  render() {
    console.log("Secondary Button");
  }
}

function createButton(type) {
  switch (type) {
    case "primary":
      return new PrimaryButton();
    case "secondary":
      return new SecondaryButton();
    default:
      return new Button();
  }
}

const btn1 = createButton("primary");
btn1.render(); // "Primary Button"

注意事项 :

适合对象创建逻辑复杂的场景。
新增类型时需要修改工厂函数,违反开闭原则。

三、 观察者模式 (Observer)

用途 :定义对象间的一对多依赖关系,当一个对象状态变化时,自动通知所有依赖者(如事件处理、数据绑定)。

javascript 复制代码
class Subject {
  constructor() {
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  update(data) {
    console.log("Received data:", data);
  }
}

const subject = new Subject();
const observer1 = new Observer();
subject.addObserver(observer1);
subject.notify("Hello!"); // "Received data: Hello!"

注意事项 :

观察者和被观察者可能形成循环依赖。

如果没有正确移除观察者会导致内存泄漏。

四、 发布-订阅模式 (Pub-Sub)

用途 :解耦事件的发布者和订阅者,通过事件中心管理通信(如 Vue 的事件总线、Redux 的订阅机制)。

javascript 复制代码
class EventBus {
  constructor() {
    this.events = {};
  }

  subscribe(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  publish(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(cb => cb(data));
    }
  }
}

const bus = new EventBus();
bus.subscribe("message", data => console.log("Received:", data));
bus.publish("message", "Hello World!"); // "Received: Hello World!"

注意事项 :

比观察者模式更解耦,但事件命名冲突可能导致问题。
避免过度使用,高频事件可能影响性能。

五、 装饰器模式 (Decorator)

用途 :动态扩展对象功能,不修改原对象代码 (如日志记录、权限校验)。

代码示例(使用 ES7 装饰器语法):

javascript 复制代码
function log(target, name, descriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args) {
    console.log(`Calling ${name} with args:`, args);
    return original.apply(this, args);
  };
  return descriptor;
}

class Calculator {
  @log
  add(a, b) {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(2, 3); // 输出: "Calling add with args: [2, 3]"

注意事项 :

装饰器可能增加代码复杂度。
JavaScript 原生装饰器语法需要 Babel 编译支持。

六、 策略模式 (Strategy)

用途 :定义一系列算法,使其可互相替换(如表单验证、支付方式选择)。

javascript 复制代码
const strategies = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b
};

class Calculator {
  calculate(strategy, a, b) {
    return strategies[strategy](a, b);
  }
}

const calc = new Calculator();
console.log(calc.calculate("add", 5, 3)); // 8

注意事项 :

适合替换大量条件分支语句。
如果策略对象可能过多,需合理管理。

七、代理模式 (Proxy)

用途 :为对象提供代理以控制访问(如数据校验、缓存、延迟加载)。

代码示例(使用 ES6 Proxy):

javascript 复制代码
const user = {
  name: "John",
  age: 30
};

const proxy = new Proxy(user, {
  get(target, prop) {
    console.log(`Reading ${prop}`);
    return target[prop];
  },
  set(target, prop, value) {
    if (prop === "age" && value < 0) {
      throw new Error("Invalid age");
    }
    target[prop] = value;
    return true;
  }
});

console.log(proxy.name); // "Reading name" → "John"
proxy.age = -1; // 抛出错误

注意事项 :

可能引入性能开销。
避免过度拦截导致调试困难。

八、 模块模式 (Module)

用途 :封装私有变量和方法,暴露公有接口(如工具库、组件封装)。

javascript 复制代码
const CounterModule = (() => {
  let count = 0;

  const increment = () => {
    count++;
    console.log("Count:", count);
  };

  return {
    increment
  };
})();

CounterModule.increment(); // "Count: 1"
// CounterModule.count → 无法访问

注意事项 :

适合小型模块化开发。
现代开发中更推荐使用 ES6 Modules。

总结

单例模式 : 用于全局状态管理、配置共享,注意避免全局污染;
工厂模式 : 用于动态创建对象,注意新增类型需修改工厂函数
观察者/发布订阅模式 : 用于事件驱动、数据绑定,注意防止内存泄漏、命名冲突
装饰器模式 : 用于功能扩展(AOP 编程),注意增加代码复杂度
策略模式 : 用于替换条件分支,注意管理大量策略对象
代理模式 : 用于访问控制、缓存,注意性能开销
模块模式 : 用于封装私有逻辑,注意现代开发优先使用 ES6 Modules

设计原则 :

不要过度设计:仅在复杂度需要时引入设计模式。

优先使用语言特性:如 ES6 的 Proxy、class、模块等原生支持。

相关推荐
破茧蝴蝶3 小时前
Python分布式爬虫完整教程
开发语言·python
Autumn_ing3 小时前
Design.md生成UI怎么验收?举例运营后台的颜色、状态和数据核对
前端·ui·ai·设计系统·paico·ai生成ui·design.md
狗凯之家源码网4 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
远离UE44 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience4 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
朱涛的自习室5 小时前
我给 Gemini 4 和 DeepSeek 搞了套「末位淘汰」,AI 干活会更猛吗?(Day 3)
android·前端·后端
栈帧之外5 小时前
【C++】STL之set
开发语言·c++
catchadmin6 小时前
免费可商用 PHP 管理后台 CatchAdmin V5.4.1 发布,新增 MCP 支持
开发语言·php
java1234_小锋7 小时前
【技术专题】HTML5 网页技术 - HTML5简介 & HTML5 文档结构
前端·html·html5
jimy17 小时前
c++的 Type Traits (二、更详细版本)
开发语言·c++