Skip to content

依赖注入

概述

依赖注入(DI)模块提供了轻量级的依赖注入容器,用于管理服务的注册和解析,实现模块间的解耦。

核心概念

什么是依赖注入

依赖注入是一种设计模式,通过容器管理对象的创建和依赖关系,而不是在代码中直接创建对象。

为什么需要 DI

  • 解耦:模块之间通过接口通信,不直接依赖实现
  • 可测试:可以轻松注入 Mock 对象进行测试
  • 可维护:集中管理依赖关系,易于修改和扩展

核心 API

getContainer

获取 DI 容器实例:

typescript
import { getContainer } from "@core/di";

const container = getContainer();

register

注册服务到容器:

typescript
// 注册单例服务
container.register<IUserService>("IUserService", UserService);

// 注册工厂函数
container.register<IUserService>("IUserService", () => {
  return new UserService();
});

// 注册实例
container.register<NetworkConfig>("NetworkConfig", () => config);

resolve

从容器中解析服务:

typescript
const userService = container.resolve<IUserService>("IUserService");

CoreServiceKeys

核心服务的键名常量:

typescript
export const CoreServiceKeys = {
  HttpClient: "HttpClient",
  ConfigManager: "ConfigManager",
  NavigationService: "NavigationService"
  // ...
};

使用示例

1. 定义服务接口

在 Shared 层定义服务契约:

typescript
// shared/contracts/IUserService.ets
export interface IUserService {
  getUserInfo(userId: string): Promise<UserInfo>;
  updateUserInfo(info: UserInfo): Promise<void>;
}

2. 实现服务

在功能包中实现服务:

typescript
// packages/user/services/UserServiceImpl.ets
import { IUserService } from "@shared/contracts";

export class UserServiceImpl implements IUserService {
  async getUserInfo(userId: string): Promise<UserInfo> {
    // 实现逻辑
  }

  async updateUserInfo(info: UserInfo): Promise<void> {
    // 实现逻辑
  }
}

3. 注册服务

在功能包初始化生命周期时注册服务:

typescript
// packages/user/UserModule.ets
import { getContainer } from "@core/di";
import { IUserService } from "@shared/contracts";

/**
 * 用户模块
 * 实现 FeatureModule 接口,支持自动注册
 */
export class UserModule implements FeatureModule {
  /**
   * 注册 DI 服务
   * @param container DI 容器
   */
  registerServices(container: Container): void {
    // 注册本模块服务
    container.register<IUserService>("UserService", () => new UserServiceImpl());
  }
}

4. 使用服务

在 ViewModel 或其他地方使用服务:

typescript
// packages/main/viewmodels/MainViewModel.ets
import { getContainer } from "@core/di";
import { IUserService } from "@shared/contracts";

@ObservedV2
export class MainViewModel extends BaseViewModel {
  private userService: IUserService;

  constructor() {
    super();
    const container = getContainer();
    this.userService = container.resolve<IUserService>("IUserService");
  }

  async loadUserInfo(): Promise<void> {
    const userInfo = await this.userService.getUserInfo("123");
    // 使用用户信息
  }
}

高级用法

工厂函数

使用工厂函数创建服务实例:

typescript
container.register<IUserService>("IUserService", () => {
  const config = container.resolve<NetworkConfig>("NetworkConfig");
  return new UserService(config);
});

单例模式

默认情况下,服务是单例的。如果需要每次都创建新实例,可以使用工厂函数:

typescript
// 单例(默认)
container.register<IUserService>("IUserService", UserService);

// 每次创建新实例
container.register<IUserService>("IUserService", () => new UserService());

依赖链

服务可以依赖其他服务:

typescript
// 注册依赖
container.register<IHttpClient>("IHttpClient", HttpClient);
container.register<IUserService>("IUserService", () => {
  const httpClient = container.resolve<IHttpClient>("IHttpClient");
  return new UserService(httpClient);
});

最佳实践

1. 使用接口

始终通过接口注册和解析服务:

typescript
// 推荐
container.register<IUserService>("IUserService", UserService);
const service = container.resolve<IUserService>("IUserService");

// 不推荐
container.register("UserService", UserService);
const service = container.resolve("UserService");

2. 使用常量键名

定义常量键名,避免字符串拼写错误:

typescript
// shared/contracts/ServiceKeys.ets
export const ServiceKeys = {
  UserService: "IUserService",
  AuthService: "IAuthService",
  // ...
};

// 使用
container.register<IUserService>(ServiceKeys.UserService, UserService);
const service = container.resolve<IUserService>(ServiceKeys.UserService);

3. 在构造函数中解析依赖

在构造函数中解析依赖,而不是在方法中:

typescript
// 推荐
export class MyViewModel {
  private userService: IUserService;

  constructor() {
    this.userService = container.resolve<IUserService>("IUserService");
  }
}

// 不推荐
export class MyViewModel {
  async loadData(): Promise<void> {
    const userService = container.resolve<IUserService>("IUserService");
    // ...
  }
}

注意事项

  1. 循环依赖:避免服务之间的循环依赖
  2. 生命周期:注意服务的生命周期,避免内存泄漏
  3. 类型安全:使用 TypeScript 泛型确保类型安全

下一步