深度解析工厂模式:从蜜雪冰城到 NestFactory,彻底搞懂“创建与使用分离”

在软件开发的浩瀚宇宙中,设计模式是前人留下的智慧结晶。而在 23 种经典设计模式中,工厂模式(Factory Pattern) 无疑是入门的第一课,也是实际开发中应用最广泛、最核心的模式之一。

很多初学者在刚接触设计模式时,往往会被各种抽象的类图绕晕。但如果我们把代码还原到生活场景,你会发现,工厂模式其实就藏在我们身边。今天,我们就以大家熟知的"蜜雪冰城"为例,结合 NestJS 框架中的 NestFactory,用最通俗易懂的方式,把工厂模式彻底讲透。

一、 为什么需要工厂?从"点奶茶"说起

想象一下,你走进一家蜜雪冰城,想喝一杯冰鲜柠檬水。你只需要对店员说:"我要一杯柠檬水。" 然后付款、等待、拿饮料。

在这个过程中,你完全不需要知道

  • 柠檬是怎么切的?
  • 糖浆是按什么比例调配的?
  • 冰块是机器制冰还是手工加冰?

你只关心"拿到一杯能喝的柠檬水"这个结果。至于背后的制作流程,那是蜜雪冰城(工厂)内部的事情。

回到代码世界,如果没有工厂模式,我们在创建对象时往往会写出这样的代码:

arduino 复制代码
// 糟糕的客户端代码
const drink = new LemonTea(); 
// 或者
const drink = new MilkTea();

当产品种类少时,这没什么问题。但如果一个系统有几十种产品,且创建逻辑非常复杂(比如需要读取配置、连接数据库、校验参数),客户端代码就会变得极其臃肿,且一旦产品类发生变化,所有调用处都要跟着改。

工厂模式的核心思想,就是将"对象的创建"与"对象的使用"分离。 开发者不需要直接 new 对象,而是把创建逻辑封装在一个工厂类中,客户端只需要向工厂"下单"即可。

二、 简单工厂:蜜雪冰城的"点单台"

结合你提供的代码,我们来看看最基础的简单工厂模式是如何运作的。

typescript 复制代码
// 1. 定义产品:它们都有相同的 show 接口
class IceCream {
  constructor() { this.name = '冰激凌'; this.price = 3; }
  show() { console.log(`${this.name} ${this.price} 元`); }
}
class LemonTea {
  constructor() { this.name = '柠檬水'; this.price = 4; }
  show() { console.log(`${this.name} ${this.price} 元`); }
}
class MilkTea {
  constructor() { this.name = '珍珠奶茶'; this.price = 8; }
  show() { console.log(`${this.name} ${this.price} 元`); }
}

// 2. 定义工厂:统一创建产品
class MixueFactory {
  static create(type) {
    switch(type) {
      case 'ice': return new IceCream();
      case 'lemon': return new LemonTea();
      case 'milk': return new MilkTea();
      default: throw new Error('本店不卖这个!');
    }
  }
}

// 3. 客户端使用:只管要,不管造
const drink1 = MixueFactory.create('ice');
drink1.show(); // 冰激凌 3 元

深度解析:

  1. 面向接口编程IceCreamLemonTeaMilkTea 虽然内部实现不同,但都提供了 show() 方法。这意味着,无论工厂返回的是哪个具体类,客户端都可以放心地调用 show(),这就是多态的体现。
  2. 封装变化 :如果蜜雪冰城明天推出了"芋圆烧仙草",你只需要在 MixueFactory 里加一个 case,客户端代码完全不需要动。
  3. 解耦 :客户端不再依赖具体的 LemonTea 类,而是依赖 MixueFactory

简单工厂的局限性

它违反了"开闭原则"。每增加一个新产品,都要修改工厂类的 switch 语句。如果产品非常多,这个工厂类就会变成一个巨大的"上帝类",难以维护。

三、 工厂方法:连锁加盟店的"扩张之道"

为了解决简单工厂的痛点,我们进化到了工厂方法模式

如果说简单工厂是"一家直营店",那工厂方法就是"连锁加盟店"。蜜雪冰城总部不再直接生产所有饮料,而是制定一个"开店标准",让不同的加盟店去生产具体的产品。

scala 复制代码
// 抽象工厂接口
class DrinkFactory {
  create() { throw new Error('子类必须实现此方法'); }
}

// 柠檬水加盟店
class LemonTeaFactory extends DrinkFactory {
  create() { return new LemonTea(); }
}

// 珍珠奶茶加盟店
class MilkTeaFactory extends DrinkFactory {
  create() { return new MilkTea(); }
}

核心区别

  • 简单工厂:一个工厂生产所有产品( centralized)。
  • 工厂方法:每个产品对应一个专属工厂(decentralized)。

什么时候用工厂方法?

当对象的创建逻辑非常复杂,或者你需要频繁扩展新产品时,工厂方法是更好的选择。它让每个工厂类职责单一,符合"单一职责原则"。

四、 抽象工厂:全家桶套餐的"产品族"

在现实商业中,蜜雪冰城不仅卖饮料,还卖冰激凌、圣代等。这些产品往往属于同一个"产品族"(比如夏季清凉套餐)。这就引出了抽象工厂模式

抽象工厂的核心是:创建一系列相关或相互依赖的对象,而无需指定它们具体的类。

比如,你要开一家"夏季清凉主题店",你需要一套配套的产品:柠檬水 + 冰激凌 + 圣代。抽象工厂能保证你拿到的这一套产品是配套的,不会出现"蜜雪冰城的柠檬水"配了"喜茶的冰激凌"这种尴尬情况。

适用场景:跨平台 UI 组件(Windows 按钮 + Windows 文本框)、数据库驱动切换(MySQL 连接 + MySQL 查询器)等。

五、 现实映射:NestFactory 与蜜雪冰城的供应链

理解了理论,我们来看看工业界的实战。

在 NestJS 框架中,NestFactory 就是一个典型的工厂。你不需要知道 NestJS 内部是如何初始化依赖注入容器、如何加载模块、如何启动 HTTP 服务器的。你只需要:

ini 复制代码
const app = await NestFactory.create(AppModule);
await app.listen(3000);

NestFactory.create() 背后封装了极其复杂的启动逻辑。它就像蜜雪冰城的供应链体系:

  • 上游:蜜雪冰城自建工厂(大咖国际),实现核心原料 100% 自产,从源头控制质量和成本。
  • 中游:通过高度标准化的流程,将复杂的饮品制作拆解为简单的"冲泡"动作。
  • 下游:加盟商(客户端)只需要按照标准操作,就能产出合格的产品。

为什么蜜雪冰城能把价格打下来?

正是因为这种极致的"工厂化"思维。通过自建供应链、产地直采(如四川安岳柠檬)、标准化门店运营,蜜雪冰城将"创建一杯饮料"的成本和复杂度降到了最低。这与工厂模式在软件工程中的目标不谋而合:隔离复杂性,控制成本,提高复用性。

六、 总结:如何判断是否使用工厂模式?

并不是所有地方都需要工厂。如果创建一个对象只需要 new 一下,没有任何复杂逻辑,请直接使用 new

当你遇到以下情况时,请考虑工厂模式:

  1. 创建逻辑复杂new 一个对象需要传十几个参数,或者需要查数据库、读配置。
  2. 需要解耦:客户端不应该依赖具体类,而应该依赖抽象。
  3. 需要扩展:系统需要支持多种类型的产品,且未来可能会增加新类型。
  4. 统一管控:需要在对象创建时统一记录日志、统计创建次数、进行权限校验等。

工厂模式不仅仅是代码技巧,更是一种架构思维。它教会我们:在复杂的系统中,找到那个"变化的点",然后把它封装起来。无论是写代码,还是做商业,这都是一种通往"高内聚、低耦合"的必经之路。

相关推荐
蔓越莓1 小时前
打包工具:编译器ESBuild
前端·面试
用户921080262861 小时前
Bubble 的 loading 和 typing:AI 回复生成中的交互处理
前端
SamChan901 小时前
用Playwright端到端测试PDF翻译功能:Web自动化测试实战
前端·python·ai·pdf·wpf
平生不晚1 小时前
在 SVG 体系里画一条任意的线
前端·算法
用户059540174461 小时前
把大模型记忆召回测试从 30 分钟手工核对压到 3 秒自动化,pytest + FAISS 这套组合救了我
前端·css
醉里博客1 小时前
醉里起始页 Snavigation2.0:纯前端导航页的二开改造与性能优化实践
前端
雪芽蓝域zzs1 小时前
新建前端pnpm(vue js ) 仿若依项目(一)
前端·javascript·vue.js
vipbic1 小时前
网站升级了,我却有点舍不得
前端·vue.js·后端
IT_陈寒1 小时前
Java Stream并行处理让我数据库崩了两次
前端·人工智能·后端