如何在NestJS中正确使用工厂模式?两种实现方案求鉴
问题
我在NestJS中实现了两种基于工厂模式的动态模块方案,不确定哪种才是正确用法:
方案一:直接通过useFactory返回对应驱动服务
该方案未设置专属服务,而是根据配置返回对应驱动服务,但会导致DriverService1与DriverService2被一同实例化:
export const createFactory = (config: IConfig): Provider<IService> => { return { provide: 'FACTORY', useFactory: (service1: DriverService1, service2: DriverService2): IService => { if (config.driver == 'DriverService1') { return service1; } else if (config.driver == 'DriverService2') { return service2; } throw new Error('not implemented') }, inject: [DriverService1, DriverService2], } }; @Module({}) export class MyModule { static register(config?: IConfig): DynamicModule { const factory = createFactory(config) return { module: MyModule, providers: [ { provide: 'CONFIG', useValue: config, }, DriverService1, DriverService2, factory ], exports: [factory], }; } }
方案二:通过专属服务内部实现工厂模式
创建专属服务MyModuleService,在服务内部管理驱动实例,仅在需要时实例化对应驱动:
interface IDriver { action1(): void action2(): void action3(): void } class Driver1 implements IDriver { public action1(): void { console.log("DriverService1 action1") } public action2(): void { console.log("DriverService1 action2") } public action3(): void { console.log("DriverService1 action3") } } class Driver2 implements IDriver { public action1(): void { console.log("DriverService2 action1") } public action2(): void { console.log("DriverService2 action2") } public action3(): void { console.log("DriverService2 action3") } } export const createFactory = (config: IConfig): Provider<MyModuleSerice> => { return { provide: 'BROKER_FACTORY', useFactory: (service: MyModuleSerice): MyModuleSerice => { if (config.driver == 'Driver1') { service.setDriver(new Driver1()); } else if (config.driver == 'Driver2') { service.setDriver(new Driver2()); } else { throw new Error('not implemented') } return service }, inject: [MyModuleSerice], } }; @Module({}) export class MyModule { static register(config?: IConfig): DynamicModule { const factory = createFactory(config) return { module: MyModule, providers: [ { provide: 'CONFIG', useValue: config, }, factory ], exports: [factory], }; } } @Injectable() class MyModuleSerice { protected driver: IDriver constructor() { } public setDriver(driver: IDriver) { this.driver = driver } public doSomething(): void { this.driver.action1() this.driver.action2() } public doSomething2(): void { this.driver.action1() this.driver.action3() } }
请问哪种实现方式才是NestJS中工厂模式的正确用法?
分析与结论
两种方案都符合NestJS语法规范,但适用场景不同,需根据实际需求选择:
方案一的优劣势
- 优势:逻辑直接,依赖注入容器自动管理驱动服务的生命周期,若驱动本身依赖其他NestJS服务(带
@Injectable装饰的类),能自动完成注入。 - 劣势:无论配置选哪个驱动,所有驱动服务都会在模块初始化时被实例化,若驱动初始化成本高(比如建立外部连接),会造成不必要的资源浪费。
方案二的优劣势
- 优势:仅实例化配置指定的驱动,避免资源冗余;通过专属服务封装驱动逻辑,对外暴露统一业务方法(如
doSomething),调用方无需感知具体驱动实现,符合封装原则。 - 劣势:若驱动需要依赖NestJS其他服务,手动
new Driver1()的方式无法利用依赖注入容器,需额外处理依赖传递;专属服务增加了一层封装,逻辑相对复杂。
推荐用法
如果你的驱动服务是无状态、初始化成本低的类,方案一简单直接,可直接使用;
如果驱动服务初始化成本高,或者需要对外提供统一的业务接口,推荐方案二,但可以优化驱动实例化方式:将驱动类标记为@Injectable(),通过依赖注入获取实例而非手动new,示例修改如下:
useFactory: (service: MyModuleSerice, driver1: Driver1, driver2: Driver2): MyModuleSerice => { if (config.driver == 'Driver1') { service.setDriver(driver1); } else if (config.driver == 'Driver2') { service.setDriver(driver2); } else { throw new Error('not implemented') } return service }, inject: [MyModuleSerice, Driver1, Driver2],
同时在模块providers中注册Driver1和Driver2,结合@Optional()装饰器或动态注册逻辑,确保仅实例化需要的驱动。
另外,更符合NestJS最佳实践的方式是利用自定义提供者的useClass动态注册,直接根据配置添加对应驱动提供者,避免实例化未使用的服务:
@Module({}) export class MyModule { static register(config?: IConfig): DynamicModule { const driverProvider = { provide: 'DRIVER', useClass: config.driver === 'DriverService1' ? DriverService1 : DriverService2, }; return { module: MyModule, providers: [ { provide: 'CONFIG', useValue: config }, driverProvider, { provide: 'FACTORY', useFactory: (driver: IService) => driver, inject: ['DRIVER'], } ], exports: ['FACTORY'], }; } }
这种方式只会实例化配置指定的驱动,同时保留依赖注入的优势,逻辑简洁高效。
内容的提问来源于stack exchange,提问作者user192344
相关产品推荐
相关产品推荐

