wzhudev / redi

💉 A dependency injection library for TypeScript & JavaScript, along with a binding for React.
https://redi.wzhu.dev
MIT License
110 stars 17 forks source link

Feature request: `WithNew()` with parameters #10

Closed darkyzhou closed 1 year ago

darkyzhou commented 2 years ago

当依赖项为工厂函数时,如果可以让 WithNew() 带参数传入工厂函数是有用的:

export class MyService {
  constructor(
    @WithNew(MyService.name)
    @Inject(LoggerService)
    private readonly loggerService: LoggerService
  ) {}

  foo() {
    this.loggerService.info('bar');
  }
}

useFactory: (name: string) => {
  return new LoggerService(name);
}

export class LoggerService {
  constructor(private readonly name: string) {}

  info(message: string) {
    console.info(`${this.name} ${message}`);
  }
}

上面的例子就使用了 WithNew() 里面的参数传入给工厂函数,相当于要求 DI 注入的服务具有某种规格。

具体的设计还有待商榷,如果此提议有效,很乐意帮忙提供 PR 😄

wzhudev commented 2 years ago

依赖注入的目的是期望依赖方(MyService)不用关心被依赖方(LoggerService)的实例化过程,这个 API 设计违反了依赖注入的初衷,即依赖方知道了被依赖方的构造参数。

对于你的 case,建议考虑扩展 createInstance 方法,这里是唯一的 exception。

darkyzhou commented 2 years ago

多谢,createInstance() 可以解决我的问题

但不太能理解“违反初衷”的原因。如果依赖方使用了 WithNew(),那么至少依赖方能够确定被依赖方实例只服务于前者。这时候将 WithNew() 里面传的参数视作像 deps 里面传的一样的依赖项,而从依赖项到构建出实例这个具体细节仍然由 useFactory 函数去完成,在我看来没有违反“依赖方知道了被依赖方的构造参数”

我看到 typedi 里也有类似的功能让我做到这样的事情: https://docs.typestack.community/typedi/v/develop/advanced-usage/08-custom-decorators 我可以自定义创建 Logger decorator 的参数,但这仍然类似于给这个 useFactory 函数提供了额外的依赖项让它完成实例构造