TypeScript工厂类如何避免switch匹配enum以符合开闭原则
解决方案
首先明确:可以通过调整工厂的实现逻辑满足开闭原则,不需要修改工厂原有代码即可扩展新的动物子类,也可以根据实际场景选择是否保留枚举作为入参。
方案1:保留枚举入参,新增注册机制(适配API传枚举值的场景)
核心思路是给工厂类添加全局的类型-构造函数映射表,以及配套的注册方法,新增动物子类时只需要注册对应关系即可,不需要修改工厂原有创建逻辑。
优化后完整代码
abstract class Animal { abstract sound(): void; } class Cat extends Animal { sound(){ console.log("meow"); } } class Dog extends Animal { sound(){ console.log("bark"); } } enum AnimalType { Cat = 1, Dog = 2, } class AnimalFactory { // 存储枚举值到对应动物构造函数的映射 private static animalConstructors = new Map<AnimalType, new () => Animal>(); // 提供公开注册方法,新增动物时调用即可 static register(type: AnimalType, constructor: new () => Animal) { this.animalConstructors.set(type, constructor); } static createAnimal(type: AnimalType): Animal { const constructor = this.animalConstructors.get(type); if (!constructor) { throw new Error("unsupported animal type"); } return new constructor(); } } // 初始化时注册现有动物类型 AnimalFactory.register(AnimalType.Cat, Cat); AnimalFactory.register(AnimalType.Dog, Dog); // 使用方式不变 const cat = AnimalFactory.createAnimal(AnimalType.Cat); const dog = AnimalFactory.createAnimal(AnimalType.Dog); cat.sound(); // 输出meow dog.sound(); // 输出bark
扩展示例
如果后续要新增鸟类,只需要做三步,完全不需要修改AnimalFactory的原有代码:
- 新增
Bird子类继承Animal - 在
AnimalType枚举中新增Bird = 3项 - 调用
AnimalFactory.register(AnimalType.Bird, Bird)完成注册
方案2:移除枚举,直接传入构造函数作为入参(类型更安全,无需注册)
如果不需要适配API传枚举值的场景,直接用构造函数作为入参是更简洁的方案,天然符合开闭原则,新增子类不需要任何额外操作:
优化后完整代码
abstract class Animal { abstract sound(): void; } class Cat extends Animal { sound(){ console.log("meow"); } } class Dog extends Animal { sound(){ console.log("bark"); } } class AnimalFactory { static createAnimal<T extends Animal>(constructor: new () => T): T { return new constructor(); } } // 使用方式 const cat = AnimalFactory.createAnimal(Cat); const dog = AnimalFactory.createAnimal(Dog); cat.sound(); dog.sound();
内容的提问来源于stack exchange,提问作者szebert
相关产品推荐
相关产品推荐

