正在加载,请稍候…

JavaScript 和 TypeScript 中的软件设计模式:实用指南

通过实用的 JavaScript/TypeScript 示例学习最常用的软件设计模式:单例、工厂、观察者、策略、装饰器、仓库等模式。

JavaScript 和 TypeScript 中的软件设计模式:实用指南

在现代 JavaScript 中哪些设计模式真正重要?

原始的 GoF《设计模式》一书(1994 年)用 C++ 描述了 23 种模式。其中许多模式解决的问题在 JavaScript 中处理方式不同——动态类型、一等函数和基于原型的对象显著改变了计算方式。

本指南聚焦于那些在 2026 年能真正改善实际代码库的模式:你会在流行框架、技术面试以及你实际维护的代码库中遇到的模式。

JavaScript 和 TypeScript 中的软件设计模式:实用指南 插图

创建型模式

单例模式(Singleton)

确保一个类只有一个实例:

class DatabaseConnection {
  private static instance: DatabaseConnection | null = null;
  private pool: Pool;
  
  private constructor() {
    this.pool = new Pool({
      connectionString: process.env.DATABASE_URL,
      max: 20,
    });
  }
  
  // 获取实例的唯一方式
  static getInstance(): DatabaseConnection {
    if (!DatabaseConnection.instance) {
      DatabaseConnection.instance = new DatabaseConnection();
    }
    return DatabaseConnection.instance;
  }
  
  async query(sql: string, params?: unknown[]) {
    return this.pool.query(sql, params);
  }
}

// 使用——总是获取同一个实例
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true

// 现代替代方案:模块级单例(更简单)
// db.ts
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export default pool; // ES 模块默认就是单例!

工厂方法模式(Factory Method)

无需指定具体类即可创建对象:

interface Notifier {
  send(to: string, message: string): Promise<void>;
}

class EmailNotifier implements Notifier {
  async send(to: string, message: string) {
    await sendEmail({ to, subject: 'Notification', body: message });
  }
}

class SMSNotifier implements Notifier {
  async send(to: string, message: string) {
    await sendSMS({ phone: to, text: message });
  }
}

class SlackNotifier implements Notifier {
  async send(to: string, message: string) {
    await postToSlack({ channel: to, text: message });
  }
}

// 工厂
class NotifierFactory {
  static create(type: 'email' | 'sms' | 'slack'): Notifier {
    const map = {
      email: EmailNotifier,
      sms: SMSNotifier,
      slack: SlackNotifier,
    };
    
    const NotifierClass = map[type];
    if (!NotifierClass) throw new Error(`Unknown notifier type: ${type}`);
    
    return new NotifierClass();
  }
}

// 使用——客户端不知道创建了哪个类
const notifier = NotifierFactory.create(process.env.NOTIFIER_TYPE as 'email');
await notifier.send('alice@example.com', 'Your order is ready!');

JavaScript 和 TypeScript 中的软件设计模式:实用指南 插图

建造者模式(Builder)

逐步构建复杂对象:

class QueryBuilder {
  private table = '';
  private conditions: string[] = [];
  private columns = '*';
  private limitVal?: number;
  private orderByCol?: string;
  private orderByDir = 'ASC';
  private params: unknown[] = [];

  from(table: string) {
    this.table = table;
    return this; // 方法链
  }
  
  select(...columns: string[]) {
    this.columns = columns.join(', ');
    return this;
  }
  
  where(condition: string, ...params: unknown[]) {
    this.params.push(...params);
    this.conditions.push(condition.replace('?', `${this.params.length}`));
    return this;
  }
  
  limit(n: number) {
    this.limitVal = n;
    return this;
  }
  
  orderBy(column: string, direction: 'ASC' | 'DESC' = 'ASC') {
    this.orderByCol = column;
    this.orderByDir = direction;
    return this;
  }
  
  build(): { sql: string; params: unknown[] } {
    let sql = `SELECT ${this.columns} FROM ${this.table}`;
    
    if (this.conditions.length > 0) {
      sql += ` WHERE ${this.conditions.join(' AND ')}`;
    }
    if (this.orderByCol) {
      sql += ` ORDER BY ${this.orderByCol} ${this.orderByDir}`;
    }
    if (this.limitVal !== undefined) {
      sql += ` LIMIT ${this.limitVal}`;
    }
    
    return { sql, params: this.params };
  }
}

// 清晰、可读的查询构建
const { sql, params } = new QueryBuilder()
  .from('users')
  .select('id', 'name', 'email')
  .where('country = ?', 'US')
  .where('created_at > ?', new Date('2026-01-01'))
  .orderBy('name')
  .limit(20)
  .build();

结构型模式

装饰器模式(Decorator)

在不修改对象的情况下为其添加行为:

interface UserService {
  findById(id: string): Promise<User | null>;
  create(data: CreateUserDto): Promise<User>;
}

// 基础实现
class UserServiceImpl implements UserService {
  async findById(id: string) {
    return db.query('SELECT * FROM users WHERE id = $1', [id]);
  }
  async create(data: CreateUserDto) {
    return db.query('INSERT INTO users ...', [data.name, data.email]);
  }
}

// 缓存装饰器(包装服务)
class CachedUserService implements UserService {
  constructor(
    private service: UserService,
    private cache: Redis
  ) {}
  
  async findById(id: string) {
    const cached = await this.cache.get(`user:${id}`);
    if (cached) return JSON.parse(cached);
    
    const user = await this.service.findById(id);
    if (user) await this.cache.setEx(`user:${id}`, 3600, JSON.stringify(user));
    return user;
  }
  
  async create(data: CreateUserDto) {
    const user = await this.service.create(data);
    await this.cache.setEx(`user:${user.id}`, 3600, JSON.stringify(user));
    return user;
  }
}

// 日志装饰器
class LoggingUserService implements UserService {
  constructor(private service: UserService, private logger: Logger) {}
  
  async findById(id: string) {
    const start = Date.now();
    const user = await this.service.findById(id);
    this.logger.info({ id, duration: Date.now() - start }, 'findById');
    return user;
  }
  
  // ... create 方法类似
}

// 组合:Logging(Caching(Base))
const userService: UserService = 
  new LoggingUserService(
    new CachedUserService(
      new UserServiceImpl(),
      redis
    ),
    logger
  );

JavaScript 和 TypeScript 中的软件设计模式:实用指南 插图

行为型模式

观察者模式(Observer)(事件发射器)

type EventMap = {
  'user.created': { userId: string; email: string };
  'order.placed': { orderId: string; total: number };
  'payment.failed': { orderId: string; reason: string };
};

class TypedEventBus {
  private listeners = new Map<string, Set<Function>>();
  
  on<K extends keyof EventMap>(
    event: K,
    listener: (data: EventMap[K]) => void
  ): () => void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    this.listeners.get(event)!.add(listener);
    
    // 返回取消订阅函数
    return () => this.listeners.get(event)?.delete(listener);
  }
  
  emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {
    this.listeners.get(event)?.forEach(listener => {
      listener(data);
    });
  }
}

const bus = new TypedEventBus();

// 订阅(类型安全!)
const unsubscribe = bus.on('user.created', ({ userId, email }) => {
  sendWelcomeEmail(email);
});

// 发射
bus.emit('user.created', { userId: '123', email: 'alice@example.com' });

// 清理
unsubscribe();

策略模式(Strategy)

定义一系列算法,使它们可以互换:

interface SortStrategy<T> {
  sort(data: T[]): T[];
}

class QuickSort<T> implements SortStrategy<T> {
  sort(data: T[]): T[] {
    return [...data].sort(); // 简化
  }
}

class MergeSort<T> implements SortStrategy<T> {
  sort(data: T[]): T[] {
    // 归并排序实现
    return [...data].sort();
  }
}

class DataProcessor<T> {
  constructor(private strategy: SortStrategy<T>) {}
  
  setStrategy(strategy: SortStrategy<T>) {
    this.strategy = strategy;
  }
  
  process(data: T[]): T[] {
    return this.strategy.sort(data);
  }
}

// 运行时切换算法
const processor = new DataProcessor(new QuickSort<number>());
processor.process([3, 1, 4, 1, 5]);

// 对于大数据集,切换到归并排序
if (data.length > 10000) {
  processor.setStrategy(new MergeSort<number>());
}

仓库模式(Repository Pattern)

通过接口抽象数据访问:

interface UserRepository {
  findById(id: string): Promise<User | null>;
  findByEmail(email: string): Promise<User | null>;
  save(user: User): Promise<User>;
  delete(id: string): Promise<void>;
}

// PostgreSQL 实现
class PgUserRepository implements UserRepository {
  constructor(private pool: Pool) {}
  
  async findById(id: string) {
    const result = await this.pool.query(
      'SELECT * FROM users WHERE id = $1', [id]
    );
    return result.rows[0] ?? null;
  }
  
  async findByEmail(email: string) {
    const result = await this.pool.query(
      'SELECT * FROM users WHERE email = $1', [email]
    );
    return result.rows[0] ?? null;
  }
  
  async save(user: User) {
    const result = await this.pool.query(
      'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
      [user.name, user.email]
    );
    return result.rows[0];
  }
  
  async delete(id: string) {
    await this.pool.query('DELETE FROM users WHERE id = $1', [id]);
  }
}

// 用于测试的内存实现!
class InMemoryUserRepository implements UserRepository {
  private users = new Map<string, User>();
  
  async findById(id: string) {
    return this.users.get(id) ?? null;
  }
  
  async findByEmail(email: string) {
    return [...this.users.values()].find(u => u.email === email) ?? null;
  }
  
  async save(user: User) {
    const saved = { ...user, id: user.id ?? crypto.randomUUID() };
    this.users.set(saved.id, saved);
    return saved;
  }
  
  async delete(id: string) {
    this.users.delete(id);
  }
}

// 服务使用接口——不知道具体实现
class UserService {
  constructor(private users: UserRepository) {}
  
  async register(email: string, password: string): Promise<User> {
    const existing = await this.users.findByEmail(email);
    if (existing) throw new Error('Email already registered');
    
    return this.users.save({ email, passwordHash: hashPassword(password) });
  }
}

// 生产环境
const userService = new UserService(new PgUserRepository(pool));

// 测试——快速,无需数据库
const userService = new UserService(new InMemoryUserRepository());

应避免的反模式

// ❌ 上帝对象——一个类做所有事
class Application {
  handleLogin() { ... }
  sendEmail() { ... }
  processPayment() { ... }
  generatePDF() { ... }
  // 还有 50 多个方法...
}

// ✅ 单一职责——每个类只有一个职责

// ❌ 贫血领域模型——对象只是数据容器
class User {
  name: string;
  email: string;
  // 没有行为——所有逻辑都在 UserService 中
}

// ✅ 富领域模型——行为与数据共存
class User {
  private passwordHash: string;
  
  verifyPassword(password: string): boolean {
    return bcrypt.compare(password, this.passwordHash);
  }
  
  changeEmail(newEmail: string): void {
    if (!isValidEmail(newEmail)) throw new Error('Invalid email');
    this.email = newEmail;
    this.emit('email.changed', { userId: this.id });
  }
}

→ 使用 JSON 查看器 可视化和检查复杂数据结构。