refactor(arch): 改进 ServiceToken 设计,统一服务获取模式 (#300)
* refactor(arch): 移除全局变量,使用 ServiceToken 模式 - 创建 PluginServiceRegistry 类,提供类型安全的服务注册/获取 - 添加 ProfilerServiceToken 和 CollisionLayerConfigToken - 重构所有 __PROFILER_SERVICE__ 全局变量访问为 getProfilerService() - 重构 __PHYSICS_RAPIER2D__ 全局变量访问为 CollisionLayerConfigToken - 在 Core 类添加 pluginServices 静态属性 - 添加 getService.ts 辅助模块简化服务获取 这是 ServiceToken 模式重构的第一阶段,移除了最常用的两个全局变量。 后续可继续应用到其他模块(Camera/Audio 等)。 * refactor(arch): 改进 ServiceToken 设计,移除重复常量 - tokens.ts: 从 engine-core 导入 createServiceToken(符合规范) - tokens.ts: Token 使用接口 IProfilerService 而非具体类 - 移除 AssetPickerDialog 和 ContentBrowser 中重复的 MANAGED_ASSET_DIRECTORIES - 统一从 editor-core 导入 MANAGED_ASSET_DIRECTORIES * fix(type): 修复 IProfilerService 接口与实现类型不匹配 - 将 ProfilerData 等数据类型移到 tokens.ts 以避免循环依赖 - ProfilerService 显式实现 IProfilerService 接口 - 更新使用方使用 IProfilerService 接口类型而非具体类 * refactor(type): 移除类型重导出,改进类型安全 - 删除 ProfilerService.ts 中的类型重导出,消费方直接从 tokens.ts 导入 - PanelDescriptor 接口添加 titleZh 属性,移除 App.tsx 中的 as any - 改进 useDynamicIcon.ts 的类型安全,使用正确的 Record 类型 * refactor(arch): 为模块添加 ServiceToken 支持 - Material System: 创建 tokens.ts,定义 IMaterialManager 接口和 MaterialManagerToken - Audio: 创建预留 tokens.ts 文件,为未来 AudioManager 服务扩展做准备 - Camera: 创建预留 tokens.ts 文件,为未来 CameraManager 服务扩展做准备 遵循"谁定义接口,谁导出 Token"原则,统一服务访问模式
This commit is contained in:
@@ -11,6 +11,7 @@ import { SceneManager } from './ECS/SceneManager';
|
||||
import { IScene } from './ECS/IScene';
|
||||
import { ServiceContainer } from './Core/ServiceContainer';
|
||||
import { PluginManager } from './Core/PluginManager';
|
||||
import { PluginServiceRegistry } from './Core/PluginServiceRegistry';
|
||||
import { IPlugin } from './Core/Plugin';
|
||||
import { WorldManager } from './ECS/WorldManager';
|
||||
import { DebugConfigService } from './Utils/Debug/DebugConfigService';
|
||||
@@ -109,6 +110,14 @@ export class Core {
|
||||
*/
|
||||
private _pluginManager: PluginManager;
|
||||
|
||||
/**
|
||||
* 插件服务注册表
|
||||
*
|
||||
* 基于 ServiceToken 的类型安全服务注册表。
|
||||
* Type-safe service registry based on ServiceToken.
|
||||
*/
|
||||
private _pluginServiceRegistry: PluginServiceRegistry;
|
||||
|
||||
/**
|
||||
* Core配置
|
||||
*/
|
||||
@@ -168,6 +177,11 @@ export class Core {
|
||||
this._pluginManager.initialize(this, this._serviceContainer);
|
||||
this._serviceContainer.registerInstance(PluginManager, this._pluginManager);
|
||||
|
||||
// 初始化插件服务注册表
|
||||
// Initialize plugin service registry
|
||||
this._pluginServiceRegistry = new PluginServiceRegistry();
|
||||
this._serviceContainer.registerInstance(PluginServiceRegistry, this._pluginServiceRegistry);
|
||||
|
||||
this.debug = this._config.debug ?? true;
|
||||
|
||||
// 初始化调试管理器
|
||||
@@ -220,6 +234,39 @@ export class Core {
|
||||
return this._instance._serviceContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件服务注册表
|
||||
*
|
||||
* 用于基于 ServiceToken 的类型安全服务注册和获取。
|
||||
* For type-safe service registration and retrieval based on ServiceToken.
|
||||
*
|
||||
* @returns PluginServiceRegistry 实例
|
||||
* @throws 如果 Core 实例未创建
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { createServiceToken } from '@esengine/ecs-framework';
|
||||
*
|
||||
* // 定义服务令牌
|
||||
* const MyServiceToken = createServiceToken<IMyService>('myService');
|
||||
*
|
||||
* // 注册服务
|
||||
* Core.pluginServices.register(MyServiceToken, myServiceInstance);
|
||||
*
|
||||
* // 获取服务(可选)
|
||||
* const service = Core.pluginServices.get(MyServiceToken);
|
||||
*
|
||||
* // 获取服务(必需,不存在则抛异常)
|
||||
* const service = Core.pluginServices.require(MyServiceToken);
|
||||
* ```
|
||||
*/
|
||||
public static get pluginServices(): PluginServiceRegistry {
|
||||
if (!this._instance) {
|
||||
throw new Error('Core实例未创建,请先调用Core.create()');
|
||||
}
|
||||
return this._instance._pluginServiceRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取World管理器
|
||||
*
|
||||
|
||||
135
packages/core/src/Core/PluginServiceRegistry.ts
Normal file
135
packages/core/src/Core/PluginServiceRegistry.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 插件服务注册表
|
||||
* Plugin Service Registry
|
||||
*
|
||||
* 基于 ServiceToken 的类型安全服务注册表。
|
||||
* Type-safe service registry based on ServiceToken.
|
||||
*
|
||||
* 设计原则 | Design principles:
|
||||
* 1. 类型安全 - 使用 ServiceToken 携带类型信息
|
||||
* 2. 显式依赖 - 通过导入 token 明确表达依赖关系
|
||||
* 3. 可选依赖 - get 返回 undefined,require 抛异常
|
||||
* 4. 单一职责 - 只负责服务注册和查询,不涉及生命周期管理
|
||||
* 5. 谁定义接口,谁导出 Token - 各模块定义自己的接口和 Token
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// 服务令牌 | Service Token
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 服务令牌接口
|
||||
* Service token interface
|
||||
*
|
||||
* 用于类型安全的服务注册和获取。
|
||||
* For type-safe service registration and retrieval.
|
||||
*
|
||||
* 注意:__phantom 是必需属性,确保 TypeScript 在跨包类型解析时保留泛型类型信息。
|
||||
* Note: __phantom is a required property to ensure TypeScript preserves generic
|
||||
* type information across packages.
|
||||
*/
|
||||
export interface ServiceToken<T> {
|
||||
readonly id: symbol;
|
||||
readonly name: string;
|
||||
/**
|
||||
* Phantom type 标记(强制类型推断)
|
||||
* Phantom type marker (enforces type inference)
|
||||
*/
|
||||
readonly __phantom: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建服务令牌
|
||||
* Create a service token
|
||||
*
|
||||
* @param name 令牌名称 | Token name
|
||||
* @returns 服务令牌 | Service token
|
||||
*/
|
||||
export function createServiceToken<T>(name: string): ServiceToken<T> {
|
||||
// __phantom 仅用于类型推断,运行时不需要实际值
|
||||
// __phantom is only for type inference, no actual value needed at runtime
|
||||
return {
|
||||
id: Symbol(name),
|
||||
name
|
||||
} as ServiceToken<T>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 插件服务注册表 | Plugin Service Registry
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 插件服务注册表
|
||||
* Plugin service registry
|
||||
*
|
||||
* 用于跨插件共享服务的类型安全注册表。
|
||||
* Type-safe registry for sharing services between plugins.
|
||||
*/
|
||||
export class PluginServiceRegistry {
|
||||
private _services = new Map<symbol, unknown>();
|
||||
|
||||
/**
|
||||
* 注册服务
|
||||
* Register a service
|
||||
*/
|
||||
register<T>(token: ServiceToken<T>, service: T): void {
|
||||
this._services.set(token.id, service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务(可选)
|
||||
* Get a service (optional)
|
||||
*/
|
||||
get<T>(token: ServiceToken<T>): T | undefined {
|
||||
return this._services.get(token.id) as T | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务(必需)
|
||||
* Get a service (required)
|
||||
*
|
||||
* @throws 如果服务未注册 | If service is not registered
|
||||
*/
|
||||
require<T>(token: ServiceToken<T>): T {
|
||||
const service = this._services.get(token.id);
|
||||
if (service === undefined) {
|
||||
throw new Error(`Service not found: ${token.name}`);
|
||||
}
|
||||
return service as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查服务是否已注册
|
||||
* Check if a service is registered
|
||||
*/
|
||||
has<T>(token: ServiceToken<T>): boolean {
|
||||
return this._services.has(token.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销服务
|
||||
* Unregister a service
|
||||
*/
|
||||
unregister<T>(token: ServiceToken<T>): boolean {
|
||||
return this._services.delete(token.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有服务
|
||||
* Clear all services
|
||||
*/
|
||||
clear(): void {
|
||||
this._services.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
* Dispose resources
|
||||
*
|
||||
* 实现 IService 接口,在服务容器清理时调用。
|
||||
* Implements IService interface, called when service container is cleaned up.
|
||||
*/
|
||||
dispose(): void {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ export { Core } from './Core';
|
||||
export { ServiceContainer, ServiceLifetime } from './Core/ServiceContainer';
|
||||
export type { IService, ServiceType, ServiceIdentifier } from './Core/ServiceContainer';
|
||||
|
||||
// 插件服务注册表(基于 ServiceToken 的类型安全服务管理)
|
||||
// Plugin Service Registry (type-safe service management based on ServiceToken)
|
||||
export { PluginServiceRegistry, createServiceToken } from './Core/PluginServiceRegistry';
|
||||
export type { ServiceToken } from './Core/PluginServiceRegistry';
|
||||
|
||||
// 插件系统
|
||||
export { PluginManager } from './Core/PluginManager';
|
||||
export { PluginState } from './Core/Plugin';
|
||||
|
||||
Reference in New Issue
Block a user