如何在TypeScript中实现不违反OCP的工厂模式?
如何在TypeScript中实现符合开闭原则(OCP)的工厂模式
嗨,我完全懂你的困扰——那些满是if/else或者switch的工厂示例确实让人头疼,完全违背了开闭原则的初衷。其实在TypeScript里,我们完全可以复刻Java中那种符合OCP的工厂实现,核心思路就是把产品注册逻辑和实例化逻辑解耦,让新增产品时不用修改工厂类本身。
核心思路
不再用硬编码的条件判断来匹配产品类型,而是用一个**注册表(Registry)**来存储产品类型标识与对应构造函数的映射。新增产品时,只需要向注册表中注册新的产品类,完全不需要改动工厂的核心创建逻辑。
完整实现示例
第一步:定义产品接口
首先我们需要一个所有产品都要实现的接口,保证产品的一致性:
interface Product { operation(): string; }
第二步:实现具体产品类
创建几个具体的产品实现,这里以简单的示例类为例:
class ConcreteProductA implements Product { operation(): string { return "输出结果:ConcreteProductA"; } } class ConcreteProductB implements Product { operation(): string { return "输出结果:ConcreteProductB"; } }
第三步:实现符合OCP的工厂类
这个工厂类包含注册表、注册方法和创建方法,核心逻辑完全不需要修改就能支持新产品:
class ProductFactory { // 私有注册表:存储产品类型标识 -> 产品构造函数的映射 private static productRegistry: Record<string, new () => Product> = {}; // 对外暴露的注册方法:新增产品时只需要调用这个方法 public static registerProduct(type: string, productClass: new () => Product): void { ProductFactory.productRegistry[type] = productClass; } // 产品创建方法:直接从注册表取构造函数,无需条件判断 public static createProduct(type: string): Product { const ProductClass = ProductFactory.productRegistry[type]; if (!ProductClass) { throw new Error(`不支持的产品类型:${type}`); } return new ProductClass(); } }
第四步:使用工厂
注册产品并创建实例,新增产品时只需要添加注册代码即可:
// 注册已有的产品(可以在任何地方注册,比如单独的模块) ProductFactory.registerProduct("A", ConcreteProductA); ProductFactory.registerProduct("B", ConcreteProductB); // 创建产品实例 const productA = ProductFactory.createProduct("A"); console.log(productA.operation()); // 输出:输出结果:ConcreteProductA const productB = ProductFactory.createProduct("B"); console.log(productB.operation()); // 输出:输出结果:ConcreteProductB
扩展支持带参数的产品构造函数
如果你的产品类需要构造参数,只需要稍微调整工厂类的类型定义,让它支持传递参数:
// 调整后的工厂类 class ProductFactory { private static productRegistry: Record<string, new (...args: any[]) => Product> = {}; public static registerProduct(type: string, productClass: new (...args: any[]) => Product): void { ProductFactory.productRegistry[type] = productClass; } public static createProduct(type: string, ...args: any[]): Product { const ProductClass = ProductFactory.productRegistry[type]; if (!ProductClass) { throw new Error(`不支持的产品类型:${type}`); } return new ProductClass(...args); } } // 带参数的产品类示例 class ConcreteProductC implements Product { private readonly name: string; constructor(name: string) { this.name = name; } operation(): string { return `输出结果:ConcreteProductC,名称:${this.name}`; } } // 注册并使用带参数的产品 ProductFactory.registerProduct("C", ConcreteProductC); const productC = ProductFactory.createProduct("C", "测试产品C"); console.log(productC.operation()); // 输出:输出结果:ConcreteProductC,名称:测试产品C
为什么符合OCP?
- 对扩展开放:新增产品时,只需要实现
Product接口,然后调用registerProduct方法注册,完全不需要修改ProductFactory的任何内部逻辑。 - 对修改关闭:工厂的核心创建逻辑(
createProduct)永远不需要改动,避免了因为修改旧代码引入bug的风险。
内容的提问来源于stack exchange,提问作者Rama Jakaria
相关产品推荐
相关产品推荐

