一、为什么我们需要设计模式?

就像建筑工地需要施工图纸,厨房炒菜需要标准化流程,程序员写代码同样需要经过验证的解决方案。设计模式就是我们应对常见开发难题的"标准答案集",今天我们就用真实的开发场景来看看JavaScript中四个超实用的设计模式。


二、永葆青春的守护者——单例模式

2.1 模式定义与生活场景

单例模式确保一个类只有一个实例,就像公司的CEO职位,整个组织只需要一位最高决策者。在前端开发中常见于全局状态管理、日志记录器等场景。

2.2 技术实现与代码示例

// 技术栈:原生JavaScript
class CacheManager {
  constructor() {
    if (!CacheManager.instance) {
      this._cache = new Map();
      CacheManager.instance = this;
    }
    return CacheManager.instance;
  }

  set(key, value) {
    this._cache.set(key, value);
  }

  get(key) {
    return this._cache.get(key);
  }
}

// 实际使用示例
const cache1 = new CacheManager();
const cache2 = new CacheManager();

console.log(cache1 === cache2); // true

cache1.set('userSession', { id: 123, token: 'abc' });
console.log(cache2.get('userSession')); // 获取到相同数据

2.3 关键要点分析

  • 应用场景:浏览器本地存储包装器、全局状态容器、WebSocket连接管理
  • 优点:减少内存消耗、避免资源冲突
  • 缺点:违反单一职责原则、测试难度增加
  • 注意事项:警惕线程安全问题(在Node.js中)、避免滥用导致代码耦合

三、批量生产的艺术——工厂模式

3.1 模式理解与生活比喻

想象你去肯德基点餐,不需要关心炸鸡怎么做,只需要说"要个汉堡套餐",这就是工厂模式的精髓——将对象创建与使用解耦。

3.2 完整实现示例

// 技术栈:ES6+
class UIComponentFactory {
  createComponent(type) {
    switch(type) {
      case 'button':
        return new Button('Submit', 'btn-primary');
      case 'input':
        return new Input('text', 'username');
      case 'modal':
        return new Modal('Warning', '确认删除吗?');
      default:
        throw new Error('未知组件类型');
    }
  }
}

class Button {
  constructor(text, style) {
    this.element = document.createElement('button');
    this.element.textContent = text;
    this.element.className = style;
  }
}

// 使用示例
const factory = new UIComponentFactory();
const loginButton = factory.createComponent('button');
document.body.appendChild(loginButton.element);

3.3 扩展应用探讨

关联技术:配合策略模式实现动态工厂,结合TypeScript实现类型安全的工厂方法:

interface Component {}
class Button implements Component {}
class Input implements Component {}

type ComponentType = 'button' | 'input';

function createComponent<T extends ComponentType>(type: T): Component {
  // 实现逻辑
}

四、事件驱动的核心——观察者模式

4.1 现实世界中的消息通知

就像快递柜的到货通知,观察者模式让多个对象能订阅某个主体的状态变化。最适合处理事件驱动型架构。

4.2 完整实现与实战示例

// 技术栈:现代JavaScript
class NewsPublisher {
  constructor() {
    this.subscribers = [];
    this.latestNews = null;
  }

  subscribe(observer) {
    this.subscribers.push(observer);
  }

  unsubscribe(observer) {
    this.subscribers = this.subscribers.filter(sub => sub !== observer);
  }

  notify(news) {
    this.latestNews = news;
    this.subscribers.forEach(observer => observer.update(news));
  }
}

class EmailSubscriber {
  update(news) {
    console.log(`[邮件通知] 最新消息:${news.title}`);
    // 实际发送邮件逻辑
  }
}

// 使用示例
const publisher = new NewsPublisher();
const emailService = new EmailSubscriber();

publisher.subscribe(emailService);
publisher.notify({ 
  title: 'JavaScript新特性发布', 
  content: 'ECMAScript 2023正式发布...'
});

4.3 性能优化技巧

  • 使用WeakMap避免内存泄漏
  • 添加防抖机制处理高频更新
  • 实现异步通知队列

五、高级解耦术——依赖注入

5.1 模式本质解读

依赖注入就像电脑的USB接口,把需要的外部服务"插"进使用它们的模块。特别适合大型应用解耦。

5.2 基础到进阶实现

基础版实现

// 技术栈:ES6 Classes
class UserService {
  constructor(httpClient) {
    this.http = httpClient;
  }

  getUser(id) {
    return this.http.get(`/users/${id}`);
  }
}

// 使用示例
const axios = require('axios');
const userService = new UserService(axios);

进阶容器实现

class DIContainer {
  constructor() {
    this.services = {};
  }

  register(name, creator) {
    this.services[name] = creator;
  }

  resolve(name) {
    const creator = this.services[name];
    return creator(this);
  }
}

// 配置注册
const container = new DIContainer();
container.register('http', () => axios.create({ timeout: 5000 }));
container.register('userService', (di) => 
  new UserService(di.resolve('http')));

六、全景对比与模式选择指南

6.1 四模式的横向对比表

模式名称 适用场景 复杂度 可测试性
单例模式 全局资源管理 ★★☆ ★★☆
工厂模式 复杂对象创建 ★★★ ★★★★
观察者模式 事件驱动系统 ★★★☆ ★★★☆
依赖注入 大型应用解耦 ★★★★ ★★★★★

6.2 选用决策树

  1. 需要控制实例数量吗? → 单例
  2. 对象创建过程复杂吗? → 工厂
  3. 需要解耦消息收发双方? → 观察者
  4. 模块依赖关系复杂? → 依赖注入

七、避坑指南与最佳实践

7.1 常见误区纠正

  • 单例模式的竞态条件问题(服务端场景)
  • 观察者模式的内存泄漏(及时取消订阅)
  • 过度设计导致的模式滥用(适合的才是最好的)

7.2 性能优化风向标

  • 单例的懒加载初始化
  • 工厂模式的对象池技术
  • 观察者的批量通知机制
  • DI容器的依赖缓存策略

八、现代生态中的模式演化

8.1 框架中的模式身影

  • React Context(单例模式实现)
  • Vue Composition API(依赖注入思想)
  • Redux(观察者模式变种)

8.2 TypeScript强化版实现

// 泛型工厂示例
interface Animal {
  speak(): void;
}

class Dog implements Animal { /*...*/ }

class AnimalFactory {
  create<T extends Animal>(type: new () => T): T {
    return new type();
  }
}

九、文章总结

选择设计模式就像选择厨房工具,了解每种刀具的特性才能游刃有余。本文演示的四种模式构成了现代JavaScript应用的骨架,但真正重要的是理解其设计哲学。记住:没有最好的模式,只有最合适的解决方案。