工厂模式核心概念解析
- 工厂模式 (Factory Pattern) 是一种创建型设计模式,核心思想是将对象的创建过程封装起来,用一个 “工厂” 类 / 函数来统一生产对象,而不是让调用方直接通过 new 关键字创建对象;
- 它解决的核心问题:
- 减少调用方与具体对象类的耦合 (调用方只需要告诉工厂 “要什么”,不用关心 “怎么造”);
- 统一管理对象的创建逻辑,便于后续扩展和维护;
- 隐藏对象创建的复杂细节 (比如初始化参数、依赖注入等);
工厂模式
三者的核心都是「把对象创建的逻辑封装起来」,区别仅在于「封装的粒度」:
- 简单工厂是「粗粒度」
- 工厂方法是「单产品粒度」
- 抽象工厂是「产品族粒度」
简单工厂模式(静态工厂)
-
最基础的实现,一个工厂函数根据传入的参数,直接创建并返回不同类型的对象 (不属于 GoF 23 种设计模式,但最常用);
-
类图:
-
场景选择:饮品店制作饮品,用户点单类型 (咖啡 / 奶茶) 直接创建对应的饮品,无需用户关心饮品的制作细节
// 定义饮品类型的枚举(替代魔法字符串,更规范) enum DrinkType { Coffee = "coffee", MilkTea = "milktea" } // 抽象饮品类(定义所有饮品的通用方法) abstract class Drink { // 抽象方法:子类必须实现,无需具体实现体 abstract make(): string; } // 具体产品:咖啡 class Coffee extends Drink { make(): string { return "制作咖啡:研磨咖啡豆 → 萃取浓缩液 → 加奶/糖 → 完成"; } } // 具体产品:奶茶 class MilkTea extends Drink { make(): string { return "制作奶茶:煮红茶 → 加鲜奶 → 调甜度 → 加珍珠 → 完成"; } } // 简单工厂:饮品工厂 class DrinkFactory { /** * 根据饮品类型创建饮品实例 * @param type 饮品类型(coffee/milkTea) * @returns 饮品实例 */ createDrink(type: string | DrinkType): Drink { const normalizedType = type.toLowerCase(); switch (normalizedType) { case DrinkType.Coffee: return new Coffee(); case DrinkType.MilkTea: return new MilkTea(); default: throw new Error(`暂不支持制作 ${type} 这种饮品`); } } } // 测试代码(客户端:顾客点单) const drinkShop = new DrinkFactory(); const myCoffee = drinkShop.createDrink("coffee"); const myMilkTea = drinkShop.createDrink(DrinkType.MilkTea); console.log("顾客点了咖啡:"); console.log(myCoffee.make()); // 输出:制作咖啡:研磨咖啡豆 → 萃取浓缩液 → 加奶/糖 → 完成 console.log("\n顾客点了奶茶:"); console.log(myMilkTea.make()); // 输出:制作奶茶:煮红茶 → 加鲜奶 → 调甜度 → 加珍珠 → 完成 -
场景解析:
- 顾客 (客户端) 只需要告诉饮品店 (工厂) 要「咖啡」或「奶茶」,无需自己动手制作;
- 如果新增饮品,需要修改 DrinkFactory 的 createDrink 方法 (违反开闭原则),这也是简单工厂的核心缺点;
工厂方法模式
-
将对象的创建延迟到子类中,核心是 “定义创建对象的接口,让子类决定实例化哪个类”,每个产品对应一个工厂子类;
-
类图:
-
场景选择:电商平台的支付方式,每种支付方式对应一个专属工厂 (支付宝工厂 / 微信支付工厂),新增支付方式只需新增「支付类 + 工厂类」,无需修改原有代码;
// 抽象产品:支付方式 abstract class Payment { /** * 支付方法 * @param amount 支付金额 * @returns 支付结果 */ abstract pay(amount: number): string; } // 具体产品:支付宝支付 class Alipay extends Payment { pay(amount: number): string { return `支付宝支付 ${amount} 元:验证账号 → 扣减余额 → 支付成功`; } } // 具体产品:微信支付 class WechatPay extends Payment { pay(amount: number): string { return `微信支付 ${amount} 元:调起微信小程序 → 输入密码 → 支付成功`; } } // 抽象工厂:支付工厂 abstract class PaymentFactory { abstract createPayment(): Payment; } // 具体工厂:支付宝工厂 class AlipayFactory extends PaymentFactory { createPayment(): Payment { return new Alipay(); } } // 具体工厂:微信支付工厂 class WechatPayFactory extends PaymentFactory { createPayment(): Payment { return new WechatPay(); } } // 测试代码(客户端:用户选择支付方式) // 选择支付宝支付 const alipayFactory = new AlipayFactory(); const alipay = alipayFactory.createPayment(); console.log(alipay.pay(99)); // 输出:支付宝支付 99 元:验证账号 → 扣减余额 → 支付成功 // 选择微信支付 const wechatPayFactory = new WechatPayFactory(); const wechatPay = wechatPayFactory.createPayment(); console.log(wechatPay.pay(199)); // 输出:微信支付 199 元:调起微信小程序 → 输入密码 → 支付成功 // 新增支付方式:银联支付(无需修改原有代码,符合开闭原则) class UnionPay extends Payment { pay(amount: number): string { return `银联支付 ${amount} 元:插卡 → 输入密码 → 支付成功`; } } class UnionPayFactory extends PaymentFactory { createPayment(): Payment { return new UnionPay(); } } const unionPayFactory = new UnionPayFactory(); const unionPay = unionPayFactory.createPayment(); console.log(unionPay.pay(299)); // 输出:银联支付 299 元:插卡 → 输入密码 → 支付成功 -
场景解析:
- 每种支付方式都有专属工厂,新增银联支付时,只需要新增 UnionPay (产品类) 和 UnionPayFactory (工厂类),完全不改动原有代码;
- 客户端需要先选择「支付工厂」,再通过工厂获取「支付方式」,逻辑更清晰,扩展性更强;
抽象工厂模式
-
用于创建一系列相关 / 依赖的对象簇,而不用指定具体类;
-
类图:
-
实现代码:
// 产品族 1:沙发(抽象类,使用 TS 原生 abstract 关键字) abstract class Sofa { // 抽象方法:子类必须实现,明确返回值类型为 string abstract sitOn(): string; } // 产品族 2:茶几(抽象类) abstract class CoffeeTable { // 抽象方法:子类必须实现 abstract putThings(): string; } // 现代风格 - 沙发 class ModernSofa extends Sofa { sitOn(): string { return "坐现代风格沙发:简约布艺,坐感偏硬"; } } // 现代风格 - 茶几 class ModernCoffeeTable extends CoffeeTable { putThings(): string { return "用现代风格茶几:钢化玻璃桌面,金属支架"; } } // 欧式风格 - 沙发 class EuropeanSofa extends Sofa { sitOn(): string { return "坐欧式风格沙发:雕花皮质,坐感柔软"; } } // 欧式风格 - 茶几 class EuropeanCoffeeTable extends CoffeeTable { putThings(): string { return "用欧式风格茶几:实木雕花桌面,雕花支架"; } } // 抽象工厂:家具工厂(定义创建所有家具的方法) abstract class FurnitureFactory { // 抽象方法:创建沙发,返回值为 Sofa 类型 abstract createSofa(): Sofa; // 抽象方法:创建茶几,返回值为 CoffeeTable 类型 abstract createCoffeeTable(): CoffeeTable; } // 具体工厂 1:现代家具工厂(创建现代风格的所有家具) class ModernFurnitureFactory extends FurnitureFactory { createSofa(): Sofa { return new ModernSofa(); } createCoffeeTable(): CoffeeTable { return new ModernCoffeeTable(); } } // 具体工厂 2:欧式家具工厂(创建欧式风格的所有家具) class EuropeanFurnitureFactory extends FurnitureFactory { createSofa(): Sofa { return new EuropeanSofa(); } createCoffeeTable(): CoffeeTable { return new EuropeanCoffeeTable(); } } // 测试代码(客户端:顾客选择家具风格) // 选择现代风格家具套件 const modernFactory = new ModernFurnitureFactory(); const modernSofa = modernFactory.createSofa(); const modernTable = modernFactory.createCoffeeTable(); console.log("现代风格家具套件:"); console.log(modernSofa.sitOn()); // 输出:坐现代风格沙发:简约布艺,坐感偏硬 console.log(modernTable.putThings()); // 输出:用现代风格茶几:钢化玻璃桌面,金属支架 console.log("\n欧式风格家具套件:"); // 选择欧式风格家具套件 const europeanFactory = new EuropeanFurnitureFactory(); const europeanSofa = europeanFactory.createSofa(); const europeanTable = europeanFactory.createCoffeeTable(); console.log(europeanSofa.sitOn()); // 输出:坐欧式风格沙发:雕花皮质,坐感柔软 console.log(europeanTable.putThings()); // 输出:用欧式风格茶几:实木雕花桌面,雕花支架 -
场景解析:
- 抽象工厂解决的是「产品族」的问题:选择「现代工厂」就只能拿到现代沙发 + 现代茶几,不会出现「现代沙发配欧式茶几」的混搭情况,保证产品的一致性;
- 新增风格 (如中式风格) 只需新增「中式沙发 + 中式茶几 + 中式家具工厂」,无需修改原有代码;但如果新增家具类型 (如椅子),则需要修改抽象工厂和所有具体工厂 (这是抽象工厂的小缺点);
三种工厂模式的对比
| 特性 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 目的 | 创建单一产品 | 创建单一产品,但延迟到子类 | 创建产品族 |
| 复杂度 | 低 | 中 | 高 |
| 灵活性 | 低 | 中 | 高 |
| 扩展性 | 添加新产品需要修改工厂类 | 添加新产品需要添加新的工厂类 | 添加新产品族容易,添加新产品困难 |
| 使用场景 | 产品种类少,不需要频繁扩展 | 产品种类多,需要频繁扩展 | 需要创建一系列相关产品 |
使用场景
简单工厂使用场景
-
核心特点:一个工厂类封装所有产品的创建逻辑,客户端传入参数即可获取对应实例;优点是简单、减少重复代码,缺点是新增产品需修改工厂 (违反开闭原则);
-
封装通用请求 / API 调用 (最典型):前端项目中,不同接口的请求方式 (GET/POST)、参数格式、拦截逻辑可能不同,用简单工厂统一创建请求实例,避免重复写请求逻辑;// 定义请求参数类型(通用) interface RequestParams { [key: string]: string | number | boolean; } // 定义请求类型枚举(替代魔法字符串) enum RequestMethod { GET = "get", POST = "post" } // 抽象请求类 abstract class Request { // 抽象方法:强制子类实现,返回值为 Promise<Response>(符合 fetch 返回类型) abstract send(): Promise<Response>; } // 具体请求:GET请求 class GetRequest extends Request { // 显式声明属性并添加类型约束 public url: string; public params: RequestParams; constructor(url: string, params: RequestParams) { super(); this.url = url; this.params = params; } send(): Promise<Response> { return fetch(`${this.url}?${new URLSearchParams(this.params)}`, { method: RequestMethod.GET }); } } // 具体请求:POST请求 class PostRequest extends Request { // 显式声明属性并添加类型约束 public url: string; public data: RequestParams; constructor(url: string, data: RequestParams) { super(); this.url = url; this.data = data; } send(): Promise<Response> { return fetch(this.url, { method: RequestMethod.POST, body: JSON.stringify(this.data), headers: { "Content-Type": "application/json" } }); } } // 简单工厂:请求工厂 class RequestFactory { /** * 创建请求实例 * @param type 请求类型(get/post) * @param url 请求地址 * @param data GET为参数,POST为请求体 * @returns 请求实例 */ static createRequest( type: string | RequestMethod, url: string, data: RequestParams ): Request { const normalizedType = type.toLowerCase(); switch (normalizedType) { case RequestMethod.GET: return new GetRequest(url, data); case RequestMethod.POST: return new PostRequest(url, data); default: throw new Error(`不支持${type}请求`); } } } // 调用示例(客户端只需传类型,无需关心请求细节) const userRequest = RequestFactory.createRequest("get", "/api/user", { id: 1 }); userRequest.send().then(res => res.json()).then(data => console.log("用户数据:", data)); // 也可以使用枚举传参(更规范) const loginRequest = RequestFactory.createRequest(RequestMethod.POST, "/api/login", { username: "test", password: "123456" }); loginRequest.send().then(res => res.json()).then(data => console.log("登录结果:", data)); -
弹窗 / 提示组件的创建(UI 组件场景):项目中常用的 Toast、Alert、Confirm 弹窗,逻辑相似但样式 / 交互不同,用简单工厂统一创建,减少页面重复代码;// 弹窗类型枚举(优化魔法字符串) enum ModalType { Toast = "toast", Alert = "alert" } // 抽象弹窗类 abstract class Modal { // 抽象方法:强制子类实现,无返回值(void) abstract show(): void; } // 轻提示弹窗 class Toast extends Modal { show(): void { console.log("轻提示:操作成功"); } } // 警告弹窗 class Alert extends Modal { show(): void { console.log("警告弹窗:数据异常"); } } // 简单工厂:弹窗工厂 class ModalFactory { /** * 创建弹窗实例 * @param type 弹窗类型(toast/alert) * @returns 弹窗实例 */ static createModal(type: string | ModalType): Modal { // 统一转小写,兼容大小写传参(如 "Toast"/"ALERT") const normalizedType = type.toLowerCase(); switch (normalizedType) { case ModalType.Toast: return new Toast(); case ModalType.Alert: return new Alert(); default: return new Toast(); // 默认返回轻提示 } } } // 页面调用(兼容原有字符串传参,也支持枚举传参) ModalFactory.createModal("toast").show(); // 原有调用方式 ModalFactory.createModal(ModalType.Alert).show(); // 更规范的枚举调用方式
工厂方法使用场景
-
核心特点:一个产品对应一个工厂,新增产品只需新增 「产品类 + 工厂类」,无需修改原有代码 (符合开闭原则);缺点是类数量会增加;
-
多渠道登录 / 支付集成(核心场景):项目需集成微信、支付宝、手机号等登录方式,每种登录的逻辑独立且可能后续新增 (如微博登录),用工厂方法解耦;// 定义登录请求参数类型 interface WxLoginParams { code: string; } interface PhoneLoginParams { phone: string; code: string; } // 抽象登录产品 abstract class Login { /** * 登录方法(抽象方法,子类需实现) * @param credentials 登录凭证(不同登录方式参数不同) * @returns Promise<Response> fetch 请求返回值 */ abstract login(...credentials: any[]): Promise<Response>; } // 具体产品:微信登录 class WxLogin extends Login { /** * 微信登录 * @param code 微信授权码 * @returns Promise<Response> */ login(code: string): Promise<Response> { const params: WxLoginParams = { code }; return fetch("/api/login/wx", { method: "POST", body: JSON.stringify(params), headers: { "Content-Type": "application/json" } }); } } // 具体产品:手机号登录 class PhoneLogin extends Login { /** * 手机号登录 * @param phone 手机号 * @param code 验证码 * @returns Promise<Response> */ login(phone: string, code: string): Promise<Response> { const params: PhoneLoginParams = { phone, code }; return fetch("/api/login/phone", { method: "POST", body: JSON.stringify(params), headers: { "Content-Type": "application/json" } }); } } // 抽象工厂:登录工厂 abstract class LoginFactory { // 抽象方法:创建登录实例 abstract createLogin(): Login; } // 具体工厂:微信登录工厂 class WxLoginFactory extends LoginFactory { createLogin(): Login { return new WxLogin(); } } // 具体工厂:手机号登录工厂 class PhoneLoginFactory extends LoginFactory { createLogin(): Login { return new PhoneLogin(); } } // 调用示例(保留原有调用方式,也可添加类型提示) const wxLoginFactory = new WxLoginFactory(); wxLoginFactory.createLogin().login("wx_code_123"); // 微信登录 const phoneLoginFactory = new PhoneLoginFactory(); phoneLoginFactory.createLogin().login("13800138000", "123456"); // 手机号登录 // 扩展示例:新增微博登录(符合开闭原则) class WbLogin extends Login { login(token: string): Promise<Response> { return fetch("/api/login/wb", { method: "POST", body: JSON.stringify({ token }), headers: { "Content-Type": "application/json" } }); } } class WbLoginFactory extends LoginFactory { createLogin(): Login { return new WbLogin(); } } const wbLoginFactory = new WbLoginFactory(); wbLoginFactory.createLogin().login("wb_token_789"); // 微博登录 -
多环境 API 适配(开发 / 测试 / 生产环境):不同环境的 API 基地址、拦截逻辑不同,且可能新增预发布环境,用工厂方法解耦;
抽象工厂使用场景
-
核心特点:一个工厂创建「一组相关 / 配套的产品」 (产品族),保证产品的一致性;缺点是新增产品种类需修改抽象工厂 (如新增「椅子」需改家具工厂);
-
主题系统(最典型:暗黑 / 浅色主题套件):项目的主题包含「按钮、输入框、表格」等组件样式,每种主题的组件样式是配套的,用抽象工厂保证主题统一;// 定义主题类型枚举(优化魔法字符串,便于扩展) enum ThemeType { Light = "light", Dark = "dark" } // 产品族1:按钮(抽象类) abstract class Button { // 抽象渲染方法,返回值为字符串(HTML 片段) abstract render(): string; } // 具体产品:浅色按钮 class LightButton extends Button { render(): string { return "<button style='background:#fff;'>浅色按钮</button>"; } } // 具体产品:暗黑按钮 class DarkButton extends Button { render(): string { return "<button style='background:#333;'>暗黑按钮</button>"; } } // 产品族2:输入框(抽象类) abstract class Input { // 抽象渲染方法,返回值为字符串(HTML 片段) abstract render(): string; } // 具体产品:浅色输入框 class LightInput extends Input { render(): string { return "<input style='border:1px solid #eee;'>"; } } // 具体产品:暗黑输入框 class DarkInput extends Input { render(): string { return "<input style='border:1px solid #666;'>"; } } // 抽象工厂:主题工厂(定义创建所有产品族的方法) abstract class ThemeFactory { // 抽象方法:创建按钮 abstract createButton(): Button; // 抽象方法:创建输入框 abstract createInput(): Input; } // 具体工厂:浅色主题工厂(创建浅色套件) class LightThemeFactory extends ThemeFactory { createButton(): Button { return new LightButton(); } createInput(): Input { return new LightInput(); } } // 具体工厂:暗黑主题工厂(创建暗黑套件) class DarkThemeFactory extends ThemeFactory { createButton(): Button { return new DarkButton(); } createInput(): Input { return new DarkInput(); } } // 调用示例(切换主题只需换工厂,保证所有组件样式统一) // 暗黑主题 const darkThemeFactory = new DarkThemeFactory(); console.log(darkThemeFactory.createButton().render()); // 输出:<button style='background:#333;'>暗黑按钮</button> console.log(darkThemeFactory.createInput().render()); // 输出:<input style='border:1px solid #666;'> // 浅色主题(扩展示例) const lightThemeFactory = new LightThemeFactory(); console.log(lightThemeFactory.createButton().render()); // 输出:<button style='background:#fff;'>浅色按钮</button> console.log(lightThemeFactory.createInput().render()); // 输出:<input style='border:1px solid #eee;'> -
多端适配(PC / 移动端组件套件):PC 端和移动端的「导航栏、列表、弹窗」组件样式 / 交互不同,且是配套的,用抽象工厂创建对应端的组件套件;// ------------- 产品族定义 ------------- // 产品1:导航(抽象类) abstract class Nav { // 可补充导航的通用抽象方法(如渲染) abstract render(): string; } // 产品2:列表(抽象类) abstract class List { // 可补充列表的通用抽象方法(如渲染) abstract render(): string; } // ------------- 具体产品:PC 端 ------------- class PcNav extends Nav { render(): string { return "<nav style='display: flex; justify-content: space-between;'>PC 端导航</nav>"; } } class PcList extends List { render(): string { return "<ul style='display: grid; grid-template-columns: repeat(3, 1fr);'>PC 端列表</ul>"; } } // ------------- 具体产品:移动端 ------------- class MobileNav extends Nav { render(): string { return "<nav style='display: flex; flex-direction: column;'>移动端导航</nav>"; } } class MobileList extends List { render(): string { return "<ul style='display: flex; flex-direction: column;'>移动端列表</ul>"; } } // ------------- 抽象工厂:端适配工厂 ------------- abstract class DeviceFactory { // 抽象方法:创建导航(返回 Nav 类型实例) abstract createNav(): Nav; // 抽象方法:创建列表(返回 List 类型实例) abstract createList(): List; } // ------------- 具体工厂:PC 端工厂 ------------- class PcFactory extends DeviceFactory { createNav(): Nav { return new PcNav(); } createList(): List { return new PcList(); } } // ------------- 具体工厂:移动端工厂 ------------- class MobileFactory extends DeviceFactory { createNav(): Nav { return new MobileNav(); } createList(): List { return new MobileList(); } } // ------------- 调用示例 ------------- // PC 端渲染 const pcFactory = new PcFactory(); console.log("PC 端导航:", pcFactory.createNav().render()); console.log("PC 端列表:", pcFactory.createList().render()); // 移动端渲染 const mobileFactory = new MobileFactory(); console.log("移动端导航:", mobileFactory.createNav().render()); console.log("移动端列表:", mobileFactory.createList().render()); -
国际化(i18n)文案套件:不同语言的「登录页文案、首页文案、个人中心文案」是配套的,用抽象工厂创建对应语言的文案套件;
三种工厂模式的核心选型思路
| 模式 | 核心适用场景 | 前端典型案例 | 选型关键 |
|---|---|---|---|
| 简单工厂 | 产品固定、轻量创建 | 请求封装、基础弹窗、格式化工具 | 产品类型少,不常新增 |
| 工厂方法 | 产品易扩展、单一产品创建 | 多渠道登录 / 支付、图表组件、多环境适配 | 产品类型可能新增,逻辑独立 |
| 抽象工厂 | 产品族创建、保证配套一致性 | 主题系统、多端适配、国际化文案 | 需创建一组配套产品 |
设计原则
上一篇