📌 写在最前面:为什么你要读这篇?
在上一篇 NestJS 入门指南中,我们反复提到一个词------工厂模式 。main.ts 里那行 NestFactory.create(AppModule) 到底是什么原理?为什么不用 new NestApplication()?如果你对这些问题还有点模糊,这篇补充文章就是为你准备的。
本文是 NestJS 系列的番外篇 ,用 蜜雪冰城点奶茶 作为核心比喻,从 GoF 经典定义到三种工厂类型,从一行 demo 拆解到 NestJS 源码对应,把工厂模式拆解得明明白白。全文覆盖:工厂模式定义、三大核心价值、三种类型对比、类与对象实例的区别、优缺点分析、NestJS 实战对应。读完这篇,你不仅懂工厂模式,还能跟面试官聊得明明白白。
📚 本文目录
| 章节 | 内容概要 | 重要度 |
|---|---|---|
| 🧁 工厂模式是什么 | 蜜雪冰城比喻,一句话讲清楚 | ⭐⭐⭐⭐⭐ |
| 一、官方定义(GoF) | 设计模式经典定义 + 小白翻译 | ⭐⭐⭐⭐ |
| 二、三种类型对比 | 简单工厂 / 工厂方法 / 抽象工厂 一张表看懂 | ⭐⭐⭐⭐⭐ |
| 三、三大核心价值 | 解耦 + 封装变化 + 多态(附代码) | ⭐⭐⭐⭐⭐ |
| 四、一行 demo 拆解 | MixueFactory.create('ice') 逐词解析 |
⭐⭐⭐⭐ |
| 五、对应 NestJS | 你的 demo 就是 NestFactory 的「教学简化版」 | ⭐⭐⭐⭐⭐ |
| 六、为什么不用 new | 直接 new 的 3 个问题 vs 工厂的 3 个优势 | ⭐⭐⭐⭐⭐ |
| 七、类 vs 对象实例 | 最容易混淆的概念,小白记忆口诀 | ⭐⭐⭐⭐ |
| 八、优缺点 | 面试必问:工厂模式的优缺点是什么? | ⭐⭐⭐⭐ |
| 九、一句话总结 | 全文核心提炼 | ⭐⭐⭐⭐⭐ |
| 十、参考资料 | GoF、NestJS 官方、Refactoring Guru | ⭐⭐⭐ |
🧁 工厂模式是什么?
设计模式是一套被反复使用、经过分类编目的代码设计经验总结,GoF 经典共 23 种。其中 工厂模式 是第一种、也是最为重要的设计模式之一。
你想喝奶茶,不用自己做(流程代码),就找蜜雪冰城(工厂)就可以了。为什么"蜜雪冰城"是工厂?因为去到蜜雪冰城可以点各种各样的奶茶或者其他商品。"工厂"就是这个概念,可以根据不同的需求,生产不同的产品。
所以 NestFactory 就是"蜜雪冰城",满足 App 的各种需要。MixueFactory 帮我们提供工厂里的各种类,并和工厂里五花八门的类解耦。开发者只需要调用 MixueFactory.create(type) 即可。由于工厂里的每个类都实现了相同的 show 接口,由工厂类生产出来的对象实例,可以放心地直接调用(工厂类返回的是 new 出来的实例,不是类本身)。
本文 demo 代码仓库 :factory-demo(文末附完整示例)
一、工厂模式官方定义(GoF 23 种之一)
"定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。" ------ GoF《设计模式:可复用面向对象软件的基础》
小白翻译 :把 new Xxx() 这件事外包给一个"工厂类",调用方不关心 new 的过程,只传个参数拿成品。
💡 注:GoF 这个官方定义描述的是「工厂方法模式」(第二章里的第二种)。本文 demo 用的「简单工厂」是它的简化版------核心思想完全一致,都是把
new外包出去。区别只是简单工厂用一个类搞定所有产品,工厂方法每个产品对应一个工厂子类。
二、工厂模式的三种类型
| 类型 | 特点 | 本文 demo 属于哪种 |
|---|---|---|
| 简单工厂(Simple Factory) | 一个工厂 + 一个 switch/if,根据参数返回不同产品 |
✅ 本文的 MixueFactory 就是这种 |
| 工厂方法(Factory Method) | 每种产品对应一个工厂子类,新增产品时加新工厂子类(遵循开闭原则) | |
| 抽象工厂(Abstract Factory) | 一个工厂能造一族相关产品(比如全套 UI 主题:按钮+输入框+下拉框) |
📌 本文写的是最基础的简单工厂 ,便于理解。NestJS 的
NestFactory也是这一类的进阶版。💡 补充说明:严格来说「简单工厂」不属于 GoF 23 种经典设计模式,它是「工厂方法」的简化版。但它最常用、最好理解,所以作为入门第一个学,很多教材也会放在一起讲。
三、工厂模式的三大核心价值
价值 1:解耦
调用方不再写死具体类名:
javascript
// ❌ 不用工厂:调用方写死了 IceCream 这个类名
const drink1 = new IceCream()
// ✅ 用工厂:调用方只关心 'ice' 这个标识
const drink1 = MixueFactory.create('ice')
价值 2:封装变化(扩展容易)
新增"咖啡"口味,只改工厂,调用方代码一行不改:
javascript
class MixueFactory {
static create(type){
switch(type){
case 'ice': return new IceCream()
case 'lemon': return new LemonTea()
case 'milk': return new MilkTea()
case 'coffee': return new Coffee() // 新增,调用方无感知
default: return null
}
}
}
价值 3:多态
所有产品实现相同方法名,调用方统一调用:
javascript
const drink1 = MixueFactory.create('ice')
drink1.show() // 不管返回啥,都能调 show
const drink2 = MixueFactory.create('lemon')
drink2.show() // 同一个方法名,不同行为 = 多态
四、一行 demo 拆解
javascript
const drink1 = MixueFactory.create('ice')
| 部分 | 作用 |
|---|---|
MixueFactory |
工厂类(用 static,不需要 new 出实例) |
.create |
静态方法,工厂对外的"点单接口" |
('ice') |
参数,告诉工厂要啥 |
const drink1 |
接住工厂返回的产品实例 |
| 返回值 | 对象实例 (new IceCream() 造出来的那个具体冰激凌),不是类本身 |
五、对应 NestJS 的 NestFactory.create
| 本文 MixueFactory demo | NestJS 的 NestFactory | 说明 |
|---|---|---|
MixueFactory |
NestFactory |
工厂本身 |
create(type) |
create(AppModule) |
创建方法 |
'ice' / 'lemon' / 'milk' 字符串 |
AppModule 模块对象 |
传给工厂的"点单参数" |
IceCream / LemonTea / MilkTea |
NestApplication 实例 |
工厂造出的产品 |
drink.show() |
app.listen(3000) |
产品对外提供服务 |
static 方法 |
static 方法 |
工厂方法都是 static |
| 同步返回 | 异步返回(await) ⚠️ | NestFactory 是异步的 |
| 写死 switch 分支 | 工厂内部按 @Module 装饰器装配 |
内部实现不同 |
📌 一句话:本文的 MixueFactory 是 NestFactory 的"教学简化版",把异步、依赖注入、装饰器解析这些复杂细节都剥掉,只剩"工厂+产品+接口"的骨架。
六、为什么不用 new 直接造?
❌ 不用工厂,直接 new
javascript
const drink1 = new IceCream()
const drink2 = new LemonTea()
const drink3 = new MilkTea()
问题:
- 调用方写死了具体类名,加新口味得改调用方
- 没有统一入口,难以统计"店里到底卖啥"
- 如果造一个产品要做复杂的初始化(比如烤冰激凌),每个调用方都要复制一遍流程
✅ 用工厂
javascript
const drink1 = MixueFactory.create('ice')
优势:
- 调用方只关心
'ice'标识,不关心具体类 - 新增口味只改工厂,调用方无感知
- 复杂初始化藏进工厂,调用方写一行就够
七、深入理解:「类」 vs 「对象实例」
工厂模式里最容易混淆的两个概念------「类」和「对象实例」:
| 概念 | 解释 | 例子 |
|---|---|---|
| 类 | 蓝图 / 模板 | IceCream 这个 class 定义 |
| 对象实例 | 按蓝图造出的具体产品 | new IceCream() 造出来的那个具体的冰激凌 |
工厂 MixueFactory.create('ice') 返回的是 new IceCream() ------ 具体的冰激凌实例 ,所以你才能 drink1.show() 直接调用。
如果返回的是"类"本身,那得这样写(累赘):
javascript
const DrinkClass = MixueFactory.create('ice') // 假设返回的是类
const drink = new DrinkClass() // 还得再 new 一次
drink.show()
而工厂模式的价值就是:调用方不用再 new 一次,直接拿到能用的实例。
🎯 小白记忆口诀:类 = 冰激凌配方(图纸),对象实例 = 按配方做出来的那根具体冰激凌(能吃)。工厂给你的是「能吃的冰激凌」,不是「配方纸」,所以拿到手就能用。
八、工厂模式的优缺点
✅ 优点
- 解耦:调用方与具体类解耦,依赖抽象不依赖细节
- 扩展容易:加新产品只改工厂,调用方无感知
- 统一入口:所有产品都从工厂出,便于管理和统计
- 封装复杂初始化 :调用方只写一行
MixueFactory.create('ice')
⚠️ 缺点
- 工厂类变复杂 :产品越多,
switch越长(简单工厂通病) - 违反开闭原则:加新产品要改工厂代码(用"工厂方法"模式可避免,每种产品对应一个工厂子类)
九、一句话总结
工厂模式 = 把"new 对象"这件事外包给专门的工厂,调用方只传参数拿成品,不用关心造的过程。
在 NestJS 里,
NestFactory就是这个"工厂",AppModule是你点的"菜单",app是工厂给你的"成品"。const app = await NestFactory.create(AppModule)这一行就是"点单→出货"的过程。
十、参考资料
- GoF《设计模式:可复用面向对象软件的基础》(23 种经典设计模式)
- NestJS 官方文档:docs.nestjs.com/
- 工厂模式图解:refactoring.guru/design-patt...
🔗 本文与系列文章的关系
本文是 NestJS 系列文章的番外篇 / 补充篇,单独讲解「工厂模式」这一核心概念,是对上一篇中「工厂模式」章节的展开和深化。
📌 上一篇 (主系列第一篇):[NestJS 完全入门指南:2万字讲透 Module/Controller/Service + 实战 CRUD](#NestJS 完全入门指南:2万字讲透 Module/Controller/Service + 实战 CRUD "#")
🔜 下一篇(主系列第二篇):数据库实战 ------ 用 TypeORM 连接 PostgreSQL,让 Todo 数据真正持久化,告别重启丢数据。敬请期待!🚀
我们下一篇(主系列第二篇)见! 🚀
📌 勘误说明:本文为系列番外篇,后续如有代码错误或表述不当,会在评论区置顶更正。建议收藏本文,方便后续查阅更新。