From 7a18d9459bd12d84bfadbda56b0fb9900a642b8c Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:13:40 +0800 Subject: [PATCH] =?UTF-8?q?docs(core):=20=E6=9B=B4=E6=96=B0=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E6=9E=B6=E6=9E=84=E6=96=87=E6=A1=A3=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E4=BA=8B=E4=BB=B6=E7=B3=BB=E7=BB=9F=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构架构文档增加模块管理和生命周期管理说明 - 更新命令系统文档移除输入参数结构简化命令定义 - 完善事件系统文档添加事件总线和注销功能说明 - 补充IoC容器文档增加多实例注册和单例管理说明 - 优化示例代码展示架构初始化和组件获取方式 - 添加配置选项说明和最佳实践建议 --- docs/zh-CN/core/architecture.md | 326 ++++++++++++++++++++++---------- docs/zh-CN/core/command.md | 261 +++++++++++++++---------- docs/zh-CN/core/events.md | 131 ++++++++++--- docs/zh-CN/core/ioc.md | 190 +++++++++++++++++-- docs/zh-CN/core/property.md | 98 +++++++++- docs/zh-CN/core/query.md | 219 +++++++++++++-------- docs/zh-CN/core/system.md | 96 ++++++++-- 7 files changed, 975 insertions(+), 346 deletions(-) diff --git a/docs/zh-CN/core/architecture.md b/docs/zh-CN/core/architecture.md index 3155f27..76592bf 100644 --- a/docs/zh-CN/core/architecture.md +++ b/docs/zh-CN/core/architecture.md @@ -2,24 +2,26 @@ ## 概述 -Architecture 包是整个框架的核心,提供了基于 MVC 架构模式的应用程序架构基础。它实现了依赖注入(IoC) -容器、组件生命周期管理,以及命令、查询、事件的统一调度机制。 +Architecture 包是整个框架的核心,提供了基于分层架构模式的应用程序架构基础。它实现了依赖注入(IoC) +容器、组件生命周期管理,以及命令、查询、事件的统一调度机制。 -**注意**:本框架的 Core 模块与 Godot 解耦,Godot 相关集成在 GFramework.Godot 包中实现。 +**注意**:本框架的 Core 模块与 Godot 解耦,Godot 相关集成在 GFramework.Godot 包中实现。 ## 核心接口 ### IArchitecture -架构接口,定义了框架的核心功能契约。 +架构接口,定义了框架的核心功能契约。 -**主要职责:** +**主要职责:** -- 组件注册:注册 System、Model、Utility -- 组件获取:从容器中获取已注册的组件 -- 命令处理:发送并执行命令 -- 查询处理:发送并执行查询 -- 事件管理:发送、注册、注销事件 +- **组件注册**:注册 System、Model、Utility +- **组件获取**:从容器中获取已注册的组件 +- **命令处理**:发送并执行命令 +- **查询处理**:发送并执行查询 +- **事件管理**:发送、注册、注销事件 +- **模块管理**:安装和管理架构模块 +- **生命周期管理**:管理架构的初始化、运行和销毁阶段 **核心方法:** ```csharp @@ -49,27 +51,27 @@ void UnRegisterEvent(Action onEvent); ### IArchitecturePhaseAware -架构阶段感知接口,允许组件监听架构阶段变化。 +架构阶段感知接口,允许组件监听架构阶段变化。 -**核心方法:** +**核心方法:** ```csharp void OnArchitecturePhase(ArchitecturePhase phase); ``` ### IArchitectureModule -架构模块接口,支持模块化架构扩展。 +架构模块接口,支持模块化架构扩展。 -**核心方法:** +**核心方法:** ```csharp void Install(IArchitecture architecture); ``` ### IAsyncInitializable -异步初始化接口,支持组件异步初始化。 +异步初始化接口,支持组件异步初始化。 -**核心方法:** +**核心方法:** ```csharp Task InitializeAsync(); ``` @@ -78,9 +80,9 @@ Task InitializeAsync(); ### Architecture 架构基类 -架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。 +架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。 -**构造函数参数:** +**构造函数参数:** ```csharp public abstract class Architecture( IArchitectureConfiguration? configuration = null, @@ -90,17 +92,19 @@ public abstract class Architecture( ) ``` -**特性:** +**特性:** -- **阶段式生命周期管理**:支持多个架构阶段( - BeforeUtilityInit、AfterUtilityInit、BeforeModelInit、AfterModelInit、BeforeSystemInit、AfterSystemInit、Ready、Destroying、Destroyed) -- **模块安装系统**:支持通过 `InstallModule` 扩展架构功能 -- **异步初始化**:支持同步和异步两种初始化方式 -- **IoC 容器集成**:内置依赖注入容器 -- **事件系统集成**:集成类型化事件系统 -- **与平台无关**:Core 模块不依赖 Godot,可以在任何 .NET 环境中使用 +- **阶段式生命周期管理** + :支持多个架构阶段(BeforeUtilityInit、AfterUtilityInit、BeforeModelInit、AfterModelInit、BeforeSystemInit、AfterSystemInit、Ready、FailedInitialization、Destroying、Destroyed) +- **模块安装系统**:支持通过 `InstallModule` 扩展架构功能 +- **异步初始化**:支持同步和异步两种初始化方式 +- **IoC 容器集成**:内置依赖注入容器 +- **事件系统集成**:集成类型化事件系统 +- **与平台无关**:Core 模块不依赖 Godot,可以在任何 .NET 环境中使用 +- **严格的阶段验证**:可配置的阶段转换验证机制 +- **组件生命周期管理**:自动管理组件的初始化和销毁 -**架构阶段:** +**架构阶段:** ```csharp public enum ArchitecturePhase { @@ -118,28 +122,29 @@ public enum ArchitecturePhase } ``` -**初始化流程:** +**初始化流程:** -1. 创建架构实例(传入配置或使用默认配置) -2. 调用用户自定义的 `Init()` 方法 -3. 初始化上下文工具(Context Utility) -4. 初始化所有注册的 Model -5. 初始化所有注册的 System -6. 冻结 IOC 容器 -7. 进入 Ready 阶段 +1. 创建架构实例(传入配置或使用默认配置) +2. 初始化基础上下文和日志系统 +3. 调用用户自定义的 `Init()` 方法 +4. 按顺序初始化组件: + - 工具初始化(BeforeUtilityInit → AfterUtilityInit) + - 模型初始化(BeforeModelInit → AfterModelInit) + - 系统初始化(BeforeSystemInit → AfterSystemInit) +5. 冻结 IOC 容器 +6. 进入 Ready 阶段 -**销毁流程:** +**销毁流程:** 1. 进入 Destroying 阶段 -2. 发送 `ArchitectureDestroyingEvent` 事件 -3. 销毁所有 System -4. 进入 Destroyed 阶段 -5. 发送 `ArchitectureDestroyedEvent` 事件 +2. 按注册逆序销毁所有实现了 `IDisposable` 的组件 +3. 进入 Destroyed 阶段 +4. 清理所有资源 **使用示例:** ```csharp -// 1. 定义你的架构(继承 Architecture 基类) +// 1. 定义你的架构(继承 Architecture 基类) public class GameArchitecture : Architecture { protected override void Init() @@ -166,31 +171,28 @@ architecture.Initialize(); // var architecture = new GameArchitecture(); // await architecture.InitializeAsync(); -// 3. 通过依赖注入使用架构 -// 在 Controller 或其他组件中注入架构实例 +// 3. 等待架构就绪 +await architecture.WaitUntilReadyAsync(); + +// 4. 通过依赖注入使用架构 +// 在 Controller 或其他组件中获取架构实例 public class GameController : IController { - private readonly IArchitecture _architecture; - - // 通过构造函数注入架构 - public GameController(IArchitecture architecture) - { - _architecture = architecture; - } + public IArchitecture GetArchitecture() => GameArchitecture.Interface; public void Start() { // 获取 Model - var playerModel = _architecture.GetModel(); + var playerModel = this.GetModel(); // 发送命令 - _architecture.SendCommand(new StartGameCommand()); + this.SendCommand(new StartGameCommand()); // 发送查询 - var score = _architecture.SendQuery(new GetScoreQuery()); + var score = this.SendQuery(new GetScoreQuery()); // 注册事件 - _architecture.RegisterEvent(OnPlayerDied); + this.RegisterEvent(OnPlayerDied); } private void OnPlayerDied(PlayerDiedEvent e) @@ -206,52 +208,73 @@ public class GameController : IController **Initialize()** -同步初始化方法,阻塞当前线程直到初始化完成。 +同步初始化方法,阻塞当前线程直到初始化完成。 ```csharp public void Initialize() ``` -使用示例: +**特点:** + +- 阻塞式初始化 +- 适用于简单场景或控制台应用 +- 初始化失败时抛出异常并进入 `FailedInitialization` 阶段 + +使用示例: ```csharp var architecture = new GameArchitecture(); architecture.Initialize(); // 阻塞等待初始化完成 ``` -异常处理: 如果初始化过程中发生异常,架构会进入 `FailedInitialization` 阶段并发送 `ArchitectureFailedInitializationEvent` -事件。 +异常处理:如果初始化过程中发生异常,架构会进入 `FailedInitialization` 阶段。 **InitializeAsync()** -异步初始化方法,返回 Task 以便调用者可以等待初始化完成。 +异步初始化方法,返回 Task 以便调用者可以等待初始化完成。 ```csharp public async Task InitializeAsync() ``` -使用示例: +**特点:** + +- 非阻塞式初始化 +- 支持异步组件初始化 +- 适用于需要异步加载资源的场景 +- 初始化失败时抛出异常并进入 `FailedInitialization` 阶段 + +使用示例: ```csharp var architecture = new GameArchitecture(); await architecture.InitializeAsync(); // 异步等待初始化完成 ``` -优势: +优势: - 支持异步初始化 Model 和 System -- 可以利用异步 I/O 操作(如异步加载数据) +- 可以利用异步 I/O 操作(如异步加载数据) - 提高初始化性能 +- 不会阻塞主线程 #### 模块管理 **InstallModule(IArchitectureModule module)** -安装架构模块,用于扩展架构功能。 +安装架构模块,用于扩展架构功能。 ```csharp -public void InstallModule(IArchitectureModule module) +public IArchitectureModule InstallModule(IArchitectureModule module) ``` -参数: +**返回值:** 返回安装的模块实例 -- `module`:要安装的模块实例 +**特点:** -使用示例: +- 模块安装时会自动注册生命周期钩子 +- 模块可以注册额外的组件到架构中 +- 只能在架构进入 Ready 阶段之前安装模块 + +参数: + +- `module`:要安装的模块实例 + +使用示例: ```csharp // 定义模块 @@ -267,23 +290,31 @@ public class NetworkModule : IArchitectureModule // 安装模块 var architecture = new GameArchitecture(); -architecture.InstallModule(new NetworkModule()); +var installedModule = architecture.InstallModule(new NetworkModule()); ``` #### 生命周期钩子 **RegisterLifecycleHook(IArchitectureLifecycle hook)** -注册生命周期钩子,用于在架构阶段变化时执行自定义逻辑。 +注册生命周期钩子,用于在架构阶段变化时执行自定义逻辑。 ```csharp -public void RegisterLifecycleHook(IArchitectureLifecycle hook) +public IArchitectureLifecycle RegisterLifecycleHook(IArchitectureLifecycle hook) ``` -参数: +**返回值:** 返回注册的生命周期钩子实例 -- `hook`:生命周期钩子实例 +**特点:** -使用示例: +- 生命周期钩子可以监听所有架构阶段变化 +- 只能在架构进入 Ready 阶段之前注册 +- 架构会按注册顺序通知所有钩子 + +参数: + +- `hook`:生命周期钩子实例 + +使用示例: ```csharp // 定义生命周期钩子 @@ -308,11 +339,11 @@ public class PerformanceMonitorHook : IArchitectureLifecycle // 注册生命周期钩子 var architecture = new GameArchitecture(); -architecture.RegisterLifecycleHook(new PerformanceMonitorHook()); +var registeredHook = architecture.RegisterLifecycleHook(new PerformanceMonitorHook()); architecture.Initialize(); ``` -**注意:** 生命周期钩子只能在架构进入 Ready 阶段之前注册。 +**注意:** 生命周期钩子只能在架构进入 Ready 阶段之前注册。 #### 属性 @@ -320,10 +351,16 @@ architecture.Initialize(); 获取当前架构的阶段。 ```csharp -public ArchitecturePhase CurrentPhase { get; } +public ArchitecturePhase CurrentPhase { get; private set; } ``` -使用示例: +**特点:** + +- 只读属性,外部无法直接修改 +- 实时反映架构的当前状态 +- 可用于条件判断和状态检查 + +使用示例: ```csharp var architecture = new GameArchitecture(); @@ -347,12 +384,18 @@ await Task.Run(async () => **Context** -获取架构上下文,提供对架构服务的访问。 +获取架构上下文,提供对架构服务的访问。 ```csharp public IArchitectureContext Context { get; } ``` -使用示例: +**特点:** + +- 提供对架构核心服务的访问 +- 包含事件总线、命令总线、查询总线等 +- 是架构内部服务的统一入口 + +使用示例: ```csharp // 通过 Context 访问服务 @@ -367,7 +410,14 @@ var environment = context.Environment; ```csharp // 1. 使用自定义配置 -var config = new ArchitectureConfiguration(); +var config = new ArchitectureConfiguration +{ + ArchitectureProperties = new ArchitectureProperties + { + StrictPhaseValidation = true, // 启用严格阶段验证 + AllowLateRegistration = false // 禁止就绪后注册组件 + } +}; var architecture = new GameArchitecture(configuration: config); // 2. 模块安装 @@ -382,10 +432,10 @@ public class GamePhaseListener : IArchitecturePhaseAware switch (phase) { case ArchitecturePhase.Ready: - GD.Print("架构已就绪,可以开始游戏了"); + Console.WriteLine("架构已就绪,可以开始游戏了"); break; case ArchitecturePhase.Destroying: - GD.Print("架构正在销毁"); + Console.WriteLine("架构正在销毁"); break; } } @@ -396,24 +446,57 @@ public class LifecycleHook : IArchitectureLifecycle { public void OnPhase(ArchitecturePhase phase, IArchitecture architecture) { - GD.Print($"架构阶段变化: {phase}"); + Console.WriteLine($"架构阶段变化: {phase}"); } } + +// 5. 等待架构就绪 +public async Task WaitForArchitectureReady() +{ + var architecture = new GameArchitecture(); + var initTask = architecture.InitializeAsync(); + + // 可以在其他地方等待架构就绪 + await architecture.WaitUntilReadyAsync(); + Console.WriteLine("架构已就绪"); +} ``` ### ArchitectureConfiguration 架构配置类 -架构配置类,用于配置架构的行为。 +架构配置类,用于配置架构的行为。 -**使用示例:** +**主要配置项:** + +```csharp +public class ArchitectureConfiguration : IArchitectureConfiguration +{ + public IArchitectureProperties ArchitectureProperties { get; set; } = new ArchitectureProperties(); + public IEnvironmentProperties EnvironmentProperties { get; set; } = new EnvironmentProperties(); + public ILoggerProperties LoggerProperties { get; set; } = new LoggerProperties(); +} + +public class ArchitectureProperties +{ + public bool StrictPhaseValidation { get; set; } = false; // 严格阶段验证 + public bool AllowLateRegistration { get; set; } = true; // 允许延迟注册 +} +``` + +**使用示例:** ```csharp var config = new ArchitectureConfiguration { - // 严格阶段验证 - StrictPhaseValidation = true, - // 允许延迟注册 - AllowLateRegistration = false + ArchitectureProperties = new ArchitectureProperties + { + StrictPhaseValidation = true, // 启用严格阶段验证 + AllowLateRegistration = false // 禁止就绪后注册组件 + }, + LoggerProperties = new LoggerProperties + { + LoggerFactoryProvider = new ConsoleLoggerFactoryProvider() // 自定义日志工厂 + } }; var architecture = new GameArchitecture(configuration: config); @@ -421,15 +504,34 @@ var architecture = new GameArchitecture(configuration: config); ### ArchitectureServices 架构服务类 -架构服务类,管理命令总线、查询总线、IOC容器和类型事件系统。 +架构服务类,管理命令总线、查询总线、IOC容器和类型事件系统。 + +**核心服务:** + +- `IIocContainer Container`:依赖注入容器 +- `IEventBus EventBus`:事件总线 +- `ICommandBus CommandBus`:命令总线 +- `IQueryBus QueryBus`:查询总线 ### ArchitectureContext 架构上下文类 -架构上下文类,提供对架构服务的访问。 +架构上下文类,提供对架构服务的访问。 + +**功能:** + +- 统一访问架构核心服务 +- 管理服务实例的生命周期 +- 提供服务解析功能 ### GameContext 游戏上下文类 -游戏上下文类,管理架构上下文与类型的绑定关系。 +游戏上下文类,管理架构上下文与类型的绑定关系。 + +**功能:** + +- 维护架构类型与上下文实例的映射 +- 提供全局上下文访问 +- 支持多架构实例管理 ## 设计模式 @@ -459,20 +561,31 @@ var architecture = new GameArchitecture(configuration: config); ### 7. 组合优于继承 -通过接口组合获得不同能力,而不是深层继承链。 +通过接口组合获得不同能力,而不是深层继承链。 + +### 8. 模块化设计 + +通过 `IArchitectureModule` 实现架构的可扩展性。 + +### 9. 工厂模式 + +通过配置对象和工厂方法创建架构实例。 ## 最佳实践 -1. **保持架构类简洁**:只在 `Init()` 中注册组件,不要包含业务逻辑 -2. **合理划分职责**: - - Model:数据和状态 - - System:业务逻辑和规则 - - Utility:无状态的工具方法 -3. **使用依赖注入**:通过构造函数注入架构实例,便于测试 -4. **事件命名规范**:使用过去式命名事件类,如 `PlayerDiedEvent` -5. **避免循环依赖**:System 不应直接引用 System,应通过事件通信 -6. **使用模块扩展**:通过 `IArchitectureModule` 实现架构的可扩展性 -7. **Core 模块与平台解耦**:GFramework.Core 不包含 Godot 相关代码,Godot 集成在单独模块中 +1. **保持架构类简洁**:只在 `Init()` 中注册组件,不要包含业务逻辑 +2. **合理划分职责**: + - Model:数据和状态 + - System:业务逻辑和规则 + - Utility:无状态的工具方法 +3. **使用依赖注入**:通过构造函数注入架构实例,便于测试 +4. **事件命名规范**:使用过去式命名事件类,如 `PlayerDiedEvent` +5. **避免循环依赖**:System 不应直接引用 System,应通过事件通信 +6. **使用模块扩展**:通过 `IArchitectureModule` 实现架构的可扩展性 +7. **Core 模块与平台解耦**:GFramework.Core 不包含 Godot 相关代码,Godot 集成在单独模块中 +8. **合理配置阶段验证**:根据项目需求配置 `StrictPhaseValidation` 和 `AllowLateRegistration` +9. **及时处理初始化异常**:捕获并处理架构初始化过程中的异常 +10. **使用异步初始化**:对于需要加载大量资源的场景,优先使用 `InitializeAsync()` ## 相关包 @@ -483,8 +596,11 @@ var architecture = new GameArchitecture(configuration: config); - **model** - 数据模型 - **system** - 业务系统 - **utility** - 工具类 -- **GFramework.Godot** - Godot 特定集成(GodotNode 扩展、GodotLogger 等) +- **GFramework.Godot** - Godot 特定集成(GodotNode 扩展、GodotLogger 等) +- **extensions** - 扩展方法 +- **logging** - 日志系统 +- **environment** - 环境管理 --- -**许可证**: Apache 2.0 \ No newline at end of file +**许可证**:Apache 2.0 \ No newline at end of file diff --git a/docs/zh-CN/core/command.md b/docs/zh-CN/core/command.md index da17a34..f4c2f80 100644 --- a/docs/zh-CN/core/command.md +++ b/docs/zh-CN/core/command.md @@ -4,9 +4,11 @@ Command 包实现了命令模式(Command Pattern),用于封装用户操作和业务逻辑。通过命令模式,可以将请求封装为对象,实现操作的参数化、队列化、日志记录、撤销等功能。 +命令系统是 GFramework CQRS 架构的重要组成部分,与事件系统和查询系统协同工作,实现完整的业务逻辑处理流程。 + ## 核心接口 -### 1. [`ICommand`](./command.md) +### ICommand 无返回值命令接口,定义了命令的基本契约。 @@ -16,7 +18,7 @@ Command 包实现了命令模式(Command Pattern),用于封装用户操作 void Execute(); // 执行命令 ``` -### 2. [`ICommand`](./command.md) +### ICommand`` 带返回值的命令接口,用于需要返回执行结果的命令。 @@ -28,37 +30,41 @@ TResult Execute(); // 执行命令并返回结果 ## 核心类 -### 1. [`AbstractCommand`](./command.md) +### AbstractCommand -无返回值命令的抽象基类,提供了命令的基础实现。它继承自 [ContextAwareBase](./rule.md) -,具有上下文感知能力。 +无返回值命令的抽象基类,提供了命令的基础实现。它继承自 ContextAwareBase,具有上下文感知能力。 + +**核心方法:** + +```csharp +void ICommand.Execute(); // 实现 ICommand 接口 +protected abstract void OnExecute(); // 抽象执行方法,由子类实现 +``` **使用示例:** ```csharp -// 定义一个命令输入参数 -public struct StartGameCommandInput : ICommandInput -{ - public int LevelId { get; set; } - public string PlayerName { get; set; } -} - // 定义一个开始游戏的命令 -public class StartGameCommand : AbstractCommand +public class StartGameCommand : AbstractCommand { - public StartGameCommand(StartGameCommandInput input) : base(input) + private readonly int _levelId; + private readonly string _playerName; + + public StartGameCommand(int levelId, string playerName) { + _levelId = levelId; + _playerName = playerName; } - protected override void OnExecute(StartGameCommandInput input) + protected override void OnExecute() { // 获取需要的模型 var playerModel = this.GetModel(); var gameModel = this.GetModel(); // 执行业务逻辑 - playerModel.PlayerName.Value = input.PlayerName; - gameModel.CurrentLevel.Value = input.LevelId; + playerModel.PlayerName.Value = _playerName; + gameModel.CurrentLevel.Value = _levelId; gameModel.GameState.Value = GameState.Playing; // 发送事件通知其他模块 @@ -74,43 +80,44 @@ public class GameController : IController public void OnStartButtonClicked() { // 发送命令实例 - this.SendCommand(new StartGameCommand(new StartGameCommandInput - { - LevelId = 1, - PlayerName = "Player1" - })); + this.SendCommand(new StartGameCommand(1, "Player1")); } } ``` -### 2. [`AbstractCommand`](./command.md) +### AbstractCommandWithResult`` -带返回值命令的抽象基类,同样继承自 [ContextAwareBase](./rule.md)。 +带返回值命令的抽象基类,同样继承自 ContextAwareBase。 + +**核心方法:** + +```csharp +TResult ICommand.Execute(); // 实现 ICommand 接口 +protected abstract TResult OnExecute(); // 抽象执行方法,由子类实现 +``` **使用示例:** ```csharp -// 定义一个计算伤害的命令输入 -public struct CalculateDamageCommandInput : ICommandInput -{ - public int AttackerAttackPower { get; set; } - public int DefenderDefense { get; set; } -} - // 定义一个计算伤害的命令 -public class CalculateDamageCommand : AbstractCommand +public class CalculateDamageCommand : AbstractCommandWithResult { - public CalculateDamageCommand(CalculateDamageCommandInput input) : base(input) + private readonly int _attackerAttackPower; + private readonly int _defenderDefense; + + public CalculateDamageCommand(int attackerAttackPower, int defenderDefense) { + _attackerAttackPower = attackerAttackPower; + _defenderDefense = defenderDefense; } - protected override int OnExecute(CalculateDamageCommandInput input) + protected override int OnExecute() { // 获取游戏配置 var config = this.GetModel(); // 计算最终伤害 - var baseDamage = input.AttackerAttackPower - input.DefenderDefense; + var baseDamage = _attackerAttackPower - _defenderDefense; var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier); return (int)finalDamage; @@ -125,11 +132,10 @@ public class CombatSystem : AbstractSystem public void Attack(Character attacker, Character defender) { // 发送命令并获取返回值 - var damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput - { - AttackerAttackPower = attacker.AttackPower, - DefenderDefense = defender.Defense - })); + var damage = this.SendCommand(new CalculateDamageCommand( + attacker.AttackPower, + defender.Defense + )); // 应用伤害 defender.Health -= damage; @@ -147,6 +153,12 @@ public class CombatSystem : AbstractSystem 3. **返回结果**:对于带返回值的命令,返回执行结果 4. **命令销毁**:命令执行完毕后可以被垃圾回收 +**注意事项:** + +- 命令应该是无状态的,执行完即可丢弃 +- 避免在命令中保存长期引用 +- 命令执行应该是原子操作 + ## CommandBus - 命令总线 ### 功能说明 @@ -156,64 +168,122 @@ public class CombatSystem : AbstractSystem **主要方法:** ```csharp -void Send(ICommand command); // 发送无返回值命令 +void Send(ICommand command); // 发送无返回值命令 TResult Send(ICommand command); // 发送带返回值命令 ``` +**特点:** + +- 统一的命令执行入口 +- 支持同步命令执行 +- 与架构上下文集成 + ### 使用示例 ```csharp -var commandBus = new CommandBus(); +// 通过架构获取命令总线 +var commandBus = architecture.Context.CommandBus; // 发送无返回值命令 -commandBus.Send(new StartGameCommand(new StartGameCommandInput())); +commandBus.Send(new StartGameCommand(1, "Player1")); // 发送带返回值命令 -var result = commandBus.Send(new CalculateDamageCommand(new CalculateDamageCommandInput())); +var damage = commandBus.Send(new CalculateDamageCommand(100, 50)); ``` -## EmptyCommandInput - 空命令输入 +## 命令基类变体 -当命令不需要输入参数时,可以使用 `EmptyCommandInput` 类: +框架提供了多种命令基类以满足不同需求: + +### AbstractCommand + +最基础的无返回值命令类 + +### AbstractCommandWithResult`` + +带返回值的命令基类 + +### AbstractCommandWithInput`` + +带输入参数的无返回值命令类 + +### AbstractCommandWithInputAndResult`` + +既带输入参数又带返回值的命令类 + +### AbstractAsyncCommand + +支持异步执行的命令基类 + +### AbstractAsyncCommandWithResult`` + +支持异步执行的带返回值命令基类 ```csharp -public class SimpleActionCommand : AbstractCommand +// 异步命令示例 +public class LoadSaveDataCommand : AbstractAsyncCommandWithResult { - public SimpleActionCommand(EmptyCommandInput input) : base(input) + private readonly string _saveSlot; + + public LoadSaveDataCommand(string saveSlot) { + _saveSlot = saveSlot; } - protected override void OnExecute(EmptyCommandInput input) + protected override async Task OnExecuteAsync() { - // 执行简单操作,无需额外参数 - this.SendEvent(new SimpleActionEvent()); + var storage = this.GetUtility(); + return await storage.LoadSaveDataAsync(_saveSlot); } } ``` +## 命令处理器执行 + +所有发送给命令总线的命令最终都会通过 `CommandExecutor` 来执行: + +```csharp +public class CommandExecutor +{ + public static void Execute(ICommand command) + { + command.Execute(); + } + + public static TResult Execute(ICommand command) + { + return command.Execute(); + } +} +``` + +**特点:** + +- 提供统一的命令执行机制 +- 支持同步和异步命令执行 +- 可以扩展添加中间件逻辑 + ## 使用场景 ### 1. 用户交互操作 ```csharp -public struct SaveGameCommandInput : ICommandInput +public class SaveGameCommand : AbstractCommand { - public string SaveSlot { get; set; } -} + private readonly string _saveSlot; -public class SaveGameCommand : AbstractCommand -{ - public SaveGameCommand(SaveGameCommandInput input) : base(input) + public SaveGameCommand(string saveSlot) { + _saveSlot = saveSlot; } - protected override void OnExecute(SaveGameCommandInput input) + protected override void OnExecute() { var saveSystem = this.GetSystem(); var playerModel = this.GetModel(); - saveSystem.SavePlayerData(playerModel, input.SaveSlot); - this.SendEvent(new GameSavedEvent(input.SaveSlot)); + saveSystem.SavePlayerData(playerModel, _saveSlot); + this.SendEvent(new GameSavedEvent(_saveSlot)); } } ``` @@ -221,18 +291,16 @@ public class SaveGameCommand : AbstractCommand ### 2. 业务流程控制 ```csharp -public struct LoadLevelCommandInput : ICommandInput +public class LoadLevelCommand : AbstractCommand { - public int LevelId { get; set; } -} + private readonly int _levelId; -public class LoadLevelCommand : AbstractCommand -{ - public LoadLevelCommand(LoadLevelCommandInput input) : base(input) + public LoadLevelCommand(int levelId) { + _levelId = levelId; } - protected override void OnExecute(LoadLevelCommandInput input) + protected override void OnExecute() { var levelSystem = this.GetSystem(); var uiSystem = this.GetSystem(); @@ -241,10 +309,10 @@ public class LoadLevelCommand : AbstractCommand uiSystem.ShowLoadingScreen(); // 加载关卡 - levelSystem.LoadLevel(input.LevelId); + levelSystem.LoadLevel(_levelId); // 发送事件 - this.SendEvent(new LevelLoadedEvent(input.LevelId)); + this.SendEvent(new LevelLoadedEvent(_levelId)); } } ``` @@ -255,45 +323,32 @@ public class LoadLevelCommand : AbstractCommand 2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃 3. **参数通过构造函数传递**:命令需要的参数应在创建时传入 4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信 -5. **合理使用返回值**:只在确实需要返回结果时使用 `AbstractCommand` +5. **合理使用返回值**:只在确实需要返回结果时使用带返回值的命令 6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand`、`SavePlayerCommand` -7. **输入参数结构化**:使用 `ICommandInput` 接口的实现类来组织命令参数 +7. **单一职责原则**:每个命令只负责一个特定的业务操作 +8. **使用异步命令**:对于需要长时间执行的操作,使用异步命令避免阻塞 +9. **命令验证**:在命令执行前验证输入参数的有效性 +10. **错误处理**:在命令中适当处理异常情况 -## 扩展功能 +## 命令模式优势 -### 命令撤销/重做(可扩展) +### 1. 可扩展性 -```csharp -public struct MoveCommandInput : ICommandInput -{ - public Vector3 NewPosition { get; set; } -} +- 命令可以被序列化和存储 +- 支持命令队列和批处理 +- 便于实现撤销/重做功能 -// 实现可撤销命令 -public class MoveCommand : AbstractCommand, IUndoableCommand -{ - private Vector3 _oldPosition; - private Vector3 _newPosition; - - public MoveCommand(MoveCommandInput input) : base(input) - { - _newPosition = input.NewPosition; - } +### 2. 可测试性 - protected override void OnExecute(MoveCommandInput input) - { - var player = this.GetModel(); - _oldPosition = player.Position; - player.Position = input.NewPosition; - } - - public void Undo() - { - var player = this.GetModel(); - player.Position = _oldPosition; - } -} -``` +- 命令逻辑独立,易于单元测试 +- 可以模拟命令执行结果 +- 支持行为驱动开发 + +### 3. 可维护性 + +- 业务逻辑集中管理 +- 降低组件间耦合度 +- 便于重构和扩展 ## 相关包 @@ -306,4 +361,4 @@ public class MoveCommand : AbstractCommand, IUndoableCommand --- -**许可证**: Apache 2.0 \ No newline at end of file +**许可证**:Apache 2.0 \ No newline at end of file diff --git a/docs/zh-CN/core/events.md b/docs/zh-CN/core/events.md index 0fa08d5..6a7e8e4 100644 --- a/docs/zh-CN/core/events.md +++ b/docs/zh-CN/core/events.md @@ -4,9 +4,11 @@ Events 包提供了一套完整的事件系统,实现了观察者模式(Observer Pattern)。通过事件系统,可以实现组件间的松耦合通信,支持无参和带参事件、事件注册/注销、以及灵活的事件组合。 +事件系统是 GFramework 架构中组件间通信的核心机制,与命令模式和查询模式共同构成了完整的 CQRS 架构。 + ## 核心接口 -### 1. IEvent +### IEvent 基础事件接口,定义了事件注册的基本功能。 @@ -16,7 +18,7 @@ Events 包提供了一套完整的事件系统,实现了观察者模式(Obse IUnRegister Register(Action onEvent); // 注册事件处理函数 ``` -### 2. IUnRegister +### IUnRegister 注销接口,用于取消事件注册。 @@ -26,7 +28,7 @@ IUnRegister Register(Action onEvent); // 注册事件处理函数 void UnRegister(); // 执行注销操作 ``` -### 3. IUnRegisterList +### IUnRegisterList 注销列表接口,用于批量管理注销对象。 @@ -36,7 +38,7 @@ void UnRegister(); // 执行注销操作 IList UnregisterList { get; } // 获取注销列表 ``` -### 4. IEventBus +### IEventBus 事件总线接口,提供基于类型的事件发送和注册。 @@ -46,14 +48,22 @@ IList UnregisterList { get; } // 获取注销列表 IUnRegister Register(Action onEvent); // 注册类型化事件 void Send(T e); // 发送事件实例 void Send() where T : new(); // 发送事件(自动创建实例) +void UnRegister(Action onEvent); // 注销事件监听器 ``` ## 核心类 -### 1. EasyEvent +### EasyEvent 无参事件类,支持注册、注销和触发无参事件。 +**核心方法:** + +```csharp +IUnRegister Register(Action onEvent); // 注册事件监听器 +void Trigger(); // 触发事件 +``` + **使用示例:** ```csharp @@ -63,7 +73,7 @@ var onClicked = new EasyEvent(); // 注册监听 var unregister = onClicked.Register(() => { - GD.Print("Button clicked!"); + Console.WriteLine("Button clicked!"); }); // 触发事件 @@ -73,10 +83,17 @@ onClicked.Trigger(); unregister.UnRegister(); ``` -### 2. Event`` +### Event`` 单参数泛型事件类,支持一个参数的事件。 +**核心方法:** + +```csharp +IUnRegister Register(Action onEvent); // 注册事件监听器 +void Trigger(T eventData); // 触发事件并传递参数 +``` + **使用示例:** ```csharp @@ -86,17 +103,24 @@ var onScoreChanged = new Event(); // 注册监听 onScoreChanged.Register(newScore => { - GD.Print($"Score changed to: {newScore}"); + Console.WriteLine($"Score changed to: {newScore}"); }); // 触发事件并传递参数 onScoreChanged.Trigger(100); ``` -### 3. Event +### Event 双参数泛型事件类。 +**核心方法:** + +```csharp +IUnRegister Register(Action onEvent); // 注册事件监听器 +void Trigger(T param1, TK param2); // 触发事件并传递两个参数 +``` + **使用示例:** ```csharp @@ -105,16 +129,24 @@ var onDamageDealt = new Event(); onDamageDealt.Register((attacker, damage) => { - GD.Print($"{attacker} dealt {damage} damage!"); + Console.WriteLine($"{attacker} dealt {damage} damage!"); }); onDamageDealt.Trigger("Player", 50); ``` -### 4. `EasyEvents` +### EasyEvents 全局事件管理器,提供类型安全的事件注册和获取。 +**核心方法:** + +```csharp +static void Register() where T : IEvent, new(); // 注册事件类型 +static T Get() where T : IEvent, new(); // 获取事件实例 +static T GetOrAddEvent() where T : IEvent, new(); // 获取或创建事件实例 +``` + **使用示例:** ```csharp @@ -127,16 +159,25 @@ var gameStartEvent = EasyEvents.Get(); // 注册监听 gameStartEvent.Register(() => { - GD.Print("Game started!"); + Console.WriteLine("Game started!"); }); // 触发事件 gameStartEvent.Trigger(); ``` -### 5. `EventBus` +### EventBus -类型化事件系统,支持基于类型的事件发送和注册。 +类型化事件系统,支持基于类型的事件发送和注册。这是架构中默认的事件总线实现。 + +**核心方法:** + +```csharp +IUnRegister Register(Action onEvent); // 注册类型化事件 +void Send(T e); // 发送事件实例 +void Send() where T : new(); // 发送事件(自动创建实例) +void UnRegister(Action onEvent); // 注销事件监听器 +``` **使用示例:** @@ -147,7 +188,7 @@ var eventBus = new EventBus(); // 注册类型化事件 eventBus.Register(e => { - GD.Print($"Player died at position: {e.Position}"); + Console.WriteLine($"Player died at position: {e.Position}"); }); // 发送事件(传递实例) @@ -158,26 +199,35 @@ eventBus.Send(new PlayerDiedEvent // 发送事件(自动创建实例) eventBus.Send(); + +// 注销事件监听器 +eventBus.UnRegister(OnPlayerDied); ``` -### 6. `DefaultUnRegister` +### DefaultUnRegister 默认注销器实现,封装注销回调。 **使用示例:** ```csharp -Action onUnregister = () => GD.Print("Unregistered"); +Action onUnregister = () => Console.WriteLine("Unregistered"); var unregister = new DefaultUnRegister(onUnregister); // 执行注销 unregister.UnRegister(); ``` -### 7. `OrEvent` +### OrEvent 事件或运算组合器,当任意一个事件触发时触发。 +**核心方法:** + +```csharp +OrEvent Or(IEvent @event); // 添加要组合的事件 +``` + **使用示例:** ```csharp @@ -189,14 +239,21 @@ var onAnyInput = new OrEvent() // 当上述任意事件触发时,执行回调 onAnyInput.Register(() => { - GD.Print("Input detected!"); + Console.WriteLine("Input detected!"); }); ``` -### 8. `UnRegisterList` +### UnRegisterList 批量管理注销对象的列表。 +**核心方法:** + +```csharp +void Add(IUnRegister unRegister); // 添加注销器到列表 +void UnRegisterAll(); // 批量注销所有事件 +``` + **使用示例:** ```csharp @@ -209,7 +266,7 @@ someEvent.Register(OnEvent).AddToUnregisterList(unregisterList); unregisterList.UnRegisterAll(); ``` -### 9. `ArchitectureEvents` +### ArchitectureEvents 定义了架构生命周期相关的事件。 @@ -317,18 +374,18 @@ public partial class GameController : Node, IController private void OnGameStarted(GameStartedEvent e) { - GD.Print("Game started!"); + Console.WriteLine("Game started!"); } private void OnPlayerDied(PlayerDiedEvent e) { - GD.Print($"Player died at {e.Position}: {e.Cause}"); + Console.WriteLine($"Player died at {e.Position}: {e.Cause}"); ShowGameOverScreen(); } private void OnLevelCompleted(LevelCompletedEvent e) { - GD.Print($"Level {e.LevelId} completed! Score: {e.Score}"); + Console.WriteLine($"Level {e.LevelId} completed! Score: {e.Score}"); ShowVictoryScreen(e); } @@ -503,6 +560,11 @@ public override void _Ready() - 事件处理器保持轻量 - 使用结构体事件减少 GC +7. **事件设计原则** + - 高内聚:事件应该代表一个完整的业务概念 + - 低耦合:事件发送者不需要知道接收者 + - 可测试:事件应该易于模拟和测试 + ## 事件 vs 其他通信方式 | 方式 | 适用场景 | 优点 | 缺点 | @@ -512,6 +574,23 @@ public override void _Ready() | **Query** | 查询数据 | 职责清晰、有返回值 | 同步调用 | | **BindableProperty** | UI 数据绑定 | 自动更新、响应式 | 仅限单一属性 | +## 事件系统架构 + +事件系统在 GFramework 中的架构位置: + +``` +Architecture (架构核心) +├── EventBus (事件总线) +├── CommandBus (命令总线) +├── QueryBus (查询总线) +└── IocContainer (IoC容器) + +Components (组件) +├── Model (发送事件) +├── System (发送/接收事件) +└── Controller (接收事件) +``` + ## 相关包 - [`architecture`](./architecture.md) - 提供全局事件系统 @@ -519,4 +598,6 @@ public override void _Ready() - [`property`](./property.md) - 可绑定属性基于事件实现 - [`controller`](./controller.md) - 控制器监听事件 - [`model`](./model.md) - 模型发送事件 -- [`system`](./system.md) - 系统发送和监听事件 \ No newline at end of file +- [`system`](./system.md) - 系统发送和监听事件 +- [`command`](./command.md) - 与事件配合实现 CQRS +- [`query`](./query.md) - 与事件配合实现 CQRS \ No newline at end of file diff --git a/docs/zh-CN/core/ioc.md b/docs/zh-CN/core/ioc.md index 974e28b..d3989d0 100644 --- a/docs/zh-CN/core/ioc.md +++ b/docs/zh-CN/core/ioc.md @@ -5,12 +5,20 @@ IoC(Inversion of Control,控制反转)包提供了一个轻量级的依赖注入容器,用于管理框架中各种组件的注册和获取。通过 IoC 容器,可以实现组件间的解耦,便于测试和维护。 +IoC 容器是 GFramework 架构的核心组件之一,为整个框架提供依赖管理和组件解析服务。 + ## 核心类 -### `IocContainer` +### IocContainer IoC 容器类,负责管理对象的注册和获取。 +**继承关系:** + +```csharp +public class IocContainer : ContextAwareBase, IIocContainer +``` + **主要功能:** - 注册实例到容器 @@ -18,13 +26,16 @@ IoC 容器类,负责管理对象的注册和获取。 - 类型安全的依赖管理 - 线程安全操作 - 容器冻结保护 +- 多实例注册支持 ## 核心方法 -### 1. Register`` 和 Register(Type, object) +### Register`` 和 Register(Type, object) 注册一个实例到容器中。 +**方法签名:** + ```csharp public void Register(T instance) public void Register(Type type, object instance) @@ -35,6 +46,13 @@ public void Register(Type type, object instance) - `instance`: 要注册的实例对象 - `type`: 要注册的类型(重载方法) +**特点:** + +- 支持泛型和非泛型注册 +- 自动注册到实例的所有接口类型 +- 线程安全操作 +- 容器冻结后抛出异常 + **使用示例:** ```csharp @@ -44,12 +62,17 @@ var container = new IocContainer(); container.Register(new PlayerModel()); container.Register(new GameSystem()); container.Register(new StorageUtility()); + +// 非泛型注册 +container.Register(typeof(ICustomService), new CustomService()); ``` -### 2. RegisterSingleton`` +### RegisterSingleton`` 注册单例实例到容器中。一个类型只允许一个实例。 +**方法签名:** + ```csharp public void RegisterSingleton(T instance) ``` @@ -58,6 +81,12 @@ public void RegisterSingleton(T instance) - `instance`: 要注册的单例实例 +**特点:** + +- 严格单例约束:同一类型只能注册一个实例 +- 重复注册会抛出 `InvalidOperationException` +- 适用于全局服务和配置对象 + **使用示例:** ```csharp @@ -65,12 +94,18 @@ var container = new IocContainer(); // 注册单例 container.RegisterSingleton(new PlayerModel()); +container.RegisterSingleton(new GameConfiguration()); + +// 以下会抛出异常(重复注册) +// container.RegisterSingleton(new AnotherPlayerModel()); ``` -### 3. RegisterPlurality +### RegisterPlurality 注册多个实例,将实例注册到其实现的所有接口和具体类型上。 +**方法签名:** + ```csharp public void RegisterPlurality(object instance) ``` @@ -79,10 +114,34 @@ public void RegisterPlurality(object instance) - `instance`: 要注册的实例 -### 4. RegisterSystem +**特点:** + +- 自动注册到所有实现的接口 +- 同时注册具体类型 +- 支持多态注册 +- 常用于 System 和 Utility 注册 + +**使用示例:** + +```csharp +var container = new IocContainer(); + +// 假设 GameSystem 实现了 IGameSystem 和 IUpdateable 接口 +var gameSystem = new GameSystem(); +container.RegisterPlurality(gameSystem); + +// 现在可以通过任一接口获取 +var system1 = container.Get(); // 返回 gameSystem +var system2 = container.Get(); // 返回 gameSystem +var system3 = container.Get(); // 返回 gameSystem +``` + +### RegisterSystem 注册系统实例,将其绑定到其所有实现的接口上。 +**方法签名:** + ```csharp public void RegisterSystem(ISystem system) ``` @@ -91,10 +150,29 @@ public void RegisterSystem(ISystem system) - `system`: 系统实例对象 -### 5. Get`` 和 GetAll`` +**特点:** + +- 专门为 System 设计的注册方法 +- 内部调用 `RegisterPlurality` +- 提供语义化的 API + +**使用示例:** + +```csharp +var container = new IocContainer(); + +// 注册系统 +container.RegisterSystem(new CombatSystem()); +container.RegisterSystem(new InventorySystem()); +container.RegisterSystem(new QuestSystem()); +``` + +### Get`` 和 GetAll`` 从容器中获取指定类型的实例。 +**方法签名:** + ```csharp public T? Get() where T : class public IReadOnlyList GetAll() where T : class @@ -105,6 +183,12 @@ public IReadOnlyList GetAll() where T : class - `Get`: 返回指定类型的实例,如果未找到则返回 `null` - `GetAll`: 返回指定类型的所有实例列表,如果未找到则返回空数组 +**特点:** + +- 线程安全读取 +- 支持接口和具体类型查询 +- 返回值快照(线程安全) + **使用示例:** ```csharp @@ -114,12 +198,15 @@ var gameSystems = container.GetAll(); // 如果类型未注册,Get 返回 null,GetAll 返回空数组 var unknownService = container.Get(); // null +var allUnknownServices = container.GetAll(); // 空数组 ``` -### 6. GetRequired`` +### GetRequired`` 获取指定类型的必需实例,如果没有注册或注册了多个实例会抛出异常。 +**方法签名:** + ```csharp public T GetRequired() where T : class ``` @@ -128,10 +215,30 @@ public T GetRequired() where T : class - 返回找到的唯一实例 -### 7. GetAllSorted`` +**特点:** + +- 严格模式获取 +- 无实例时抛出 `InvalidOperationException` +- 多实例时抛出 `InvalidOperationException` +- 适用于必需的单例依赖 + +**使用示例:** + +```csharp +// 获取必需的服务 +var config = container.GetRequired(); // 必须存在且唯一 + +// 以下情况会抛出异常: +// 1. IGameConfiguration 未注册 +// 2. IGameConfiguration 注册了多个实例 +``` + +### GetAllSorted`` 获取并排序(系统调度专用)。 +**方法签名:** + ```csharp public IReadOnlyList GetAllSorted(Comparison comparison) where T : class ``` @@ -144,24 +251,71 @@ public IReadOnlyList GetAllSorted(Comparison comparison) where T : clas - 按指定方式排序后的实例列表 +**特点:** + +- 支持自定义排序 +- 适用于需要按优先级执行的场景 +- 常用于 System 更新顺序控制 + +**使用示例:** + +```csharp +// 按优先级排序系统 +var sortedSystems = container.GetAllSorted((a, b) => +{ + var priorityA = GetSystemPriority(a); + var priorityB = GetSystemPriority(b); + return priorityA.CompareTo(priorityB); +}); +``` + +## IoC 容器架构 + +``` +Architecture (架构层) +├── IocContainer (IoC容器) +│ ├── Register() // 泛型注册 +│ ├── Register(Type, obj) // 非泛型注册 +│ ├── RegisterSingleton() // 单例注册 +│ ├── RegisterPlurality() // 多态注册 +│ ├── RegisterSystem() // 系统注册 +│ ├── Get() // 获取实例 +│ ├── GetAll() // 获取所有实例 +│ ├── GetRequired() // 获取必需实例 +│ ├── GetAllSorted() // 排序获取 +│ ├── Contains() // 检查存在性 +│ ├── ContainsInstance() // 检查实例 +│ ├── Clear() // 清空容器 +│ └── Freeze() // 冻结容器 +│ +├── Components (组件) +│ ├── Model (数据模型) +│ ├── System (业务系统) +│ └── Utility (工具类) +│ +└── Context (上下文) + └── 提供容器访问 +``` + ## 在框架中的使用 ### Architecture 中的应用 IoC 容器是 [`Architecture`](./architecture.md) 类的核心组件,用于管理所有的 System、Model 和 Utility。 -``` +```csharp public abstract class Architecture : IArchitecture { // 内置 IoC 容器 private readonly IocContainer _mContainer = new(); // 注册系统 - public void RegisterSystem(TSystem system) where TSystem : ISystem + public TSystem RegisterSystem(TSystem system) where TSystem : ISystem { - system.SetArchitecture(this); - _mContainer.Register(system); // 注册到容器 - // ... + system.SetContext(Context); + _mContainer.RegisterPlurality(system); // 注册到容器 + RegisterLifecycleComponent(system); // 处理生命周期 + return system; } // 获取系统 @@ -169,6 +323,16 @@ public abstract class Architecture : IArchitecture => _mContainer.Get(); // 从容器获取 // Model 和 Utility 同理 + public TModel RegisterModel(TModel model) where TModel : IModel + { + model.SetContext(Context); + _mContainer.RegisterPlurality(model); + RegisterLifecycleComponent(model); + return model; + } + + public TModel GetModel() where TModel : class, IModel + => _mContainer.Get(); } ``` diff --git a/docs/zh-CN/core/property.md b/docs/zh-CN/core/property.md index 4bbc90c..48e879f 100644 --- a/docs/zh-CN/core/property.md +++ b/docs/zh-CN/core/property.md @@ -4,9 +4,11 @@ Property 包提供了可绑定属性(BindableProperty)的实现,支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。 +BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。 + ## 核心接口 -### [`IReadonlyBindableProperty`](./property.md) +### IReadonlyBindableProperty`` 只读可绑定属性接口,提供属性值的读取和变更监听功能。 @@ -26,7 +28,7 @@ IUnRegister RegisterWithInitValue(Action action); void UnRegister(Action onValueChanged); ``` -### [`IBindableProperty`](./property.md) +### IBindableProperty`` 可绑定属性接口,继承自只读接口,增加了修改能力。 @@ -42,10 +44,33 @@ void SetValueWithoutEvent(T newValue); ## 核心类 -### [`BindableProperty`](./property.md) +### BindableProperty`` 可绑定属性的完整实现。 +**核心方法:** + +```csharp +// 构造函数 +BindableProperty(T defaultValue = default!); + +// 属性值 +T Value { get; set; } + +// 注册监听 +IUnRegister Register(Action onValueChanged); +IUnRegister RegisterWithInitValue(Action action); + +// 取消监听 +void UnRegister(Action onValueChanged); + +// 设置值但不触发事件 +void SetValueWithoutEvent(T newValue); + +// 设置自定义比较器 +BindableProperty WithComparer(Func comparer); +``` + **使用示例:** ```csharp @@ -55,7 +80,7 @@ var health = new BindableProperty(100); // 监听值变化(不会立即触发) var unregister = health.Register(newValue => { - GD.Print($"Health changed to: {newValue}"); + Console.WriteLine($"Health changed to: {newValue}"); }); // 设置值(会触发监听器) @@ -74,7 +99,7 @@ health.SetValueWithoutEvent(75); // 1. 注册并立即获得当前值 health.RegisterWithInitValue(value => { - GD.Print($"Current health: {value}"); // 立即输出当前值 + Console.WriteLine($"Current health: {value}"); // 立即输出当前值 // 后续值变化时也会调用 }); @@ -84,9 +109,13 @@ BindableProperty.Comparer = (a, b) => Math.Abs(a - b) < 1; // 3. 使用实例方法设置比较器 var position = new BindableProperty(Vector3.Zero) .WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等 + +// 4. 字符串比较器示例 +var name = new BindableProperty("Player") + .WithComparer((a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); ``` -### [`BindablePropertyUnRegister`](./property.md) +### BindablePropertyUnRegister`` 可绑定属性的注销器,负责清理监听。 @@ -98,6 +127,15 @@ var unregister = health.Register(OnHealthChanged); unregister.UnRegister(); ``` +## BindableProperty 工作原理 + +BindableProperty 基于事件系统实现属性变化通知: + +1. **值设置**:当设置 `Value` 属性时,首先进行值比较 +2. **变化检测**:使用 `EqualityComparer.Default` 或自定义比较器检测值变化 +3. **事件触发**:如果值发生变化,调用所有注册的回调函数 +4. **内存管理**:通过 `IUnRegister` 机制管理监听器的生命周期 + ## 在 Model 中使用 ### 定义可绑定属性 @@ -110,6 +148,7 @@ public class PlayerModel : AbstractModel public BindableProperty Level { get; } = new(1); public BindableProperty Health { get; } = new(100); public BindableProperty MaxHealth { get; } = new(100); + public BindableProperty Position { get; } = new(Vector3.Zero); // 只读属性(外部只能读取和监听) public IReadonlyBindableProperty ReadonlyHealth => Health; @@ -123,7 +162,33 @@ public class PlayerModel : AbstractModel { this.SendEvent(new PlayerDiedEvent()); } + else if (hp < MaxHealth.Value * 0.3f) + { + this.SendEvent(new LowHealthWarningEvent()); + } }); + + // 监听等级变化 + Level.Register(newLevel => + { + this.SendEvent(new PlayerLevelUpEvent { NewLevel = newLevel }); + }); + } + + // 业务方法 + public void TakeDamage(int damage) + { + Health.Value = Math.Max(0, Health.Value - damage); + } + + public void Heal(int amount) + { + Health.Value = Math.Min(MaxHealth.Value, Health.Value + amount); + } + + public float GetHealthPercentage() + { + return (float)Health.Value / MaxHealth.Value; } } ``` @@ -137,6 +202,7 @@ public partial class PlayerUI : Control, IController { [Export] private Label _healthLabel; [Export] private Label _nameLabel; + [Export] private ProgressBar _healthBar; private IUnRegisterList _unregisterList = new UnRegisterList(); @@ -151,6 +217,15 @@ public partial class PlayerUI : Control, IController .RegisterWithInitValue(health => { _healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}"; + _healthBar.Value = (float)health / playerModel.MaxHealth.Value * 100; + }) + .AddToUnregisterList(_unregisterList); + + // 绑定最大生命值 + playerModel.MaxHealth + .RegisterWithInitValue(maxHealth => + { + _healthBar.MaxValue = maxHealth; }) .AddToUnregisterList(_unregisterList); @@ -161,6 +236,17 @@ public partial class PlayerUI : Control, IController _nameLabel.Text = name; }) .AddToUnregisterList(_unregisterList); + + // 绑定位置(仅用于调试显示) + playerModel.Position + .RegisterWithInitValue(pos => + { + // 仅在调试模式下显示 + #if DEBUG + Console.WriteLine($"Player position: {pos}"); + #endif + }) + .AddToUnregisterList(_unregisterList); } public override void _ExitTree() diff --git a/docs/zh-CN/core/query.md b/docs/zh-CN/core/query.md index 3c5abc4..78a7a2a 100644 --- a/docs/zh-CN/core/query.md +++ b/docs/zh-CN/core/query.md @@ -5,6 +5,8 @@ Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。Query 用于封装数据查询逻辑,与 Command 不同的是,Query 有返回值且不应该修改系统状态。 +查询系统是 GFramework CQRS 架构的重要组成部分,专门负责数据读取操作,与命令系统和事件系统协同工作。 + ## 核心接口 ### IQuery`` @@ -19,22 +21,28 @@ TResult Do(); // 执行查询并返回结果 ## 核心类 -### [`AbstractQuery`](./query.md) +### AbstractQuery`` -抽象查询基类,提供了查询的基础实现。它接受一个泛型输入参数 TInput,该参数必须实现 IQueryInput 接口。 +抽象查询基类,提供了查询的基础实现。 + +**核心方法:** + +```csharp +TResult IQuery.Do(); // 实现 IQuery 接口 +protected abstract TResult OnDo(); // 抽象查询方法,由子类实现 +``` **使用方式:** ```csharp -public abstract class AbstractQuery(TInput input) : ContextAwareBase, IQuery - where TInput : IQueryInput +public abstract class AbstractQuery : ContextAwareBase, IQuery { - public TResult Do() => OnDo(input); // 执行查询,传入输入参数 - protected abstract TResult OnDo(TInput input); // 子类实现查询逻辑 + public TResult Do() => OnDo(); // 执行查询 + protected abstract TResult OnDo(); // 子类实现查询逻辑 } ``` -### [`EmptyQueryInput`](./query.md) +### EmptyQueryInput 空查询输入类,用于表示不需要任何输入参数的查询操作。 @@ -47,10 +55,16 @@ public sealed class EmptyQueryInput : IQueryInput } ``` -### [`QueryBus`](./query.md) +### QueryBus 查询总线实现,负责执行查询并返回结果。 +**核心方法:** + +```csharp +TResult Send(IQuery query); // 发送并执行查询 +``` + **使用方式:** ```csharp @@ -69,56 +83,41 @@ public sealed class QueryBus : IQueryBus ### 1. 定义查询 ```csharp -// 定义查询输入参数 -public record GetPlayerGoldQueryInput : IQueryInput; - // 查询玩家金币数量 -public class GetPlayerGoldQuery : AbstractQuery +public class GetPlayerGoldQuery : AbstractQuery { -public GetPlayerGoldQuery() : base(new EmptyQueryInput()) -{ -} - - protected override int OnDo(GetPlayerGoldQueryInput input) + protected override int OnDo() { return this.GetModel().Gold.Value; } - } // 查询玩家是否死亡 -public record IsPlayerDeadQueryInput : IQueryInput; - -public class IsPlayerDeadQuery : AbstractQuery +public class IsPlayerDeadQuery : AbstractQuery { -public IsPlayerDeadQuery() : base(new EmptyQueryInput()) -{ -} - - protected override bool OnDo(IsPlayerDeadQueryInput input) + protected override bool OnDo() { return this.GetModel().Health.Value <= 0; } - } // 查询背包中指定物品的数量 -public record GetItemCountQueryInput(string ItemId) : IQueryInput; - -public class GetItemCountQuery : AbstractQuery +public class GetItemCountQuery : AbstractQuery { -public GetItemCountQuery(string itemId) : base(new GetItemCountQueryInput(itemId)) -{ -} - - protected override int OnDo(GetItemCountQueryInput input) + private readonly string _itemId; + + public GetItemCountQuery(string itemId) { - var inventory = this.GetModel(); - return inventory.GetItemCount(input.ItemId); + _itemId = itemId; } + protected override int OnDo() + { + var inventory = this.GetModel(); + return inventory.GetItemCount(_itemId); + } } - +``` ``` ### 2. 发送查询 @@ -144,11 +143,11 @@ public partial class ShopUI : Control, IController if (playerGold >= _itemPrice) { // 发送购买命令 - this.SendCommand(new BuyItemCommand { ItemId = "sword_01" }); + this.SendCommand(new BuyItemCommand("sword_01")); } else { - GD.Print("金币不足!"); + Console.WriteLine("金币不足!"); } } } @@ -159,11 +158,11 @@ public partial class ShopUI : Control, IController ```csharp public class CombatSystem : AbstractSystem { -protected override void OnInit() -{ -// 注册事件监听 -this.RegisterEvent(OnEnemyAttack); -} + protected override void OnInit() + { + // 注册事件监听 + this.RegisterEvent(OnEnemyAttack); + } private void OnEnemyAttack(EnemyAttackEvent e) { @@ -173,12 +172,11 @@ this.RegisterEvent(OnEnemyAttack); if (!isDead) { // 执行伤害逻辑 - this.SendCommand(new TakeDamageCommand { Damage = e.Damage }); + this.SendCommand(new TakeDamageCommand(e.Damage)); } } - } - +``` ``` ## 高级用法 @@ -189,22 +187,24 @@ this.RegisterEvent(OnEnemyAttack); // 查询指定范围内的敌人列表 public class GetEnemiesInRangeQuery : AbstractQuery> { - public Vector3 Center { get; set; } - public float Radius { get; set; } + private readonly Vector3 _center; + private readonly float _radius; + + public GetEnemiesInRangeQuery(Vector3 center, float radius) + { + _center = center; + _radius = radius; + } protected override List OnDo() { var enemySystem = this.GetSystem(); - return enemySystem.GetEnemiesInRange(Center, Radius); + return enemySystem.GetEnemiesInRange(_center, _radius); } } // 使用 -var enemies = this.SendQuery(new GetEnemiesInRangeQuery -{ - Center = playerPosition, - Radius = 10.0f -}); +var enemies = this.SendQuery(new GetEnemiesInRangeQuery(playerPosition, 10.0f)); ``` ### 2. 组合查询 @@ -213,7 +213,12 @@ var enemies = this.SendQuery(new GetEnemiesInRangeQuery // 查询玩家是否可以使用技能 public class CanUseSkillQuery : AbstractQuery { -public string SkillId { get; set; } + private readonly string _skillId; + + public CanUseSkillQuery(string skillId) + { + _skillId = skillId; + } protected override bool OnDo() { @@ -221,37 +226,44 @@ public string SkillId { get; set; } var skillModel = this.GetModel(); // 查询技能消耗 - var skillCost = this.SendQuery(new GetSkillCostQuery { SkillId = SkillId }); + var skillCost = this.SendQuery(new GetSkillCostQuery(_skillId)); // 检查是否满足条件 return playerModel.Mana.Value >= skillCost.ManaCost - && !this.SendQuery(new IsSkillOnCooldownQuery { SkillId = SkillId }); + && !this.SendQuery(new IsSkillOnCooldownQuery(_skillId)); } - } public class GetSkillCostQuery : AbstractQuery { -public string SkillId { get; set; } + private readonly string _skillId; + + public GetSkillCostQuery(string skillId) + { + _skillId = skillId; + } protected override SkillCost OnDo() { - return this.GetModel().GetSkillCost(SkillId); + return this.GetModel().GetSkillCost(_skillId); } - } public class IsSkillOnCooldownQuery : AbstractQuery { -public string SkillId { get; set; } + private readonly string _skillId; + + public IsSkillOnCooldownQuery(string skillId) + { + _skillId = skillId; + } protected override bool OnDo() { - return this.GetModel().IsOnCooldown(SkillId); + return this.GetModel().IsOnCooldown(_skillId); } - } - +``` ``` ### 3. 聚合数据查询 @@ -332,18 +344,38 @@ protected override void OnInit() { } ``` +## Query 的执行机制 + +所有发送给查询总线的查询最终都会通过 `QueryExecutor` 来执行: + +```csharp +public class QueryExecutor +{ + public static TResult Execute(IQuery query) + { + return query.Do(); + } +} +``` + +**特点:** + +- 提供统一的查询执行机制 +- 支持同步查询执行 +- 与架构上下文集成 + ## Command vs Query ### Command(命令) - **用途**:修改系统状态 -- **返回值**:无返回值(void) +- **返回值**:无返回值(void)或有返回值 - **示例**:购买物品、造成伤害、升级角色 ### Query(查询) - **用途**:读取数据,不修改状态 -- **返回值**:有返回值 +- **返回值**:必须有返回值 - **示例**:获取金币数量、检查技能冷却、查询玩家位置 ```csharp @@ -370,12 +402,17 @@ public class GoodQuery : AbstractQuery // ✅ 修改数据应该使用 Command public class AddGoldCommand : AbstractCommand { - public int Amount { get; set; } + private readonly int _amount; + + public AddGoldCommand(int amount) + { + _amount = amount; + } protected override void OnExecute() { var model = this.GetModel(); - model.Gold.Value += Amount; + model.Gold.Value += _amount; } } ``` @@ -387,6 +424,9 @@ public class AddGoldCommand : AbstractCommand 3. **可组合** - 复杂查询可以通过组合简单查询实现 4. **避免过度查询** - 如果需要频繁查询,考虑使用 BindableProperty 5. **命名清晰** - Query 名称应该清楚表达查询意图(Get、Is、Can、Has等前缀) +6. **参数通过构造函数传递** - 查询需要的参数应在创建时传入 +7. **查询无状态** - 查询不应该保存长期状态,执行完即可丢弃 +8. **合理使用缓存** - 对于复杂计算,可以在 Model 中缓存结果 ## 性能优化 @@ -396,7 +436,7 @@ public class AddGoldCommand : AbstractCommand // 在 Model 中缓存复杂计算 public class PlayerModel : AbstractModel { -private int? _cachedPower; + private int? _cachedPower; public int GetPower() { @@ -417,9 +457,8 @@ private int? _cachedPower; { _cachedPower = null; } - } - +``` ``` ### 2. 批量查询 @@ -428,20 +467,46 @@ private int? _cachedPower; // 一次查询多个数据,而不是多次单独查询 public class GetMultipleItemCountsQuery : AbstractQuery> { - public List ItemIds { get; set; } + private readonly List _itemIds; + + public GetMultipleItemCountsQuery(List itemIds) + { + _itemIds = itemIds; + } protected override Dictionary OnDo() { var inventory = this.GetModel(); - return ItemIds.ToDictionary(id => id, id => inventory.GetItemCount(id)); + return _itemIds.ToDictionary(id => id, id => inventory.GetItemCount(id)); } } ``` +## 查询模式优势 + +### 1. 职责分离 + +- 读写操作明确分离 +- 便于优化读写性能 +- 降低系统复杂度 + +### 2. 可扩展性 + +- 读写可以独立扩展 +- 支持不同的数据存储策略 +- 便于实现读写分离 + +### 3. 可维护性 + +- 查询逻辑集中管理 +- 便于重构和优化 +- 降低组件间耦合 + ## 相关包 - [`command`](./command.md) - CQRS 的命令部分 - [`model`](./model.md) - Query 主要从 Model 获取数据 - [`system`](./system.md) - System 中可以发送 Query - [`controller`](./controller.md) - Controller 中可以发送 Query -- [`extensions`](./extensions.md) - 提供 SendQuery 扩展方法 \ No newline at end of file +- [`extensions`](./extensions.md) - 提供 SendQuery 扩展方法 +- [`architecture`](./architecture.md) - 架构核心,负责查询的分发和执行 \ No newline at end of file diff --git a/docs/zh-CN/core/system.md b/docs/zh-CN/core/system.md index e9c2c47..7ce3691 100644 --- a/docs/zh-CN/core/system.md +++ b/docs/zh-CN/core/system.md @@ -4,9 +4,11 @@ System 包定义了业务逻辑层(Business Logic Layer)。System 负责处理游戏的核心业务逻辑,协调 Model 之间的交互,响应事件并执行复杂的业务流程。 +System 是 GFramework 架构中业务逻辑的核心组件,通过事件系统与 Model 和 Controller 进行通信。 + ## 核心接口 -### [`ICanGetSystem`](./system.md) +### ICanGetSystem 标记接口,表示该类型可以获取其他 System。 @@ -16,7 +18,7 @@ System 包定义了业务逻辑层(Business Logic Layer)。System 负责处 public interface ICanGetSystem : IBelongToArchitecture ``` -### [`ISystem`](./system.md) +### ISystem System 接口,定义了系统的基本行为。 @@ -30,7 +32,10 @@ void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 **继承的能力:** -- `ICanSetArchitecture` - 可设置架构 +- `IContextAware` - 上下文感知 +- `IInitializable` - 可初始化 +- `IDisposable` - 可销毁 +- `IArchitecturePhaseAware` - 架构阶段感知 - `ICanGetModel` - 可获取 Model - `ICanGetUtility` - 可获取 Utility - `ICanGetSystem` - 可获取其他 System @@ -39,33 +44,57 @@ void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 ## 核心类 -### [`AbstractSystem`](./system.md) +### AbstractSystem 抽象 System 基类,提供了 System 的基础实现。继承自 ContextAwareBase,具有上下文感知能力。 +**核心方法:** + +```csharp +void IInitializable.Init(); // 实现初始化接口 +void IDisposable.Destroy(); // 实现销毁接口 +protected abstract void OnInit(); // 抽象初始化方法,由子类实现 +protected virtual void OnDestroy(); // 虚拟销毁方法,子类可重写 +public virtual void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 +``` + **使用方式:** ```csharp public abstract class AbstractSystem : ContextAwareBase, ISystem { - void ISystem.Init() => OnInit(); // 系统初始化,内部调用抽象方法 OnInit() - void ISystem.Destroy() => OnDestroy(); // 系统销毁,内部调用 OnDestroy() - protected abstract void OnInit(); // 子类实现初始化逻辑 - protected virtual void OnDestroy(); // 子类可选择重写销毁逻辑 + void IInitializable.Init() => OnInit(); // 系统初始化,内部调用抽象方法 OnInit() + void IDisposable.Destroy() => OnDestroy(); // 系统销毁,内部调用 OnDestroy() + protected abstract void OnInit(); // 子类实现初始化逻辑 + protected virtual void OnDestroy(); // 子类可选择重写销毁逻辑 public virtual void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 } ``` +## System 生命周期 + +System 的生命周期由架构管理: + +1. **注册阶段**:通过 `architecture.RegisterSystem()` 注册到架构 +2. **初始化阶段**:架构调用 `Init()` 方法,内部执行 `OnInit()` +3. **运行阶段**:处理事件和执行业务逻辑 +4. **销毁阶段**:架构调用 `Destroy()` 方法,内部执行 `OnDestroy()` + ## 基本使用 ### 1. 定义 System -``` +```csharp // 战斗系统 public class CombatSystem : AbstractSystem { + private ILogger _logger; + protected override void OnInit() { + _logger = this.GetUtility(); + _logger.Info("CombatSystem initialized"); + // 注册事件监听 this.RegisterEvent(OnEnemyAttack); this.RegisterEvent(OnPlayerAttack); @@ -83,6 +112,8 @@ public class CombatSystem : AbstractSystem // 发送伤害事件 this.SendEvent(new PlayerTookDamageEvent { Damage = damage }); + + _logger.Debug($"Player took {damage} damage from enemy attack"); } private void OnPlayerAttack(PlayerAttackEvent e) @@ -98,6 +129,8 @@ public class CombatSystem : AbstractSystem EnemyId = e.Enemy.Id, Damage = damage }); + + _logger.Debug($"Enemy {e.Enemy.Id} took {damage} damage from player attack"); } private int CalculateDamage(int attackPower, int defense) @@ -107,34 +140,50 @@ public class CombatSystem : AbstractSystem protected override void OnDestroy() { + _logger.Info("CombatSystem destroyed"); // 清理资源 base.OnDestroy(); } + + public override void OnArchitecturePhase(ArchitecturePhase phase) + { + switch (phase) + { + case ArchitecturePhase.AfterSystemInit: + _logger.Info("CombatSystem is ready"); + break; + case ArchitecturePhase.Destroying: + _logger.Info("CombatSystem is shutting down"); + break; + } + } } ``` ### 2. 注册 System -``` +```csharp public class GameArchitecture : Architecture { protected override void Init() { // 注册 Model - this.RegisterModel(new PlayerModel()); - this.RegisterModel(new EnemyModel()); + this.RegisterModel(new PlayerModel()); + this.RegisterModel(new EnemyModel()); + this.RegisterModel(new InventoryModel()); // 注册 System(系统注册后会自动调用 Init) - this.RegisterSystem(new CombatSystem()); - this.RegisterSystem(new InventorySystem()); - this.RegisterSystem(new QuestSystem()); + this.RegisterSystem(new CombatSystem()); + this.RegisterSystem(new InventorySystem()); + this.RegisterSystem(new QuestSystem()); + this.RegisterSystem(new UISystem()); } } ``` ### 3. 在其他组件中获取 System -``` +```csharp // 在 Controller 中 public partial class GameController : Node, IController { @@ -145,6 +194,9 @@ public partial class GameController : Node, IController // 获取 System var combatSystem = this.GetSystem(); var questSystem = this.GetSystem(); + + // 使用 System + combatSystem.StartBattle(); } } @@ -154,7 +206,17 @@ public class StartBattleCommand : AbstractCommand protected override void OnExecute() { var combatSystem = this.GetSystem(); - // 使用 System... + combatSystem.StartBattle(); + } +} + +// 在其他 System 中 +public class AISystem : AbstractSystem +{ + protected override void OnInit() + { + var combatSystem = this.GetSystem(); + // 与其他 System 协作 } } ```