本文是「TS手写设计模式」系列的第一篇,带你用TypeScript手把手实现所有5种创建型设计模式,包括:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。每种模式不仅提供完整代码、UML类图,还结合前端场景与DSL系统落地应用,深入浅出,带你真正理解设计思想,而不仅是死记套路。
GitHub 项目地址 👉 cynthiaCh/design-patterns-ts
🔍 为什么要学习创建型模式?
在我们的前端项目中,你是否遇到过这些问题:
- 组件初始化逻辑冗余、难以维护?
- 表单字段配置混乱、创建方式不统一?
- 多个主题皮肤 / 地域配置需要灵活切换?
这些问题,其实都可以归结为:“对象如何被创建” 的问题,也正是创建型模式要解决的核心。
创建型模式的目标是 将对象的创建过程从使用者中剥离出来,实现代码的高内聚、低耦合,为我们构建灵活可扩展的前端架构(特别是通用性开发和DSL场景)提供了极大帮助。
✅ 1. 单例模式 Singleton
🧠 核心思想
确保一个类只有一个实例,并提供一个全局访问点。是一种最简单却经久不衰的设计模式。
🧪 TypeScript 实现
export class Singleton {
private static instance: Singleton
private constructor() {}
static getInstance() {
if (!this.instance) this.instance = new Singleton()
return this.instance
}
say() {
return 'I am the only instance'
}
}
🧩 场景类比
就像浏览器的 window
或 Vue 中的 Vuex store
,我们希望某些全局状态是唯一的。
✅ 使用场景
- 日志记录器(Logger)
- 网络连接池(HTTP Client)
- 全局配置中心
🧠 在组件系统中这样用
我们常常希望一些全局服务组件只存在一个实例,比如:
- 全局 Loading 控制器:用于统一控制加载状态
LoadingController.getInstance().show()
- 全局 Message / Toast 工具:避免重复弹窗、管理队列
- DSL 运行上下文管理器:统一注册变量、响应事件(可复用)
export class LoadingController { private static instance: LoadingController private constructor() {} static getInstance() { if (!this.instance) this.instance = new LoadingController() return this.instance } show() {/*...*/} hide() {/*...*/} }
✅ 2. 工厂方法模式 Factory Method
🧠 核心思想
将创建对象的逻辑延迟到子类中实现。我们只关心“创建一个什么类型的产品”,而不关心“如何创建”。
📌 传统写法的问题
function createFruit(type: string) {
switch(type) {
case 'apple': return new Apple()
case 'banana': return new Banana()
}
}
这种写法一旦要添加新类型,就得改 switch
,违反了开闭原则。
✅ 工厂方法优化
interface Fruit {
eat(): void
}
abstract class FruitCreator {
abstract factoryMethod(): Fruit
}
class AppleCreator extends FruitCreator {
factoryMethod(): Fruit {
return new Apple()
}
}
🧩 应用场景
- 动态创建不同类型组件(比如不同输入组件)
- 字段渲染器(fieldRenderer)注册与生成
🧠 在组件系统中这样用
当我们在构建表单 DSL 时,经常根据字段类型动态渲染不同组件:
class FieldFactory { static create(type: string): Component { switch (type) { case 'input': return new InputField() case 'select': return new SelectField() } } }
更优方式是将构建逻辑写到子类中,并注册 creator map,支持插件化扩展和解耦。适用于你在 DSL 中构建「类型 = 渲染器」的结构。
✅ 3. 抽象工厂模式 Abstract Factory
🧠 核心思想
定义多个相关或依赖的对象创建接口,而无需指定它们具体的类。适用于“产品族”的场景。
示例
interface FruitFactory {
createApple(): Apple
createBanana(): Banana
}
class ChinaFruitFactory implements FruitFactory {
createApple() { return new ChinaApple() }
createBanana() { return new ChinaBanana() }
}
🧩 应用类比
- Ant Design 不同主题的样式组件(暗色、亮色)
- 根据“地域、行业”等创建不同 DSL 字段组件组合
✅ 优点
- 保证产品之间组合一致性
- 易于扩展,支持多套版本共存
🧠 在系统中这样用
当你要为不同“产品线/国家/主题”构建一套组件库,比如:
interface FormFactory { createTextField(): TextField createSelectField(): SelectField } class CNFormFactory implements FormFactory { /* 红色中文主题 */ } class USFormFactory implements FormFactory { /* 英文蓝色主题 */ }
在实际项目中,这让你轻松切换 DSL 渲染策略,而无需侵入式修改组件。
✅ 4. 建造者模式 Builder
🧠 核心思想
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
✨ 示例代码
const field = new FieldBuilder()
.setType('select')
.setLabel('省份')
.setOptions(['北京', '上海'])
.setRequired(true)
.build()
💡 使用优势
- 链式调用,简洁清晰
- 拆解复杂构建流程
- 支持字段 DSL 的灵活配置
🧩 场景例子
- 表单构造器(FormBuilder)
- JSON Schema 构造器
🧠 在 DSL 系统中这样用
构造一个字段配置常常是个冗长的对象:
const field = { type: 'select', label: '省份', required: true, options: [...] }
使用建造者后:
new FieldBuilder() .setType('select') .setLabel('省份') .setOptions([...]) .build()
优点:DSL 配置更清晰,字段支持默认注入、自定义生成逻辑。
✅ 5. 原型模式 Prototype
🧠 核心思想
用一个已经存在的实例作为原型,创建一个和它相似的新对象。常用于“复制”而非“新建”。
✅ TS实现
interface Prototype<T> {
clone(): T
}
class Field implements Prototype<Field> {
constructor(public label: string) {}
clone(): Field {
return new Field(this.label)
}
}
🧩 应用场景
- 表单模板复制功能
- 低代码平台拖拽生成多个字段副本
- 低代码编辑器中的“克隆模块”功能
🧠 在表单/编辑器中这样用
用户在拖拽时经常有“克隆已有字段”的需求:
const newField = oldField.clone()
优于 JSON.parse/深拷贝方式,因为:
- 可以定制 clone 行为(清除 ID、调整默认值)
- 可维护性强,可读性好
🧠 小结
模式 | 关键动机 | 典型场景 |
---|---|---|
Singleton | 全局唯一实例 | 配置中心、日志系统 |
Factory Method | 延迟构造逻辑 | 渲染器、工厂注册中心 |
Abstract Factory | 产品族构造 | 主题切换、风格替换 |
Builder | 分步骤构造 | 字段构造器、组件生成器 |
Prototype | 快速复制 | 表单字段复制、DSL模板 |
这些模式看似“老生常谈”,但一旦深入理解、落地于实际项目结构中,就会成为你构建大型系统时的底层设计武器。
🚀 下一步计划
下一篇我们将进入「结构型模式」篇章,重点讲解:
- 装饰器模式(字段增强)
- 适配器模式(配置转换)
- 外观模式(封装接口)
✨ 如果你正在构建 DSL、字段引擎或中后台组件库,这一系列内容将极具启发。
欢迎 Star 项目支持继续更新 👉 GitHub仓库
也欢迎关注掘金《TS手写设计模式》系列,每周更新,深挖设计思想,重构认知体系。