MST

星途 面试题库

面试题:TypeScript类构造函数在复杂依赖注入场景下的设计与优化

假设你正在开发一个大型前端应用,有一个`UserService`类,其构造函数依赖于`HttpClient`类(用于网络请求)和`Logger`类(用于记录日志)。`HttpClient`和`Logger`本身也可能有复杂的依赖关系。请设计一个合理的依赖注入方案,使用TypeScript类构造函数来处理这些依赖,确保代码的可测试性、可维护性和高效性,并说明在这种场景下如何避免循环依赖问题。
42.3万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. 依赖注入方案设计

首先定义HttpClientLogger接口以及其实现类。假设HttpClient用于发送网络请求,Logger用于记录日志。

// HttpClient接口
interface HttpClient {
  sendRequest(url: string, method: string, data?: any): Promise<any>;
}

// Logger接口
interface Logger {
  log(message: string): void;
}

// HttpClient实现类
class RealHttpClient implements HttpClient {
  async sendRequest(url: string, method: string, data?: any): Promise<any> {
    // 实际的网络请求逻辑
    return Promise.resolve({});
  }
}

// Logger实现类
class RealLogger implements Logger {
  log(message: string): void {
    console.log(message);
  }
}

// UserService类,依赖HttpClient和Logger
class UserService {
  private httpClient: HttpClient;
  private logger: Logger;

  constructor(httpClient: HttpClient, logger: Logger) {
    this.httpClient = httpClient;
    this.logger = logger;
  }

  async getUserData(userId: string) {
    try {
      const response = await this.httpClient.sendRequest(`/users/${userId}`, 'GET');
      this.logger.log(`Fetched user data for ${userId}`);
      return response;
    } catch (error) {
      this.logger.log(`Error fetching user data for ${userId}: ${error}`);
      throw error;
    }
  }
}

2. 如何使用依赖注入

在应用的入口或模块初始化阶段,可以这样创建UserService实例:

// 创建HttpClient和Logger实例
const httpClient = new RealHttpClient();
const logger = new RealLogger();

// 使用依赖注入创建UserService实例
const userService = new UserService(httpClient, logger);

3. 避免循环依赖问题

  • 正向依赖原则:确保依赖关系是单向的,尽量让高层次模块依赖低层次模块,而不是相互依赖。例如在上述例子中,UserService依赖HttpClientLogger,这两个类不应该反过来依赖UserService
  • 提前实例化:在应用启动时,尽早实例化所有需要的依赖。通过在入口处创建所有单例实例,可以确保在任何类尝试访问依赖之前,依赖已经被创建,从而避免循环依赖。例如在上述代码中,先创建httpClientlogger,再创建userService
  • 使用接口隔离:通过接口来定义依赖关系,而不是具体的实现类。这样可以降低耦合度,减少循环依赖的可能性。例如UserService依赖HttpClientLogger接口,而不是具体的RealHttpClientRealLogger类,这使得代码结构更加清晰,并且在替换实现时更加容易。