在nest.js中实施策略

And*_*rew 5 singleton dependency-injection strategy-pattern nestjs

我正在尝试使用服务的策略模式,但是我尝试用作策略上下文的模块似乎只坚持两者之一。这是示例代码:

动物模块.ts

@Module({})
export class AnimalModule {
    static register(strategy): DynamicModule {
        return {
            module: AnimalModule,
            providers: [{ provide: 'STRATEGY', useValue: strategy }, AnimalService],
            imports: [],
            exports: [AnimalService]
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

动物服务.ts

@Injectable()
export class AnimalService {
    constructor (@Inject('STRATEGY') private strategy) {
        this.strategy = strategy
    }

    public makeSound() {
        return this.strategy.makeSound()
    }
}
Run Code Online (Sandbox Code Playgroud)

猫模块.ts

@Module({
    imports: [
        AnimalModule.register(catStrategy),
    ],
    controllers: [CatController],
    providers: [CatService],
})
export class CatModule {}
Run Code Online (Sandbox Code Playgroud)

猫服务.ts

@Injectable()
export class CatService {
    constructor(
        private readonly animalService: AnimalService,
    ) {}

    public makeSound() {
        return this.animalService.makeSound()
    }
}
Run Code Online (Sandbox Code Playgroud)

狗模块.ts

@Module({
    imports: [
        AnimalModule.register(dogStrategy),
    ],
    controllers: [DogController],
    providers: [DogService],
})
export class DogModule {}
Run Code Online (Sandbox Code Playgroud)

狗服务.ts

@Injectable()
export class DogService {
    constructor(
        private readonly animalService: AnimalService,
    ) {}

    public makeSound() {
        return this.animalService.makeSound()
    }
}
Run Code Online (Sandbox Code Playgroud)

猫策略.ts

class CatStrategy {
    public makeSound() {
        return 'meow';
    }
}

export const catStrategy = new CatStrategy();
Run Code Online (Sandbox Code Playgroud)

复制问题的仓库:https://github.com/kunukmak/nestjs-strategy-problem-example

澄清一下,在这种情况下,catService.makeSound 和dogService.makeSound 都返回“meow”。可以让狗叫吗?

Fra*_*cia 3

我认为您正在寻找类似以下内容的内容。在此处查看存储库以获取完整示例。您可以在下面看到,我们正在从 AnimalModule 类注册 DynamicModule:

@Module({
    imports: [AnimalModule.register()],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule {}
Run Code Online (Sandbox Code Playgroud)

从调用返回的 DynamicModuleregister()负责确定要提供的 AnimalModule 的实现。这意味着我们可以根据环境中的配置自定义 AnimalModule。

@Module({})
export class AnimalModule {
    public static register(): DynamicModule {
        const AnimalClassProvider = AnimalModule.getClassProvider();
        return {
            module: AnimalModule,
            controllers: [AnimalController],
            providers: [AnimalClassProvider],
            exports: [AnimalClassProvider],
        };
    }

    private static getClassProvider(): ClassProvider<AnimalService> {
        const animalStrategy = process.env.ANIMAL_STRATEGY as AnimalStrategy;
        const AnimalServiceClass = AnimalModule.getClassFromStrategy(animalStrategy);
        return {
            provide: AnimalService,
            useClass: AnimalServiceClass,
        };
    }

    private static getClassFromStrategy(strategy: AnimalStrategy): Type<AnimalService> {
        switch (strategy) {
            case AnimalStrategy.CAT: return CatService;
            case AnimalStrategy.DOG: return DogService;
            default: return AnimalService;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,AnimalStrategy 只是一个枚举,用于确定我们应该提供哪种服务实现。

通过这种方法,我们允许 Nest 正确构建 Provider 及其所有依赖项。我们只负责告诉 Nest 当遇到 AnimalService 依赖时它将构造哪个实现。这使得我们的应用程序的其余部分不知道实现,只使用我们的 AnimalService 抽象。

来自我们的 AnimalController:

@Controller('animal')
export class AnimalController {
    constructor(private readonly animalService: AnimalService) {}

    @Post()
    create(@Body() createAnimalDto: CreateAnimalDto) {
        return this.animalService.create(createAnimalDto);
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

到我们应用程序中的另一个服务:

@Injectable()
export class PetOwnerService {
    constructor(
        private readonly animalService: AnimalService,
        private readonly petOwnerService: PetOwnerService,
    ) {}

    feedPet(petName: string) {
        const petIsHungry = this.petOwnerService.isPetHungry(petName);
        if (petIsHungry) this.animalService.feed(petName);
        // ... 
    }
}
Run Code Online (Sandbox Code Playgroud)