依赖注入
概述
依赖注入(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");
// ...
}
}注意事项
- 循环依赖:避免服务之间的循环依赖
- 生命周期:注意服务的生命周期,避免内存泄漏
- 类型安全:使用 TypeScript 泛型确保类型安全