设计模式实践笔记

最近把设计模式又过了一遍,留下几条实际有用的判断。

创建型模式

单例模式

// 基础单例
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    this.data = {};
    Singleton.instance = this;
  }
}

// 使用
const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2);  // true

// 懒汉单例
class LazySingleton {
  constructor() {
    if (!LazySingleton.instance) {
      this.data = {};
      LazySingleton.instance = this;
    }
    return LazySingleton.instance;
  }
}

工厂模式

// 简单工厂
class UserFactory {
  static createUser(type, data) {
    switch (type) {
      case 'admin':
        return new AdminUser(data);
      case 'user':
        return new RegularUser(data);
      case 'guest':
        return new GuestUser(data);
      default:
        throw new Error('Unknown user type');
    }
  }
}

// 使用
const admin = UserFactory.createUser('admin', { name: 'Alice' });
const user = UserFactory.createUser('user', { name: 'Bob' });

// 抽象工厂
class DatabaseConnectionFactory {
  static createConnection(type, config) {
    switch (type) {
      case 'mysql':
        return new MySQLConnection(config);
      case 'postgres':
        return new PostgreSQLConnection(config);
      case 'mongodb':
        return new MongoDBConnection(config);
      default:
        throw new Error('Unknown database type');
    }
  }
}

建造者模式

// 复杂对象创建
class UserBuilder {
  constructor() {
    this.user = {
      id: null,
      name: null,
      email: null,
      phone: null,
      address: null,
      preferences: {}
    };
  }
  
  setId(id) {
    this.user.id = id;
    return this;
  }
  
  setName(name) {
    this.user.name = name;
    return this;
  }
  
  setEmail(email) {
    this.user.email = email;
    return this;
  }
  
  setPhone(phone) {
    this.user.phone = phone;
    return this;
  }
  
  setAddress(address) {
    this.user.address = address;
    return this;
  }
  
  addPreference(key, value) {
    this.user.preferences[key] = value;
    return this;
  }
  
  build() {
    if (!this.user.name || !this.user.email) {
      throw new Error('Name and email are required');
    }
    return this.user;
  }
}

// 使用
const user = new UserBuilder()
  .setId(1)
  .setName('Alice')
  .setEmail('[email protected]')
  .setPhone('1234567890')
  .addPreference('theme', 'dark')
  .build();

结构型模式

适配器模式

// 旧接口
class LegacyPaymentSystem {
  processPayment(amount, cardNumber, expiry, cvv) {
    console.log(`Processing payment of $${amount}`);
    return { success: true, transactionId: '12345' };
  }
}

// 新接口
class ModernPaymentInterface {
  pay(amount, paymentMethod) {
    throw new Error('Method not implemented');
  }
}

// 适配器
class PaymentAdapter extends ModernPaymentInterface {
  constructor(legacySystem) {
    super();
    this.legacySystem = legacySystem;
  }
  
  pay(amount, paymentMethod) {
    const { cardNumber, expiry, cvv } = paymentMethod;
    return this.legacySystem.processPayment(amount, cardNumber, expiry, cvv);
  }
}

// 使用
const legacySystem = new LegacyPaymentSystem();
const adapter = new PaymentAdapter(legacySystem);

const result = adapter.pay(100, {
  cardNumber: '4111111111111111',
  expiry: '12/25',
  cvv: '123'
});

装饰器模式

// 基础组件
class Coffee {
  cost() {
    return 2;
  }
  
  description() {
    return 'Coffee';
  }
}

// 装饰器
class CoffeeDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  
  cost() {
    return this.coffee.cost();
  }
  
  description() {
    return this.coffee.description();
  }
}

// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
  cost() {
    return this.coffee.cost() + 0.5;
  }
  
  description() {
    return this.coffee.description() + ', Milk';
  }
}

class SugarDecorator extends CoffeeDecorator {
  cost() {
    return this.coffee.cost() + 0.25;
  }
  
  description() {
    return this.coffee.description() + ', Sugar';
  }
}

// 使用
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);

console.log(coffee.cost());  // 2.75
console.log(coffee.description());  // Coffee, Milk, Sugar

代理模式

// 代理对象
class ImageProxy {
  constructor(filename) {
    this.filename = filename;
    this.image = null;
  }
  
  display() {
    if (!this.image) {
      this.image = new HighResImage(this.filename);
      this.image.load();
    }
    this.image.display();
  }
}

// 实际对象
class HighResImage {
  constructor(filename) {
    this.filename = filename;
  }
  
  load() {
    console.log(`Loading image: ${this.filename}`);
  }
  
  display() {
    console.log(`Displaying image: ${this.filename}`);
  }
}

// 使用
const image = new ImageProxy('large_image.jpg');
image.display();  // 第一次加载
image.display();  // 使用缓存的图像

行为型模式

观察者模式

// 主题
class Subject {
  constructor() {
    this.observers = [];
  }
  
  subscribe(observer) {
    this.observers.push(observer);
  }
  
  unsubscribe(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }
  
  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

// 观察者
class Observer {
  constructor(name) {
    this.name = name;
  }
  
  update(data) {
    console.log(`${this.name} received:`, data);
  }
}

// 使用
const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');

subject.subscribe(observer1);
subject.subscribe(observer2);

subject.notify({ message: 'Hello' });

策略模式

// 策略接口
class SortStrategy {
  sort(array) {
    throw new Error('Method not implemented');
  }
}

// 具体策略
class BubbleSort extends SortStrategy {
  sort(array) {
    const n = array.length;
    for (let i = 0; i < n - 1; i++) {
      for (let j = 0; j < n - i - 1; j++) {
        if (array[j] > array[j + 1]) {
          [array[j], array[j + 1]] = [array[j + 1], array[j]];
        }
      }
    }
    return array;
  }
}

class QuickSort extends SortStrategy {
  sort(array) {
    if (array.length <= 1) return array;
    
    const pivot = array[0];
    const left = array.slice(1).filter(x => x <= pivot);
    const right = array.slice(1).filter(x => x > pivot);
    
    return [
      ...this.sort(left),
      pivot,
      ...this.sort(right)
    ];
  }
}

// 上下文
class SortContext {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  sort(array) {
    return this.strategy.sort(array);
  }
}

// 使用
const context = new SortContext(new BubbleSort());
const sorted1 = context.sort([5, 2, 9, 1, 5, 6]);

context.setStrategy(new QuickSort());
const sorted2 = context.sort([5, 2, 9, 1, 5, 6]);

命令模式

// 命令接口
class Command {
  execute() {
    throw new Error('Method not implemented');
  }
  
  undo() {
    throw new Error('Method not implemented');
  }
}

// 具体命令
class AddCommand extends Command {
  constructor(receiver, value) {
    super();
    this.receiver = receiver;
    this.value = value;
  }
  
  execute() {
    this.receiver.add(this.value);
  }
  
  undo() {
    this.receiver.subtract(this.value);
  }
}

class SubtractCommand extends Command {
  constructor(receiver, value) {
    super();
    this.receiver = receiver;
    this.value = value;
  }
  
  execute() {
    this.receiver.subtract(this.value);
  }
  
  undo() {
    this.receiver.add(this.value);
  }
}

// 接收者
class Calculator {
  constructor() {
    this.value = 0;
  }
  
  add(value) {
    this.value += value;
  }
  
  subtract(value) {
    this.value -= value;
  }
  
  getValue() {
    return this.value;
  }
}

// 调用者
class Invoker {
  constructor() {
    this.commands = [];
  }
  
  executeCommand(command) {
    command.execute();
    this.commands.push(command);
  }
  
  undo() {
    if (this.commands.length > 0) {
      const command = this.commands.pop();
      command.undo();
    }
  }
}

// 使用
const calculator = new Calculator();
const invoker = new Invoker();

invoker.executeCommand(new AddCommand(calculator, 10));
invoker.executeCommand(new AddCommand(calculator, 5));
invoker.executeCommand(new SubtractCommand(calculator, 3));

console.log(calculator.getValue());  // 12

invoker.undo();
console.log(calculator.getValue());  // 9

踩过的坑

坑一:过度使用设计模式

为了使用设计模式而使用模式。

解决:只在需要时使用模式。

// 不需要设计模式
function add(a, b) {
  return a + b;
}

// 需要设计模式:复杂对象创建
const user = new UserBuilder()
  .setId(1)
  .setName('Alice')
  .build();

坑二:设计模式选择不当

选择了不适合的设计模式。

解决:理解模式的适用场景。

// 错误:使用单例管理状态
class StateManager {
  constructor() {
    if (StateManager.instance) {
      return StateManager.instance;
    }
    this.state = {};
    StateManager.instance = this;
  }
}

// 正确:使用状态管理库
import { createStore } from 'redux';

const store = createStore(reducer);

坑三:模式实现不完整

只实现了模式的一部分,导致问题。

解决:完整实现模式的各个方面。

// 完整的观察者模式
class Observable {
  constructor() {
    this.observers = [];
  }
  
  subscribe(observer) {
    const observerId = Symbol('observer');
    this.observers.push({ id: observerId, observer });
    
    // 返回取消订阅的函数
    return () => {
      this.observers = this.observers.filter(
        obs => obs.id !== observerId
      );
    };
  }
  
  notify(data) {
    this.observers.forEach(({ observer }) => {
      try {
        observer.update(data);
      } catch (error) {
        console.error('Observer error:', error);
      }
    });
  }
}

写在最后

设计模式这东西,不只是模板,是思维。

解决了

  • 代码复用
  • 可维护性
  • 扩展性

带来了

  • 复杂度增加
  • 学习成本
  • 过度设计

使用之前先评估:

  • 问题复杂度
  • 团队能力
  • 项目规模
  • 性能要求

不是所有问题都需要设计模式,有时候简单解决更好。


这次设计模式学习花了一个月,从理论到实践。学习完成后,代码质量提升明显,团队协作效率提升了 30%。

可用性说明:本文发布于 2021 年 7 月,距今已超过五年。文中涉及的软件版本、接口、下载地址、命令参数和操作界面可能已经发生变化,部分方案在当前环境下可能失效。请结合官方最新文档核对后再操作,生产环境使用前务必先行验证。

版权声明: 本文首发于 指尖魔法屋-设计模式实践笔记https://blog.thinkmoon.cn/post/97-design-patterns-theory-practice-complete-guide/) 转载或引用必须申明原指尖魔法屋来源及源地址!