讲解

想象一家连锁奶茶店的总部规定了标准出品流程:接单、制茶、封装、叫号——但每款茶(珍珠奶茶、柠檬绿茶)由门店现场按配方制作。总部管流程,门店管「做哪一杯」。工厂方法模式就是这个结构:父类定义使用对象的流程骨架,把「创建哪个具体对象」这一步延迟给子类决定。

GoF 的定义是:定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。它的关键不是「有个方法返回对象」,而是「创建决策随子类化而多态」——新增一种产品时,新增一个工厂子类即可,符合开闭原则。

先说清它和「简单工厂」的区别,这是面试和实战里最容易混淆的点。简单工厂是一个类里一个 switch:根据参数返回不同产品。它直白好用,但每加一种产品就要改这个 switch——违反开闭原则。工厂方法把这个 switch 摊开成一棵工厂类层级:每种产品配一个工厂子类,新增产品时加一对新类(产品 + 工厂),老代码纹丝不动。代价是类的数量翻倍,所以产品种类少且稳定时,简单工厂完全够用。

适用场景:一个类无法预知它需要创建的对象的确切类型;希望把「创建什么」和「怎么用」解耦;框架设计中让用户扩展产品类型(日志框架、通知渠道、ORM 的驱动连接都是经典案例)。

示例

以日志器为例:LoggerFactory 定义了业务流程 info()(先拿日志器再写日志),但 createLogger() 留给子类决定产出哪种日志器:

import assert from 'node:assert/strict';

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

class ConsoleLogger implements Logger {
  log(message: string): string {
    return '[console] ' + message;
  }
}

class FileLogger implements Logger {
  log(message: string): string {
    return '[file] ' + message;
  }
}

// 父类定义流程(info),创建决策下沉为抽象方法
abstract class LoggerFactory {
  abstract createLogger(): Logger;

  info(message: string): string {
    return this.createLogger().log(message);
  }
}

class ConsoleLoggerFactory extends LoggerFactory {
  createLogger(): Logger {
    return new ConsoleLogger();
  }
}

class FileLoggerFactory extends LoggerFactory {
  createLogger(): Logger {
    return new FileLogger();
  }
}

const factories: LoggerFactory[] = [new ConsoleLoggerFactory(), new FileLoggerFactory()];
const outputs = factories.map((f) => f.info('启动完成'));
assert.deepEqual(outputs, ['[console] 启动完成', '[file] 启动完成']);
console.log(outputs.join('\n'));

调用方手里的类型是抽象工厂 LoggerFactory,它不在乎子类是哪一个。新增 RemoteLogger 时,只需要再加 RemoteLogger 和 RemoteLoggerFactory 两个类,ConsoleLoggerFactory、FileLoggerFactory 以及所有调用 info() 的代码都不受影响——这正是开闭原则在「创建对象」这件事上的落地。

常见坑

  • 分不清简单工厂和工厂方法:前者是一个类的条件分支,后者是工厂类的多态层级;说「我们用了工厂模式」时最好明确是哪一种。
  • 产品只有一种也上工厂方法:层级是为「种类会扩展」准备的,种类固定时简单工厂或直接 new 更诚实。
  • 工厂方法里塞业务逻辑:createLogger 只负责创建,初始化参数校验之外别放别的;流程逻辑放在父类使用产品的方法里。
  • 为每个产品机械地建工厂子类:如果多个产品的创建逻辑只差一个参数,用一个带参数的工厂方法比铺开五个子类更合理——模式服从场景。
  • 以为工厂方法能消除所有 new:它只是把 new 集中并多态化,系统的某处(通常是入口)仍然要做一次具体选择。

小结

工厂方法 = 父类定流程、子类定产品,把实例化延迟到子类,换取新增产品时的开闭原则。它与简单工厂的取舍本质是「类的数量」换「修改的封闭」。下一章是它的升级版:一次要创建一族相互关联的产品时,用抽象工厂。