【问题标题】:Avoid Circular Dependency injection避免循环依赖注入
【发布时间】:2017-12-13 17:21:31
【问题描述】:

在我的 Angular 应用程序中,我正在实现 2 个服务:

  • 身份验证服务
  • 空闲服务

这两个服务相互依赖:

  1. 当我注销时,我必须停止观察用户的空闲状态(通过调用 IdleService 中的方法)
  2. 当用户超时时,我必须从AuthenticationService 调用logout 方法来注销用户。

这种方法会导致循环依赖问题。你知道如何避免它或如何用更好的方法改变我的实际方法吗?

【问题讨论】:

  • 也许使用中介模式来处理服务到服务的通信?

标签: angular dependency-injection circular-dependency


【解决方案1】:

Dependency Injection in .NET 2nd edition (§6.3) 一书中,Mark Seemann 和我描述了依赖循环通常是由Single Responsibility Principle 违规引起的。我的印象是,在您的具体情况下也是如此。当一个类有许多不是很内聚的方法时,通常会违反 SRP。

解决方案是将AuthenticationServiceIdleService(或什至两者)两个类中的任何一个拆分为更小的类。

【讨论】:

  • 我认为 SRP 体现在 LLai 的解决方案中,他从 Authentication 服务中去掉了空闲管理,只保留在 Idleness 服务中。否则,AuthenticationService 只有处理身份验证的方法(loginlogout),而IdleService 只有空闲方法,用于监视用户空闲,触发超时......也许我看不清楚但我想不出如何将这些服务中的每一项拆分成更小的类
  • LLai 的解决方案也处理 SRP,尽管他没有明确提及这一点。我无法真正评论如何准确修复 SRP 违规,因为您没有包含任何代码。但是分离方法只是对抗 SRP 的一种方法,但并不总能解决问题,因为即使是单一方法类仍然可能违反 SRP。
【解决方案2】:

我会说身份验证服务负责保存、管理和公开用户身份验证的状态。我的意思是,身份验证服务根本不应该关心用户的闲置。这是空闲服务的责任。

所以我会在身份验证服务中创建一个 BehaviorSubject 来广播用户身份验证的状态。空闲服务可以使用主题来确定空闲计时器何时需要启动/停止。

export class AuthService {
    user: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

    logIn(){
        // logic to log user in
        this.user.next(true);
    }
    logOut(){
        // logic to log user out
        this.user.next(false);
    }
}

现在idle服务可以注入auth服务并决定如何处理idle timer

export class IdleService {
    constructor(private authService: AuthService){
        this.authService.user.subscribe(res => {
            // watch user authentication state and determine how to handle idle timer
        });
    }

    idleOut(){
        this.authService.logOut();
    }
}

为此,您必须在应用程序加载时注入 IdleService(例如在 app.component 中,以便 IdleService 构造函数方法运行。我不确定这是否是最优雅的解决方案,但这是第一件事想到了。这是一个非常基本的stackblitz 演示这个概念。

【讨论】:

    【解决方案3】:

    您可以在IdleService 中创建一个可观察对象或主题,如果时间到了,它会返回断开连接消息。这样,只有AuthenticationService 导入IdleService。例如:

    import { IdleService } from './services/idle.service';
    import { Observable } from 'rxjs/Observable'; 
    
    @Injectable()
    export class AuthenticationService {
    
      disconnect: Obervable<boolean>;
    
      constructor(private idleService: IdleService) { 
        this.disconnect = this.idleService.disconnectObservable;
        this.disconnect.subscribe(res => {
          if (res === true) {
            this.logout();
          }
        });
      }
    
      logout() {
      //some code here
      }
    }
    

    IdleService

    import { Observable } from 'rxjs/Observable';
    
    @Injectable()
    export class IdleService {
    
      public disconnectObservable: Observable<boolean>;
    
      constructor() {
        this.disconnectObservable = new Observable(observer => {
          // insert your logic here
          setTimeout(() => {
              observer.next('true');
          }, 1000);
    
          setTimeout(() => {
              observer.complete();
          }, 3000);
        });
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-09-03
      • 2013-02-06
      • 1970-01-01
      • 1970-01-01
      • 2012-02-15
      • 2012-08-10
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      相关资源
      最近更新 更多