正在加载,请稍候…

TypeScript 设计模式:工厂、观察者、策略等

在 TypeScript 中实现经典 GoF 设计模式,学习何时以及如何应用工厂、观察者、策略、装饰器和仓储模式。

Design Patterns in TypeScript: Factory, Observer, Strategy, and More

TypeScript 设计模式:实用指南

工厂模式

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

interface Logger {
  log(message: string): void;
  error(message: string): void;
}

class ConsoleLogger implements Logger {
  log(msg: string) { console.log(`[LOG] ${msg}`); }
  error(msg: string) { console.error(`[ERR] ${msg}`); }
}

class FileLogger implements Logger {
  constructor(private path: string) {}
  log(msg: string) { /* 写入文件 */ }
  error(msg: string) { /* 写入文件 */ }
}

class LoggerFactory {
  static create(type: 'console' | 'file', options?: { path?: string }): Logger {
    switch (type) {
      case 'console': return new ConsoleLogger();
      case 'file': return new FileLogger(options?.path ?? 'app.log');
      default: throw new Error(`Unknown logger type: ${type}`);
    }
  }
}

const logger = LoggerFactory.create('console');

Design Patterns in TypeScript: Factory, Observer, Strategy, and More illustration

观察者模式

定义订阅机制以通知对象事件。

type EventMap = Record<string, unknown[]>;

class EventEmitter<T extends EventMap> {
  private listeners = new Map<keyof T, Set<Function>>();

  on<K extends keyof T>(event: K, listener: (...args: T[K]) => void): void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    this.listeners.get(event)!.add(listener);
  }

  off<K extends keyof T>(event: K, listener: Function): void {
    this.listeners.get(event)?.delete(listener);
  }

  emit<K extends keyof T>(event: K, ...args: T[K]): void {
    this.listeners.get(event)?.forEach(l => l(...args));
  }
}

// 使用示例
type OrderEvents = {
  created: [order: Order];
  shipped: [orderId: string, trackingCode: string];
  delivered: [orderId: string];
};

class OrderService extends EventEmitter<OrderEvents> {
  async createOrder(data: CreateOrderDto): Promise<Order> {
    const order = await this.db.create(data);
    this.emit('created', order); // 通知监听者
    return order;
  }
}

orderService.on('created', (order) => emailService.sendConfirmation(order));
orderService.on('shipped', (id, tracking) => notificationService.notify(id, tracking));

Design Patterns in TypeScript: Factory, Observer, Strategy, and More illustration

策略模式

定义一系列算法并使它们可互换。

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

class QuickSort<T> implements SortStrategy<T> {
  sort(data: T[]): T[] {
    if (data.length <= 1) return data;
    const pivot = data[Math.floor(data.length / 2)];
    const left = data.filter(x => x < pivot);
    const mid = data.filter(x => x === pivot);
    const right = data.filter(x => x > pivot);
    return [...this.sort(left), ...mid, ...this.sort(right)];
  }
}

class BubbleSort<T> implements SortStrategy<T> {
  sort(data: T[]): T[] {
    const arr = [...data];
    for (let i = 0; i < arr.length; i++) {
      for (let j = 0; j < arr.length - i - 1; j++) {
        if (arr[j] > arr[j + 1]) [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
    return arr;
  }
}

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

Design Patterns in TypeScript: Factory, Observer, Strategy, and More illustration

装饰器模式

动态地向对象添加行为。

interface TextProcessor {
  process(text: string): string;
}

class PlainText implements TextProcessor {
  process(text: string): string { return text; }
}

abstract class TextDecorator implements TextProcessor {
  constructor(protected wrapped: TextProcessor) {}
  process(text: string): string { return this.wrapped.process(text); }
}

class UpperCaseDecorator extends TextDecorator {
  process(text: string): string { return super.process(text).toUpperCase(); }
}

class TrimDecorator extends TextDecorator {
  process(text: string): string { return super.process(text).trim(); }
}

// 组合装饰器
const processor = new UpperCaseDecorator(new TrimDecorator(new PlainText()));
processor.process('  hello world  '); // 'HELLO WORLD'

仓储模式

将数据访问抽象到接口之后。

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

class PostgresUserRepository implements UserRepository {
  constructor(private db: Database) {}

  async findById(id: string): Promise<User | null> {
    const rows = await this.db.query('SELECT * FROM users WHERE id = $1', [id]);
    return rows[0] ? this.mapToUser(rows[0]) : null;
  }

  async save(user: User): Promise<User> {
    const result = await this.db.query(
      'INSERT INTO users (id, email, name) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET ...',
      [user.id, user.email, user.name]
    );
    return this.mapToUser(result[0]);
  }

  private mapToUser(row: Record<string, unknown>): User {
    return { id: row.id as string, email: row.email as string, name: row.name as string };
  }
}

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

模式是解决重复问题的方案,而不是强加于每个问题的蓝图。