设计模式了解

4 阅读16分钟

设计模式

核心原则

SOLID 原则

  • 单一职责原则(Single Responsibility Principle):一个类应该只有一个引起它变化的原因。
  • 开闭原则(Open-Closed Principle):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
  • 里氏替换原则(Liskov Substitution Principle):子类必须能够替换父类且不影响程序的正确性。
  • 依赖倒置原则(Dependency Inversion Principle):高层模块不应该依赖于低层模块,而应该依赖于抽象。
  • 接口隔离原则(Interface Segregation Principle):客户端不应该依赖于它不需要的接口。

其他

  • 迪米特法则(Law of Demeter) - 也叫最少知识原则(Least Knowledge Principle):一个对象应该对其他对象有最少的了解,只和直接的朋友通信,而不和陌生人通信。
  • 合成/聚合复用原则(Composite/Aggregate Reuse Principle):优先使用对象组合/聚合,而不是类继承。

分类:

  • 创建型模式
  • 结构型模式
  • 行为型模式

创建型模式

创建对象的同时,可以隐藏创建对象的逻辑,使代码更加灵活。

不适用 new 关键字创建对象的场景。

使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活

  • 工厂模式(Factory Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 单例模式(Singleton Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)

单例模式

确保一个类只有一个实例,并提供一个全局访问点。

常用于需要频繁创建和销毁的对象,或者需要协调对共享资源的访问的场景。

解释: 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

// 我们需要一个辅助方法
// 该方法可以将一个类转为一个单例类
function getSingle(fn) {
  let instance = null; // 用于存储唯一的实例对象
  return function (...args) {
    if (instance !== null) { // 进入此分支,说明当前已经存在实例对象
      return instance;
    }
    // 没有进入上面的 if,说明当前不存在实例对象
    instance = new fn(...args);
    return instance;
  };
}

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hello ${this.name}`;
  }
}

const p1 = new Person("John");
const p2 = new Person("John");
console.log(p1 === p2); // false

const SinglePerson = getSingle(Person);
const p3 = new SinglePerson("John");
const p4 = new SinglePerson("John");
console.log(p3 === p4); // true
export class Person {
  private static instance: Person; // 静态属性,用于存储全局唯一的实例
  private name: string;
  private age: number;

  // 私有化了构造方法,外部无法通过 new Person() 来创建实例
  private constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public static getInstance(name: string, age: number): Person {
    // 进行单例的判断
    if (Person.instance === null) {
      Person.instance = new Person(name, age);
    }
    // 没有进入上面的分支,说明已经 new 过一次,实例对象存储在 instance 中,直接返回即可
    return Person.instance;
  }

  sayHello(){
    console.log("Hello");
  }
}

const p1 = Person.getInstance("Tom", 18);
const p2 = Person.getInstance("John", 18);
console.log(p1 === p2); // true

工厂模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

常用于需要根据不同条件创建不同对象的场景,避免在代码中出现大量的 new 操作和 if-else 判断。

解释: 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。它专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

// 产品类
class Product {
  constructor(name) {
    this.name = name;
  }
  operation() {
    console.log(`${this.name} operation`);
  }
}

// 具体产品
class ConcreteProductA extends Product {
  constructor() {
    super("ConcreteProductA");
  }
}

class ConcreteProductB extends Product {
  constructor() {
    super("ConcreteProductB");
  }
}

// 工厂类
class SimpleFactory {
  static createProduct(type) {
    switch (type) {
      case 'A':
        return new ConcreteProductA();
      case 'B':
        return new ConcreteProductB();
      default:
        throw new Error('Unknown product type');
    }
  }
}

// 使用
const productA = SimpleFactory.createProduct('A');
const productB = SimpleFactory.createProduct('B');
productA.operation(); // ConcreteProductA operation
productB.operation(); // ConcreteProductB operation

实际应用场景: 不同类型的用户(普通用户、VIP用户)创建不同的折扣策略。

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

常用于需要创建多个产品族(比如不同风格的UI组件)的场景。

解释: 抽象工厂模式是工厂模式的升级版,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

// 抽象产品A
class AbstractProductA {
  operationA() {}
}

// 具体产品A1
class ProductA1 extends AbstractProductA {
  operationA() {
    console.log("ProductA1 operation");
  }
}

// 具体产品A2
class ProductA2 extends AbstractProductA {
  operationA() {
    console.log("ProductA2 operation");
  }
}

// 抽象产品B
class AbstractProductB {
  operationB() {}
}

// 具体产品B1
class ProductB1 extends AbstractProductB {
  operationB() {
    console.log("ProductB1 operation");
  }
}

// 具体产品B2
class ProductB2 extends AbstractProductB {
  operationB() {
    console.log("ProductB2 operation");
  }
}

// 抽象工厂
class AbstractFactory {
  createProductA() {}
  createProductB() {}
}

// 具体工厂1
class Factory1 extends AbstractFactory {
  createProductA() {
    return new ProductA1();
  }
  createProductB() {
    return new ProductB1();
  }
}

// 具体工厂2
class Factory2 extends AbstractFactory {
  createProductA() {
    return new ProductA2();
  }
  createProductB() {
    return new ProductB2();
  }
}

// 使用
const factory1 = new Factory1();
const productA1 = factory1.createProductA();
const productB1 = factory1.createProductB();
productA1.operationA(); // ProductA1 operation
productB1.operationB(); // ProductB1 operation

const factory2 = new Factory2();
const productA2 = factory2.createProductA();
const productB2 = factory2.createProductB();
productA2.operationA(); // ProductA2 operation
productB2.operationB(); // ProductB2 operation

实际应用场景: 跨平台UI组件库,同一套设计需要适配不同平台(Web、Mobile、Desktop)。

建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

常用于需要创建包含多个组成部分的复杂对象的场景。

解释: 建造者模式可以将复杂对象的构建过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

// 产品类
class Computer {
  constructor() {
    this.cpu = null;
    this.memory = null;
    this.disk = null;
  }

  showInfo() {
    console.log(`CPU: ${this.cpu}, Memory: ${this.memory}, Disk: ${this.disk}`);
  }
}

// 建造者类
class ComputerBuilder {
  constructor() {
    this.computer = new Computer();
  }

  setCpu(cpu) {
    this.computer.cpu = cpu;
    return this;
  }

  setMemory(memory) {
    this.computer.memory = memory;
    return this;
  }

  setDisk(disk) {
    this.computer.disk = disk;
    return this;
  }

  getResult() {
    return this.computer;
  }
}

// 指挥者类(可选)
class ComputerDirector {
  static buildGamingComputer() {
    return new ComputerBuilder()
      .setCpu("Intel Core i9")
      .setMemory("32GB")
      .setDisk("1TB SSD")
      .getResult();
  }

  static buildOfficeComputer() {
    return new ComputerBuilder()
      .setCpu("Intel Core i5")
      .setMemory("16GB")
      .setDisk("512GB SSD")
      .getResult();
  }
}

// 使用
const gamingComputer = ComputerDirector.buildGamingComputer();
const officeComputer = ComputerDirector.buildOfficeComputer();

gamingComputer.showInfo(); // CPU: Intel Core i9, Memory: 32GB, Disk: 1TB SSD
officeComputer.showInfo(); // CPU: Intel Core i5, Memory: 16GB, Disk: 512GB SSD

// 也可以自定义构建
const customComputer = new ComputerBuilder()
  .setCpu("AMD Ryzen 7")
  .setMemory("64GB")
  .setDisk("2TB SSD")
  .getResult();
customComputer.showInfo(); // CPU: AMD Ryzen 7, Memory: 64GB, Disk: 2TB SSD

实际应用场景: 创建复杂的表单、配置对象、SQL查询构建器等。

原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

常用于需要复制复杂对象的场景,避免重复的初始化操作。

解释: 原型模式就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

// 原型类
class Prototype {
  constructor(name) {
    this.name = name;
  }

  clone() {
    // 浅拷贝
    return Object.create(this);
  }
}

// 具体原型
class ConcretePrototype extends Prototype {
  constructor(name, age) {
    super(name);
    this.age = age;
  }

  clone() {
    // 浅拷贝
    const clone = Object.create(this);
    clone.age = this.age;
    return clone;
  }
}

// 使用
const prototype = new ConcretePrototype("John", 30);
const clone1 = prototype.clone();
const clone2 = prototype.clone();

console.log(clone1.name, clone1.age); // John 30
console.log(clone2.name, clone2.age); // John 30

clone1.name = "Mike";
clone1.age = 25;

console.log(clone1.name, clone1.age); // Mike 25
console.log(clone2.name, clone2.age); // John 30
console.log(prototype.name, prototype.age); // John 30

实际应用场景: 复制复杂的对象结构,如图形编辑器中的图形对象、游戏中的角色等。


结构型模式

关注类和对象的组合,以形成更大的结构。

继承的概念被用来组合接口和定义组合对象获得新功能的方式

  • 适配器模式(Adapter Pattern)
  • 装饰器模式(Decorator Pattern)
  • 桥接模式(Bridge Pattern)
  • 代理模式(Proxy Pattern)
  • 外观模式(Facade Pattern)
  • 组合模式(Composite Pattern)
  • 享元模式(Flyweight Pattern)

适配器模式

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

常用于在不改变现有代码的基础上,使不兼容的类能够协同工作。

// 目标接口
class Target {
  request() {
    console.log("Target request");
  }
}

// 被适配的类
class Adaptee {
  specificRequest() {
    console.log("Adaptee specificRequest");
  }
}

// 适配器类
class Adapter extends Target {
  constructor(adaptee) {
    super();
    this.adaptee = adaptee;
  }

  request() {
    this.adaptee.specificRequest();
  }
}

// 使用
const target = new Target();
target.request(); // Target request

const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
adapter.request(); // Adaptee specificRequest

实际应用场景:

  • 第三方API接口适配
  • 不同数据格式的转换
  • 旧系统与新系统的集成

装饰器模式

动态地给一个对象添加一些额外的职责,而不会改变其结构。

常用于在不改变对象自身的基础上,动态地添加功能或行为。

// 被装饰的对象
class Person {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello ${this.name}`);
  }
}

// 装饰器类
class Decorator {
  constructor(person) {
    this.person = person;
  }

  sayHello() {
    console.log("Before sayHello");
    this.person.sayHello();
    console.log("After sayHello");
  }
}

// 使用
const person = new Person("John");
const decorator = new Decorator(person);
decorator.sayHello();
// Before sayHello
// Hello John
// After sayHello

实际应用场景:

  • React中的Higher-Order Components
  • 中间件函数(如Express.js中间件)
  • 动态添加日志、缓存、权限验证等功能

桥接模式

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

常用于需要将抽象和实现解耦的场景,避免由于继承导致的类爆炸。

解释: 桥接模式适用于那些不希望由于继承原因而将抽象和实现固定在一起的情况。

// 实现接口
class DrawingAPI {
  drawCircle(radius, x, y) {}
}

// 具体实现1
class DrawingAPI1 extends DrawingAPI {
  drawCircle(radius, x, y) {
    console.log(`API1.circle at ${x}:${y} radius ${radius}`);
  }
}

// 具体实现2
class DrawingAPI2 extends DrawingAPI {
  drawCircle(radius, x, y) {
    console.log(`API2.circle at ${x}:${y} radius ${radius}`);
  }
}

// 抽象类
class Shape {
  constructor(drawingAPI) {
    this.drawingAPI = drawingAPI;
  }
  draw() {}
  resizeByPercentage(pct) {}
}

// 抽象的具体实现
class CircleShape extends Shape {
  constructor(x, y, radius, drawingAPI) {
    super(drawingAPI);
    this.x = x;
    this.y = y;
    this.radius = radius;
  }

  draw() {
    this.drawingAPI.drawCircle(this.radius, this.x, this.y);
  }

  resizeByPercentage(pct) {
    this.radius *= pct;
  }
}

// 使用
const api1 = new DrawingAPI1();
const api2 = new DrawingAPI2();

const shape1 = new CircleShape(1, 2, 3, api1);
const shape2 = new CircleShape(5, 7, 11, api2);

shape1.draw(); // API1.circle at 1:2 radius 3
shape2.draw(); // API2.circle at 5:7 radius 11

shape1.resizeByPercentage(2.5);
shape1.draw(); // API1.circle at 1:2 radius 7.5

实际应用场景:

  • 跨平台的UI组件(不同操作系统的渲染方式)
  • 数据库驱动抽象
  • 支付系统(不同支付渠道的抽象)

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

常用于需要延迟加载、访问控制、日志记录等场景。

// 真实主题
class RealImage {
  constructor(filename) {
    this.filename = filename;
    this.loadFromDisk();
  }

  display() {
    console.log(`Displaying ${this.filename}`);
  }

  loadFromDisk() {
    console.log(`Loading ${this.filename} from disk`);
  }
}

// 代理类
class ProxyImage {
  constructor(filename) {
    this.filename = filename;
    this.realImage = null;
  }

  display() {
    if (this.realImage === null) {
      this.realImage = new RealImage(this.filename);
    }
    this.realImage.display();
  }
}

// 使用
const proxyImage1 = new ProxyImage("image1.jpg");
const proxyImage2 = new ProxyImage("image2.jpg");

proxyImage1.display(); // Loading image1.jpg from disk, Displaying image1.jpg
proxyImage1.display(); // Displaying image1.jpg (不会重复加载)
proxyImage2.display(); // Loading image2.jpg from disk, Displaying image2.jpg

实际应用场景:

  • 图片懒加载
  • 前端路由守卫
  • 缓存代理
  • 虚拟代理(DOM操作节流)

外观模式

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

常用于简化复杂子系统的访问,降低客户端与子系统的耦合度。

// 子系统A
class SubsystemA {
  operationA() {
    console.log("SubsystemA operation");
  }
}

// 子系统B
class SubsystemB {
  operationB() {
    console.log("SubsystemB operation");
  }
}

// 子系统C
class SubsystemC {
  operationC() {
    console.log("SubsystemC operation");
  }
}

// 外观类
class Facade {
  constructor() {
    this.subsystemA = new SubsystemA();
    this.subsystemB = new SubsystemB();
    this.subsystemC = new SubsystemC();
  }

  operation() {
    this.subsystemA.operationA();
    this.subsystemB.operationB();
    this.subsystemC.operationC();
  }
}

// 使用
const facade = new Facade();
facade.operation();
// SubsystemA operation
// SubsystemB operation
// SubsystemC operation

实际应用场景:

  • DOM操作封装
  • 第三方库的接口封装
  • 复杂业务流程的统一入口

组合模式

将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

常用于需要表示树形结构的场景,如文件系统、菜单等。

// 组件接口
class Component {
  constructor(name) {
    this.name = name;
  }
  add(component) {}
  remove(component) {}
  display(depth) {}
}

// 叶子节点
class Leaf extends Component {
  constructor(name) {
    super(name);
  }

  add(component) {
    console.log("Cannot add to a leaf");
  }

  remove(component) {
    console.log("Cannot remove from a leaf");
  }

  display(depth) {
    console.log(`${"-".repeat(depth)}${this.name}`);
  }
}

// 组合节点
class Composite extends Component {
  constructor(name) {
    super(name);
    this.children = [];
  }

  add(component) {
    this.children.push(component);
  }

  remove(component) {
    const index = this.children.indexOf(component);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }

  display(depth) {
    console.log(`${"-".repeat(depth)}${this.name}`);
    for (const child of this.children) {
      child.display(depth + 2);
    }
  }
}

// 使用
const root = new Composite("Root");
const branch1 = new Composite("Branch 1");
const branch2 = new Composite("Branch 2");
const leaf1 = new Leaf("Leaf 1");
const leaf2 = new Leaf("Leaf 2");
const leaf3 = new Leaf("Leaf 3");

root.add(branch1);
root.add(branch2);
branch1.add(leaf1);
branch1.add(leaf2);
branch2.add(leaf3);

root.display();
// Root
// --Branch 1
// ----Leaf 1
// ----Leaf 2
// --Branch 2
// ----Leaf 3

实际应用场景:

  • 文件系统
  • 组织架构
  • UI组件树(React/Vue组件)
  • 菜单系统

享元模式

运用共享技术有效地支持大量细粒度的对象。

常用于需要创建大量相似对象的场景,通过共享来减少内存占用。

// 享元工厂
class FlyweightFactory {
  constructor() {
    this.flyweights = {};
  }

  getFlyweight(key) {
    if (!this.flyweights[key]) {
      this.flyweights[key] = new ConcreteFlyweight(key);
    }
    return this.flyweights[key];
  }

  getCount() {
    return Object.keys(this.flyweights).length;
  }
}

// 具体享元
class ConcreteFlyweight {
  constructor(intrinsicState) {
    this.intrinsicState = intrinsicState;
  }

  operation(extrinsicState) {
    console.log(`Intrinsic: ${this.intrinsicState}, Extrinsic: ${extrinsicState}`);
  }
}

// 使用
const factory = new FlyweightFactory();

const flyweight1 = factory.getFlyweight("shared");
const flyweight2 = factory.getFlyweight("shared");
const flyweight3 = factory.getFlyweight("unique");

console.log(factory.getCount()); // 2

flyweight1.operation("First call");
flyweight2.operation("Second call");
flyweight3.operation("Third call");
// Intrinsic: shared, Extrinsic: First call
// Intrinsic: shared, Extrinsic: Second call
// Intrinsic: unique, Extrinsic: Third call

实际应用场景:

  • 文本编辑器的字符对象
  • 游戏中的粒子效果
  • 大量相似的数据对象

行为型模式

关注对象之间的通信和协作,以实现特定的行为。

  • 策略模式(Strategy Pattern)
  • 模板方法模式(Template Method Pattern)
  • 观察者模式(Observer Pattern)
  • 迭代器模式(Iterator Pattern)
  • 中介者模式(Mediator Pattern)
  • 状态模式(State Pattern)
  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 备忘录模式(Memento Pattern)
  • 解释器模式(Interpreter Pattern)
  • 访问者模式(Visitor Pattern)

观察者模式

定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

常用于事件处理、异步编程、UI组件之间的通信等场景。

// 观察者
class Observer {
  constructor(name) {
    this.name = name;
  }

  update() {
    console.log(`${this.name} 收到了通知`);
  }
}

// 主题类
class Subject {
  constructor() {
    this.observers = []; // 用于存储所有的观察者对象
  }

  // 用于添加观察者对象
  attach(observer) {
    this.observers.push(observer);
  }

  // 用于删除观察者对象
  detach(observer) {
    this.observers = this.observers.filter((obs) => obs !== observer);
  }

  // 当主题对象的状态发生改变时,调用此方法通知所有的观察者对象
  notify() {
    this.observers.forEach((observer) => observer.update());
  }
}

// 使用
const subject = new Subject();
const observer1 = new Observer("观察者1");
const observer2 = new Observer("观察者2");
const observer3 = new Observer("观察者3");

subject.attach(observer1);
subject.attach(observer2);
subject.attach(observer3);

subject.notify();
// 观察者1 收到了通知
// 观察者2 收到了通知
// 观察者3 收到了通知

subject.detach(observer2);
subject.notify();
// 观察者1 收到了通知
// 观察者3 收到了通知

实际应用场景:

  • 事件监听(addEventListener)
  • Vue/React的响应式系统
  • WebSocket消息推送
  • Redux/Vuex的状态管理

策略模式

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

常用于需要在运行时根据不同条件选择不同算法的场景。

// 策略接口
class Strategy {
  execute(a, b) {}
}

// 具体策略1:加法
class AddStrategy extends Strategy {
  execute(a, b) {
    return a + b;
  }
}

// 具体策略2:减法
class SubtractStrategy extends Strategy {
  execute(a, b) {
    return a - b;
  }
}

// 具体策略3:乘法
class MultiplyStrategy extends Strategy {
  execute(a, b) {
    return a * b;
  }
}

// 上下文
class Context {
  constructor(strategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  executeStrategy(a, b) {
    return this.strategy.execute(a, b);
  }
}

// 使用
const context = new Context(new AddStrategy());
console.log(context.executeStrategy(5, 3)); // 8

context.setStrategy(new SubtractStrategy());
console.log(context.executeStrategy(5, 3)); // 2

context.setStrategy(new MultiplyStrategy());
console.log(context.executeStrategy(5, 3)); // 15

实际应用场景:

  • 表单验证规则
  • 排序算法选择
  • 支付方式选择
  • 动画效果配置

模板方法模式

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

常用于需要定义算法框架,但某些具体步骤由子类实现的场景。

// 抽象类
class AbstractClass {
  templateMethod() {
    this.primitiveOperation1();
    this.primitiveOperation2();
    this.concreteOperation();
  }

  primitiveOperation1() {} // 抽象方法,由子类实现
  primitiveOperation2() {} // 抽象方法,由子类实现
  concreteOperation() {    // 具体方法,直接实现
    console.log("Concrete operation");
  }
}

// 具体类A
class ConcreteClassA extends AbstractClass {
  primitiveOperation1() {
    console.log("ConcreteClassA: Primitive operation 1");
  }

  primitiveOperation2() {
    console.log("ConcreteClassA: Primitive operation 2");
  }
}

// 具体类B
class ConcreteClassB extends AbstractClass {
  primitiveOperation1() {
    console.log("ConcreteClassB: Primitive operation 1");
  }

  primitiveOperation2() {
    console.log("ConcreteClassB: Primitive operation 2");
  }
}

// 使用
const concreteA = new ConcreteClassA();
concreteA.templateMethod();
// ConcreteClassA: Primitive operation 1
// ConcreteClassA: Primitive operation 2
// Concrete operation

const concreteB = new ConcreteClassB();
concreteB.templateMethod();
// ConcreteClassB: Primitive operation 1
// ConcreteClassB: Primitive operation 2
// Concrete operation

实际应用场景:

  • 生命周期钩子
  • 数据处理流程
  • 业务流程定义

责任链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

常用于需要多个对象依次处理请求的场景,如审批流程、中间件链。

// 处理者抽象类
class Handler {
  constructor() {
    this.nextHandler = null;
  }

  setNext(handler) {
    this.nextHandler = handler;
    return handler;
  }

  handle(request) {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return null;
  }
}

// 具体处理者1
class ConcreteHandler1 extends Handler {
  handle(request) {
    if (request === "request1") {
      return `Handler1 handled ${request}`;
    }
    return super.handle(request);
  }
}

// 具体处理者2
class ConcreteHandler2 extends Handler {
  handle(request) {
    if (request === "request2") {
      return `Handler2 handled ${request}`;
    }
    return super.handle(request);
  }
}

// 具体处理者3
class ConcreteHandler3 extends Handler {
  handle(request) {
    if (request === "request3") {
      return `Handler3 handled ${request}`;
    }
    return super.handle(request);
  }
}

// 使用
const handler1 = new ConcreteHandler1();
const handler2 = new ConcreteHandler2();
const handler3 = new ConcreteHandler3();

handler1.setNext(handler2).setNext(handler3);

console.log(handler1.handle("request1")); // Handler1 handled request1
console.log(handler1.handle("request2")); // Handler2 handled request2
console.log(handler1.handle("request3")); // Handler3 handled request3
console.log(handler1.handle("request4")); // null

实际应用场景:

  • Express中间件
  • 审批流程
  • 异常处理链
  • 事件冒泡

命令模式

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

常用于需要将请求调用者和接收者解耦的场景,支持撤销/重做操作。

// 命令接口
class Command {
  execute() {}
  undo() {}
}

// 接收者
class Receiver {
  action() {
    console.log("Receiver action performed");
  }

  undoAction() {
    console.log("Receiver action undone");
  }
}

// 具体命令
class ConcreteCommand extends Command {
  constructor(receiver) {
    super();
    this.receiver = receiver;
  }

  execute() {
    this.receiver.action();
  }

  undo() {
    this.receiver.undoAction();
  }
}

// 调用者
class Invoker {
  constructor() {
    this.command = null;
    this.history = [];
  }

  setCommand(command) {
    this.command = command;
  }

  executeCommand() {
    this.command.execute();
    this.history.push(this.command);
  }

  undoCommand() {
    const command = this.history.pop();
    if (command) {
      command.undo();
    }
  }
}

// 使用
const receiver = new Receiver();
const command = new ConcreteCommand(receiver);
const invoker = new Invoker();

invoker.setCommand(command);
invoker.executeCommand(); // Receiver action performed
invoker.executeCommand(); // Receiver action performed
invoker.undoCommand();    // Receiver action undone
invoker.undoCommand();    // Receiver action undone

实际应用场景:

  • 撤销/重做功能
  • 宏命令
  • GUI操作
  • 事务处理

状态模式

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

常用于对象行为依赖于其状态的场景,避免大量的条件判断。

// 状态接口
class State {
  handle(context) {}
}

// 具体状态A
class ConcreteStateA extends State {
  handle(context) {
    console.log("State A handling");
    context.setState(new ConcreteStateB());
  }
}

// 具体状态B
class ConcreteStateB extends State {
  handle(context) {
    console.log("State B handling");
    context.setState(new ConcreteStateA());
  }
}

// 上下文
class Context {
  constructor() {
    this.state = new ConcreteStateA();
  }

  setState(state) {
    this.state = state;
  }

  getState() {
    return this.state;
  }

  request() {
    this.state.handle(this);
  }
}

// 使用
const context = new Context();
context.request(); // State A handling
context.request(); // State B handling
context.request(); // State A handling
context.request(); // State B handling

实际应用场景:

  • 订单状态流转
  • 游戏角色状态
  • 工作流引擎
  • 自动机实现

中介者模式

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

常用于多个对象之间复杂的交互关系,需要降低耦合度的场景。

// 中介者接口
class Mediator {
  send(message, colleague) {}
}

// 同事接口
class Colleague {
  constructor(mediator) {
    this.mediator = mediator;
  }

  send(message) {
    this.mediator.send(message, this);
  }

  receive(message) {
    console.log(`${this.constructor.name} received: ${message}`);
  }
}

// 具体中介者
class ConcreteMediator extends Mediator {
  constructor() {
    super();
    this.colleagues = {};
  }

  addColleague(name, colleague) {
    this.colleagues[name] = colleague;
  }

  send(message, sender) {
    for (const name in this.colleagues) {
      if (this.colleagues[name] !== sender) {
        this.colleagues[name].receive(message);
      }
    }
  }
}

// 具体同事
class ConcreteColleague1 extends Colleague {}
class ConcreteColleague2 extends Colleague {}
class ConcreteColleague3 extends Colleague {}

// 使用
const mediator = new ConcreteMediator();

const colleague1 = new ConcreteColleague1(mediator);
const colleague2 = new ConcreteColleague2(mediator);
const colleague3 = new ConcreteColleague3(mediator);

mediator.addColleague("Colleague1", colleague1);
mediator.addColleague("Colleague2", colleague2);
mediator.addColleague("Colleague3", colleague3);

colleague1.send("Hello from Colleague1");
// ConcreteColleague2 received: Hello from Colleague1
// ConcreteColleague3 received: Hello from Colleague1

实际应用场景:

  • 聊天室
  • 表单验证
  • MVC框架中的Controller
  • 事件总线

迭代器模式

提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。

常用于需要遍历集合对象的场景。

// 迭代器接口
class Iterator {
  hasNext() {}
  next() {}
}

// 聚合接口
class Aggregate {
  createIterator() {}
}

// 具体迭代器
class ConcreteIterator extends Iterator {
  constructor(aggregate) {
    this.aggregate = aggregate;
    this.index = 0;
  }

  hasNext() {
    return this.index < this.aggregate.items.length;
  }

  next() {
    if (this.hasNext()) {
      return this.aggregate.items[this.index++];
    }
    return null;
  }
}

// 具体聚合
class ConcreteAggregate extends Aggregate {
  constructor() {
    super();
    this.items = [];
  }

  addItem(item) {
    this.items.push(item);
  }

  createIterator() {
    return new ConcreteIterator(this);
  }
}

// 使用
const aggregate = new ConcreteAggregate();
aggregate.addItem("Item 1");
aggregate.addItem("Item 2");
aggregate.addItem("Item 3");

const iterator = aggregate.createIterator();

while (iterator.hasNext()) {
  console.log(iterator.next());
}
// Item 1
// Item 2
// Item 3

实际应用场景:

  • 数组遍历
  • 树形结构遍历
  • 分页加载
  • 无限滚动

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

常用于需要保存和恢复对象状态的场景,如撤销功能、游戏存档。

// 备忘录类
class Memento {
  constructor(state) {
    this.state = state;
  }

  getState() {
    return this.state;
  }
}

// 发起人
class Originator {
  constructor() {
    this.state = null;
  }

  setState(state) {
    this.state = state;
  }

  getState() {
    return this.state;
  }

  saveToMemento() {
    return new Memento(this.state);
  }

  restoreFromMemento(memento) {
    this.state = memento.getState();
  }
}

// 管理者
class Caretaker {
  constructor() {
    this.mementos = [];
  }

  addMemento(memento) {
    this.mementos.push(memento);
  }

  getMemento(index) {
    return this.mementos[index];
  }
}

// 使用
const originator = new Originator();
const caretaker = new Caretaker();

originator.setState("State 1");
caretaker.addMemento(originator.saveToMemento());

originator.setState("State 2");
caretaker.addMemento(originator.saveToMemento());

originator.setState("State 3");
console.log(originator.getState()); // State 3

originator.restoreFromMemento(caretaker.getMemento(0));
console.log(originator.getState()); // State 1

originator.restoreFromMemento(caretaker.getMemento(1));
console.log(originator.getState()); // State 2

实际应用场景:

  • 撤销功能
  • 游戏存档
  • 文本编辑器
  • 数据库事务回滚

访问者模式

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

常用于需要对一个对象结构中的对象进行多种不同操作的场景。

// 访问者接口
class Visitor {
  visitConcreteElementA(element) {}
  visitConcreteElementB(element) {}
}

// 元素接口
class Element {
  accept(visitor) {}
}

// 具体元素A
class ConcreteElementA extends Element {
  accept(visitor) {
    visitor.visitConcreteElementA(this);
  }

  operationA() {
    return "ConcreteElementA operation";
  }
}

// 具体元素B
class ConcreteElementB extends Element {
  accept(visitor) {
    visitor.visitConcreteElementB(this);
  }

  operationB() {
    return "ConcreteElementB operation";
  }
}

// 具体访问者
class ConcreteVisitor extends Visitor {
  visitConcreteElementA(element) {
    console.log(`Visitor processing: ${element.operationA()}`);
  }

  visitConcreteElementB(element) {
    console.log(`Visitor processing: ${element.operationB()}`);
  }
}

// 对象结构
class ObjectStructure {
  constructor() {
    this.elements = [];
  }

  attach(element) {
    this.elements.push(element);
  }

  detach(element) {
    const index = this.elements.indexOf(element);
    if (index !== -1) {
      this.elements.splice(index, 1);
    }
  }

  accept(visitor) {
    for (const element of this.elements) {
      element.accept(visitor);
    }
  }
}

// 使用
const objectStructure = new ObjectStructure();
const elementA = new ConcreteElementA();
const elementB = new ConcreteElementB();

objectStructure.attach(elementA);
objectStructure.attach(elementB);

const visitor = new ConcreteVisitor();
objectStructure.accept(visitor);
// Visitor processing: ConcreteElementA operation
// Visitor processing: ConcreteElementB operation

实际应用场景:

  • 编译器语法树遍历
  • 文档对象模型(DOM)操作
  • 复杂对象结构的处理
  • 代码分析工具

解释器模式

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

常用于需要解释和执行特定语法规则的场景。

// 抽象表达式
class Expression {
  interpret(context) {}
}

// 终结符表达式
class TerminalExpression extends Expression {
  constructor(data) {
    super();
    this.data = data;
  }

  interpret(context) {
    if (context.includes(this.data)) {
      return true;
    }
    return false;
  }
}

// 非终结符表达式
class OrExpression extends Expression {
  constructor(expr1, expr2) {
    super();
    this.expr1 = expr1;
    this.expr2 = expr2;
  }

  interpret(context) {
    return this.expr1.interpret(context) || this.expr2.interpret(context);
  }
}

class AndExpression extends Expression {
  constructor(expr1, expr2) {
    super();
    this.expr1 = expr1;
    this.expr2 = expr2;
  }

  interpret(context) {
    return this.expr1.interpret(context) && this.expr2.interpret(context);
  }
}

// 使用
const terminal1 = new TerminalExpression("John");
const terminal2 = new TerminalExpression("Jane");
const terminal3 = new TerminalExpression("Married");

const andExpr = new AndExpression(terminal1, terminal3);
const orExpr = new OrExpression(andExpr, new TerminalExpression("Single"));

const context1 = "John Married";
const context2 = "Jane Single";
const context3 = "John Single";

console.log(orExpr.interpret(context1)); // true
console.log(orExpr.interpret(context2)); // true
console.log(orExpr.interpret(context3)); // false

实际应用场景:

  • 正则表达式引擎
  • SQL解析器
  • 配置文件解析
  • 模板引擎

设计模式对比总结

模式类型模式名称核心思想典型应用场景
创建型单例模式确保一个类只有一个实例配置管理、连接池、全局缓存
创建型工厂模式封装对象创建逻辑根据条件创建不同类型对象
创建型抽象工厂创建产品族跨平台组件库、主题系统
创建型建造者模式分步构建复杂对象配置对象构建、复杂表单
创建型原型模式通过克隆创建对象复杂对象复制、性能优化
结构型适配器模式接口转换兼容第三方库集成、旧系统改造
结构型装饰器模式动态添加职责功能增强、AOP实现
结构型桥接模式分离抽象和实现跨平台开发、多维度变化
结构型代理模式控制对象访问延迟加载、访问控制、缓存
结构型外观模式简化子系统接口复杂系统封装、API设计
结构型组合模式树形结构统一处理文件系统、UI组件树
结构型享元模式对象共享减少内存大量相似对象、文本编辑
行为型观察者模式一对多依赖通知事件系统、响应式编程
行为型策略模式算法封装可替换验证规则、排序算法
行为型模板方法算法骨架延迟实现生命周期、业务流程
行为型责任链请求链式处理中间件、审批流程
行为型命令模式请求封装支持撤销GUI操作、事务处理
行为型状态模式状态决定行为订单流转、游戏角色
行为型中介者中介解耦交互聊天室、表单验证
行为型迭代器统一遍历接口集合遍历、树形结构
行为型备忘录状态保存恢复撤销功能、游戏存档
行为型访问者操作与对象分离编译器、代码分析
行为型解释器语法规则解释正则表达式、配置解析

设计模式使用建议

何时使用设计模式

  1. 代码复用:当多个地方出现相似代码时
  2. 降低耦合:当模块间依赖过于紧密时
  3. 扩展性需求:当系统需要频繁添加新功能时
  4. 维护性要求:当代码逻辑复杂难以理解时
  5. 性能优化:当需要优化内存或执行效率时

避免过度设计

  1. 简单优先:能用简单方案解决的就不要用复杂模式
  2. 按需引入:只在确实需要时才引入设计模式
  3. 渐进重构:先写简单代码,发现问题时再重构
  4. 团队共识:确保团队成员都理解所使用的模式

学习路径

  1. 掌握核心原则:SOLID原则是基础
  2. 从常用模式开始:单例、工厂、观察者、策略等
  3. 结合实际项目:在工作中尝试应用
  4. 阅读优秀代码:学习框架和库的实现
  5. 持续实践:通过实际项目加深理解

文档说明: 本文档涵盖了23种经典设计模式的核心概念、代码示例和实际应用场景,适合作为学习和参考材料使用。