实现之间的依赖注入切换

Dependency injection switch between implementations

我有一个应用程序可以在两种不同的模式下运行:演示模式和正常模式。演示模式仅在用户没有帐户且只想试用该应用程序以查看其工作原理时使用。

现在假设我们有一个在应用程序中使用的服务。例如,检索 Account 并验证用户的服务:

abstract class AccountService {
  authenticate(username: string, password: string): Observable<Boolean>;
  getAccount(): Observable<Account>;
}

对于这项服务,我们有两种实现方式,一种用于一种模式。 AccountServiceImpl,对于正常模式,向某个服务器执行 HTTP 请求以检索帐户。而 AccountServiceDemoImpl 只是 returns 一个硬编码的

假设我们有一个 AccountComponent 组件向用户显示帐户名称。对于这个组件,我们使用哪个实现并不重要,所以我们只需在其构造函数中注入一个 AccountService

一切都很好,我们可以轻松地使用任一实现来填充 AccountComponent。现在我们希望能够在用户单击登录页面上的按钮时在运行时在这些实现之间切换。所以我写了一个工厂提供者来确定使用哪个 AccountService 实现

{provide: AccountService, deps[AccountServiceImpl, AccountServiceDemoImpl], (regularService, demoService) => demoModeActivated ? demoService : regularService}

可以想象,AccountService也是在登录页面注入的,用来处理用户的认证。这意味着工厂已经被评估过了。现在,问题是此评估的结果缓存在 Angular 中。因此,当激活演示模式并且用户导航到 AccountComponent 时,将再次注入实际的 AccountServiceImpl

有没有办法清除 DI 缓存(对于一组特定的 DI 令牌)?还是我应该尝试以另一种方式处理此功能? (例如,编写另一个 AccountService 的实现,委托给 AccountServiceImplAccountServiceDemoImpl。)

最初似乎是工厂供应商会为您解决这个问题。不幸的是,工厂左轮手枪是同步的,您依赖异步调用来确定提供哪种服务。

相反,您可以使用 Resolve 守卫。在 resolve 方法中,您将 return 一个调用 auth 方法的可观察对象,然后映射到更新任一服务。然后在第一次运行后调用 first() 运算符来完成可观察对象。

它解析守卫似乎是为 return 简单的 DTO 结构而设计的,而不是服务。但这有效。

您可以在 Resolve Guard 中注入两个服务并映射到任一服务,而不是更新实例,以防您希望该服务保持单例。

export class AccountServiceResolver implements Resolve<AccountService> {

  constructor(private accountService: AccountService, demmoService: AccountDemoService) { }

  resolve(): Observable<AccountService> {

    return this.accountService.getAccount().map(account => {

      if (account.real) {
        return this.accountService;
      } else {
        return this.demoService;
      }
    }).first();
  }

}

然后在你的组件中获取这个:

ngOnInit(route: ActivatedRoute) {

  const service = route.data[0];
}