diff --git a/GFramework.Core/architecture/README.md b/GFramework.Core/architecture/README.md index 0e157cb..a8fdef3 100644 --- a/GFramework.Core/architecture/README.md +++ b/GFramework.Core/architecture/README.md @@ -8,7 +8,7 @@ Architecture 包是整个框架的核心,提供了基于 MVC 架构模式的 ## 核心接口 -### [`IArchitecture`](IArchitecture.cs) +### IArchitecture 架构接口,定义了框架的核心功能契约。 @@ -28,9 +28,9 @@ void RegisterSystem(TSystem system) where TSystem : ISystem; void RegisterModel(TModel model) where TModel : IModel; void RegisterUtility(TUtility utility) where TUtility : IUtility; -// 组件获取 -T GetSystem() where T : class, ISystem; +// 组件获取(通过容器) T GetModel() where T : class, IModel; +T GetSystem() where T : class, ISystem; T GetUtility() where T : class, IUtility; // 命令处理 @@ -47,7 +47,7 @@ IUnRegister RegisterEvent(Action onEvent); void UnRegisterEvent(Action onEvent); ``` -### [`IArchitecturePhaseAware`](IArchitecturePhaseAware.cs) +### IArchitecturePhaseAware 架构阶段感知接口,允许组件监听架构阶段变化。 @@ -57,7 +57,7 @@ void UnRegisterEvent(Action onEvent); void OnArchitecturePhase(ArchitecturePhase phase); ``` -### [`IArchitectureModule`](IArchitectureModule.cs) +### IArchitectureModule 架构模块接口,支持模块化架构扩展。 @@ -67,7 +67,7 @@ void OnArchitecturePhase(ArchitecturePhase phase); void Install(IArchitecture architecture); ``` -### [`IAsyncInitializable`](IAsyncInitializable.cs) +### IAsyncInitializable 异步初始化接口,支持组件异步初始化。 @@ -83,6 +83,17 @@ Task InitializeAsync(); 架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。 +**构造函数参数:** + +```csharp +public abstract class Architecture( + IArchitectureConfiguration? configuration = null, + IEnvironment? environment = null, + IArchitectureServices? services = null, + IArchitectureContext? context = null +) +``` + **特性:** - **阶段式生命周期管理** @@ -132,7 +143,7 @@ public enum ArchitecturePhase **使用示例:** -```csharp +``csharp // 1. 定义你的架构(继承 Architecture 基类) public class GameArchitecture : Architecture { @@ -196,7 +207,7 @@ public class GameController : IController **高级特性:** -```csharp +``csharp // 1. 使用自定义配置 var config = new ArchitectureConfiguration(); var architecture = new GameArchitecture(configuration: config); @@ -238,7 +249,7 @@ public class LifecycleHook : IArchitectureLifecycle **使用示例:** -```csharp +``csharp var config = new ArchitectureConfiguration { // 严格阶段验证 diff --git a/GFramework.Core/command/README.md b/GFramework.Core/command/README.md index f29aa4f..139e1a2 100644 --- a/GFramework.Core/command/README.md +++ b/GFramework.Core/command/README.md @@ -10,16 +10,6 @@ Command 包实现了命令模式(Command Pattern),用于封装用户操作 无返回值命令接口,定义了命令的基本契约。 -**继承的能力接口:** - -- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构 -- [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统 -- [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型 -- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具 -- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件 -- [`ICanSendCommand`](ICanSendCommand.cs) - 可发送命令 -- [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询 - **核心方法:** ```csharp @@ -36,30 +26,39 @@ void Execute(); // 执行命令 TResult Execute(); // 执行命令并返回结果 ``` -### 3. [`ICanSendCommand`](ICanSendCommand.cs) - -标记接口,表示实现者可以发送命令。继承自 [`IBelongToArchitecture`](../rule/IBelongToArchitecture.cs)。 - ## 核心类 -### 1. [`AbstractCommand`](AbstractCommand.cs) +### 1. [`AbstractCommand`](AbstractCommand.cs) -无返回值命令的抽象基类,提供了命令的基础实现。 +无返回值命令的抽象基类,提供了命令的基础实现。它继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28) +,具有上下文感知能力。 **使用示例:** ```csharp -// 定义一个开始游戏的命令 -public class StartGameCommand : AbstractCommand +// 定义一个命令输入参数 +public struct StartGameCommandInput : ICommandInput { - protected override void OnExecute() + public int LevelId { get; set; } + public string PlayerName { get; set; } +} + +// 定义一个开始游戏的命令 +public class StartGameCommand : AbstractCommand +{ + public StartGameCommand(StartGameCommandInput input) : base(input) + { + } + + protected override void OnExecute(StartGameCommandInput input) { // 获取需要的模型 var playerModel = this.GetModel(); var gameModel = this.GetModel(); // 执行业务逻辑 - playerModel.Health.Value = 100; + playerModel.PlayerName.Value = input.PlayerName; + gameModel.CurrentLevel.Value = input.LevelId; gameModel.GameState.Value = GameState.Playing; // 发送事件通知其他模块 @@ -74,41 +73,44 @@ public class GameController : IController public void OnStartButtonClicked() { - // 方式1:发送命令实例 - this.SendCommand(new StartGameCommand()); - - // 方式2:通过泛型发送(需要无参构造函数) - this.SendCommand(); + // 发送命令实例 + this.SendCommand(new StartGameCommand(new StartGameCommandInput + { + LevelId = 1, + PlayerName = "Player1" + })); } } ``` -### 2. [`AbstractCommand`](AbstractCommand.cs) +### 2. [`AbstractCommand`](AbstractCommand.cs) -带返回值命令的抽象基类。 +带返回值命令的抽象基类,同样继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)。 **使用示例:** ```csharp -// 定义一个计算伤害的命令 -public class CalculateDamageCommand : AbstractCommand +// 定义一个计算伤害的命令输入 +public struct CalculateDamageCommandInput : ICommandInput { - private readonly int _attackPower; - private readonly int _defense; - - public CalculateDamageCommand(int attackPower, int defense) + public int AttackerAttackPower { get; set; } + public int DefenderDefense { get; set; } +} + +// 定义一个计算伤害的命令 +public class CalculateDamageCommand : AbstractCommand +{ + public CalculateDamageCommand(CalculateDamageCommandInput input) : base(input) { - _attackPower = attackPower; - _defense = defense; } - - protected override int OnExecute() + + protected override int OnExecute(CalculateDamageCommandInput input) { // 获取游戏配置 var config = this.GetModel(); // 计算最终伤害 - var baseDamage = _attackPower - _defense; + var baseDamage = input.AttackerAttackPower - input.DefenderDefense; var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier); return (int)finalDamage; @@ -123,9 +125,11 @@ public class CombatSystem : AbstractSystem public void Attack(Character attacker, Character defender) { // 发送命令并获取返回值 - var damage = this.SendCommand( - new CalculateDamageCommand(attacker.AttackPower, defender.Defense) - ); + var damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput + { + AttackerAttackPower = attacker.AttackPower, + DefenderDefense = defender.Defense + })); // 应用伤害 defender.Health -= damage; @@ -139,26 +143,78 @@ public class CombatSystem : AbstractSystem ## 命令的生命周期 1. **创建命令**:实例化命令对象,传入必要的参数 -2. **设置架构**:框架自动调用 `SetArchitecture()` 设置架构引用 -3. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()` -4. **返回结果**:对于带返回值的命令,返回执行结果 -5. **命令销毁**:命令执行完毕后可以被垃圾回收 +2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()` +3. **返回结果**:对于带返回值的命令,返回执行结果 +4. **命令销毁**:命令执行完毕后可以被垃圾回收 + +## CommandBus - 命令总线 + +### 功能说明 + +[CommandBus](file:///d:/Project/Rider/GFramework/GFramework.Core/command/CommandBus.cs#L8-L34) 是命令执行的核心组件,负责发送和执行命令。 + +**主要方法:** + +```csharp +void Send(ICommand command); // 发送无返回值命令 +TResult Send(ICommand command); // 发送带返回值命令 +``` + +### 使用示例 + +```csharp +var commandBus = new CommandBus(); + +// 发送无返回值命令 +commandBus.Send(new StartGameCommand(new StartGameCommandInput())); + +// 发送带返回值命令 +var result = commandBus.Send(new CalculateDamageCommand(new CalculateDamageCommandInput())); +``` + +## EmptyCommandInput - 空命令输入 + +当命令不需要输入参数时,可以使用 [EmptyCommentInput](file:///d:/Project/Rider/GFramework/GFramework.Core/command/EmptyCommentInput.cs#L7-L11) +类: + +```csharp +public class SimpleActionCommand : AbstractCommand +{ + public SimpleActionCommand(EmptyCommentInput input) : base(input) + { + } + + protected override void OnExecute(EmptyCommentInput input) + { + // 执行简单操作,无需额外参数 + this.SendEvent(new SimpleActionEvent()); + } +} +``` ## 使用场景 ### 1. 用户交互操作 ```csharp -// UI 按钮点击 -public class SaveGameCommand : AbstractCommand +public struct SaveGameCommandInput : ICommandInput { - protected override void OnExecute() + public string SaveSlot { get; set; } +} + +public class SaveGameCommand : AbstractCommand +{ + public SaveGameCommand(SaveGameCommandInput input) : base(input) + { + } + + protected override void OnExecute(SaveGameCommandInput input) { var saveSystem = this.GetSystem(); var playerModel = this.GetModel(); - saveSystem.SavePlayerData(playerModel); - this.SendEvent(new GameSavedEvent()); + saveSystem.SavePlayerData(playerModel, input.SaveSlot); + this.SendEvent(new GameSavedEvent(input.SaveSlot)); } } ``` @@ -166,17 +222,18 @@ public class SaveGameCommand : AbstractCommand ### 2. 业务流程控制 ```csharp -// 关卡切换 -public class LoadLevelCommand : AbstractCommand +public struct LoadLevelCommandInput : ICommandInput { - private readonly int _levelId; - - public LoadLevelCommand(int levelId) + public int LevelId { get; set; } +} + +public class LoadLevelCommand : AbstractCommand +{ + public LoadLevelCommand(LoadLevelCommandInput input) : base(input) { - _levelId = levelId; } - - protected override void OnExecute() + + protected override void OnExecute(LoadLevelCommandInput input) { var levelSystem = this.GetSystem(); var uiSystem = this.GetSystem(); @@ -185,99 +242,50 @@ public class LoadLevelCommand : AbstractCommand uiSystem.ShowLoadingScreen(); // 加载关卡 - levelSystem.LoadLevel(_levelId); + levelSystem.LoadLevel(input.LevelId); // 发送事件 - this.SendEvent(new LevelLoadedEvent(_levelId)); + this.SendEvent(new LevelLoadedEvent(input.LevelId)); } } ``` -### 3. 网络请求封装 - -```csharp -// 登录命令 -public class LoginCommand : AbstractCommand -{ - private readonly string _username; - private readonly string _password; - - public LoginCommand(string username, string password) - { - _username = username; - _password = password; - } - - protected override bool OnExecute() - { - var networkSystem = this.GetSystem(); - var playerModel = this.GetModel(); - - // 发送登录请求 - var result = networkSystem.Login(_username, _password); - - if (result.Success) - { - playerModel.UserId = result.UserId; - playerModel.Username = _username; - this.SendEvent(new LoginSuccessEvent()); - return true; - } - else - { - this.SendEvent(new LoginFailedEvent(result.ErrorMessage)); - return false; - } - } -} -``` - -## 命令 vs 系统方法 - -**何时使用命令:** - -- 需要参数化操作 -- 需要记录操作历史(用于撤销/重做) -- 操作需要跨多个系统协调 -- 用户触发的离散操作 - -**何时使用系统方法:** - -- 持续运行的逻辑(如每帧更新) -- 系统内部的私有逻辑 -- 不需要外部调用的功能 - ## 最佳实践 1. **保持命令原子性**:一个命令应该完成一个完整的业务操作 2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃 3. **参数通过构造函数传递**:命令需要的参数应在创建时传入 4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信 -5. **合理使用返回值**:只在确实需要返回结果时使用 `ICommand` +5. **合理使用返回值**:只在确实需要返回结果时使用 `AbstractCommand` 6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand`、`SavePlayerCommand` +7. **输入参数结构化**:使用 `ICommandInput` 接口的实现类来组织命令参数 ## 扩展功能 ### 命令撤销/重做(可扩展) ```csharp -// 可撤销命令接口 -public interface IUndoableCommand : ICommand +public struct MoveCommandInput : ICommandInput { - void Undo(); + public Vector3 NewPosition { get; set; } } // 实现可撤销命令 -public class MoveCommand : AbstractCommand, IUndoableCommand +public class MoveCommand : AbstractCommand, IUndoableCommand { private Vector3 _oldPosition; private Vector3 _newPosition; - protected override void OnExecute() + public MoveCommand(MoveCommandInput input) : base(input) + { + _newPosition = input.NewPosition; + } + + protected override void OnExecute(MoveCommandInput input) { var player = this.GetModel(); _oldPosition = player.Position; - player.Position = _newPosition; + player.Position = input.NewPosition; } public void Undo() diff --git a/GFramework.Core/environment/README.md b/GFramework.Core/environment/README.md new file mode 100644 index 0000000..2db0ef1 --- /dev/null +++ b/GFramework.Core/environment/README.md @@ -0,0 +1,218 @@ +# Environment 包使用说明 + +## 概述 + +Environment 包定义了环境配置功能。Environment 提供了一个键值对存储系统,用于在运行时存储和获取各种环境特定的值,如配置选项、路径设置等。它允许应用程序在不同环境下灵活调整行为。 + +## 核心接口 + +### IEnvironment + +环境接口,定义了环境值的存储和获取功能。 + +**核心成员:** + +```csharp +string Name { get; } // 环境名称 +T? Get(string key) where T : class; // 根据键获取值 +bool TryGet(string key, out T value) where T : class; // 尝试获取值 +T GetRequired(string key) where T : class; // 获取必需值(不存在则抛异常) +void Register(string key, object value); // 注册键值对 +void Initialize(); // 初始化环境 +``` + +## 核心类 + +### [`EnvironmentBase`](EnvironmentBase.cs) + +环境基础抽象类,实现了 IEnvironment 接口,提供环境值的存储和获取功能。 + +**使用方式:** + +```csharp +public abstract class EnvironmentBase : ContextAwareBase, IEnvironment +{ + protected readonly Dictionary Values = new(); // 存储环境值 + + public abstract string Name { get; } // 环境名称 + + public virtual T? Get(string key) where T : class + { + return TryGet(key, out var value) ? value : null; + } + + public virtual bool TryGet(string key, out T value) where T : class + { + if (Values.TryGetValue(key, out var obj) && obj is T typed) + { + value = typed; + return true; + } + + value = null!; + return false; + } + + public virtual T GetRequired(string key) where T : class + { + if (TryGet(key, out var value)) + return value; + + throw new InvalidOperationException( + $"Environment [{Name}] missing required value: key='{key}', type='{typeof(T).Name}'"); + } + + void IEnvironment.Register(string key, object value); + protected void Register(string key, object value); // 注册键值对 + public abstract void Initialize(); // 初始化环境 +} +``` + +### [`DefaultEnvironment`](DefaultEnvironment.cs) + +默认环境实现类,继承自 EnvironmentBase。 + +**使用方式:** + +```csharp +public class DefaultEnvironment : EnvironmentBase +{ + public override string Name { get; } = "Default"; // 环境名称 + + public override void Initialize() + { + // 默认环境初始化逻辑 + } +} +``` + +## 基本使用 + +### 1. 定义自定义环境 + +```csharp +public class GameEnvironment : EnvironmentBase +{ + public override string Name { get; } = "Game"; + + public override void Initialize() + { + // 注册一些环境特定的值 + Register("GameMode", "Survival"); + Register("MaxPlayers", 4); + Register("ServerAddress", "localhost"); + } + + // 便捷方法 + public string GameMode => Get("GameMode") ?? "Default"; + public int MaxPlayers => Get("MaxPlayers") ?? 1; + public string ServerAddress => Get("ServerAddress") ?? "localhost"; +} +``` + +### 2. 在架构中使用环境 + +```csharp +public class GameArchitecture : Architecture +{ + protected override void Init() + { + // 环境已经在架构初始化过程中自动初始化 + // 但是我们可以在需要的时候获取环境值 + var gameMode = this.Context.Environment.Get("GameMode"); + + // 注册模型和系统 + RegisterModel(new PlayerModel()); + RegisterSystem(new GameplaySystem()); + } +} +``` + +### 3. 使用环境值 + +```csharp +public class NetworkSystem : AbstractSystem +{ + private string _serverAddress; + + protected override void OnInit() + { + // 从环境中获取服务器地址 + _serverAddress = this.GetContext().Environment.Get("ServerAddress") ?? "localhost"; + + // 注册事件 + this.RegisterEvent(OnConnectToServer); + } + + private void OnConnectToServer(ConnectToServerEvent e) + { + // 使用环境中的服务器地址 + Connect(_serverAddress, e.Port); + } + + private void Connect(string address, int port) + { + // 连接逻辑 + } +} +``` + +## Environment vs Configuration + +### Environment(环境) + +- **动态性**:运行时可以更改 +- **灵活性**:根据部署环境调整行为 +- **存储类型**:运行时值、连接信息等 +- **访问方式**:通过键值对访问 + +### Configuration(配置) + +- **静态性**:通常在启动时确定 +- **持久性**:保存在文件或外部源 +- **存储类型**:应用设置、参数等 +- **访问方式**:通过配置对象访问 + +## 最佳实践 + +1. **明确环境名称**:为每个环境提供有意义的名称 +2. **类型安全**:使用泛型方法确保类型安全 +3. **错误处理**:使用 GetRequired 方法获取必需值 +4. **初始化时机**:在架构初始化期间完成环境设置 +5. **避免过度使用**:仅存储环境相关的值 + +## 错误示例 + +```csharp +// ❌ 错误:获取必需值但不存在 +public class BadExampleSystem : AbstractSystem +{ + protected override void OnInit() + { + // 如果"RequiredValue"不存在,这会抛出异常 + var value = this.GetContext().Environment.GetRequired("RequiredValue"); + } +} + +// ✅ 正确:安全获取值 +public class GoodExampleSystem : AbstractSystem +{ + protected override void OnInit() + { + // 使用 TryGet 安全获取值 + if (this.GetContext().Environment.TryGet("OptionalValue", out var value)) + { + // 处理值 + } + + // 或者提供默认值 + var gameMode = this.GetContext().Environment.Get("GameMode") ?? "Default"; + } +} +``` + +## 相关包 + +- [`architecture`](../architecture/README.md) - 在架构中使用环境配置 +- [`rule`](../rule/README.md) - 环境基类继承自 ContextAwareBase +- [`ioc`](../ioc/README.md) - 环境值可通过IoC容器管理 \ No newline at end of file diff --git a/GFramework.Core/events/README.md b/GFramework.Core/events/README.md index 80651d6..4d50cbc 100644 --- a/GFramework.Core/events/README.md +++ b/GFramework.Core/events/README.md @@ -6,7 +6,7 @@ Events 包提供了一套完整的事件系统,实现了观察者模式(Obse ## 核心接口 -### 1. [`IEasyEvent`](IEasyEvent.cs) +### 1. [`IEvent`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEvent.cs#L7-L11) 基础事件接口,定义了事件注册的基本功能。 @@ -16,7 +16,7 @@ Events 包提供了一套完整的事件系统,实现了观察者模式(Obse IUnRegister Register(Action onEvent); // 注册事件处理函数 ``` -### 2. [`IUnRegister`](IUnRegister.cs) +### 2. [`IUnRegister`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegister.cs#L6-L10) 注销接口,用于取消事件注册。 @@ -26,20 +26,28 @@ IUnRegister Register(Action onEvent); // 注册事件处理函数 void UnRegister(); // 执行注销操作 ``` -### 3. [`IUnRegisterList`](IUnRegisterList.cs) +### 3. [ +`IUnRegisterList`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegisterList.cs#L6-L10) 注销列表接口,用于批量管理注销对象。 **属性:** ```csharp -List UnregisterList { get; } // 获取注销列表 +IList UnregisterList { get; } // 获取注销列表 ``` -### 4. 能力接口 +### 4. [`IEventBus`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEventBus.cs#L6-L22) -- [`ICanRegisterEvent`](ICanRegisterEvent.cs) - 标记可以注册事件的组件 -- [`ICanSendEvent`](ICanSendEvent.cs) - 标记可以发送事件的组件 +事件总线接口,提供基于类型的事件发送和注册。 + +**核心方法:** + +```csharp +IUnRegister Register(Action onEvent); // 注册类型化事件 +void Send(T e); // 发送事件实例 +void Send() where T : new(); // 发送事件(自动创建实例) +``` ## 核心类 @@ -66,7 +74,7 @@ onClicked.Trigger(); unregister.UnRegister(); ``` -### 2. [`EasyEvent`](EasyEventGeneric.cs) +### 2. [`Event`](EasyEventGeneric.cs) 单参数泛型事件类,支持一个参数的事件。 @@ -74,7 +82,7 @@ unregister.UnRegister(); ```csharp // 创建带参数的事件 -var onScoreChanged = new EasyEvent(); +var onScoreChanged = new Event(); // 注册监听 onScoreChanged.Register(newScore => @@ -86,7 +94,7 @@ onScoreChanged.Register(newScore => onScoreChanged.Trigger(100); ``` -### 3. [`EasyEvent`](EasyEventGeneric.cs) +### 3. [`Event`](EasyEventGeneric.cs) 双参数泛型事件类。 @@ -94,7 +102,7 @@ onScoreChanged.Trigger(100); ```csharp // 伤害事件:攻击者、伤害值 -var onDamageDealt = new EasyEvent(); +var onDamageDealt = new Event(); onDamageDealt.Register((attacker, damage) => { @@ -104,25 +112,7 @@ onDamageDealt.Register((attacker, damage) => onDamageDealt.Trigger("Player", 50); ``` -### 4. [`EasyEvent`](EasyEventGeneric.cs) - -三参数泛型事件类。 - -**使用示例:** - -```csharp -// 位置变化事件:对象、旧位置、新位置 -var onPositionChanged = new EasyEvent(); - -onPositionChanged.Register((obj, oldPos, newPos) => -{ - GD.Print($"{obj} moved from {oldPos} to {newPos}"); -}); - -onPositionChanged.Trigger("Player", Vector3.Zero, new Vector3(10, 0, 0)); -``` - -### 5. [`EasyEvents`](EasyEvents.cs) +### 4. [`EasyEvents`](EasyEvents.cs) 全局事件管理器,提供类型安全的事件注册和获取。 @@ -145,7 +135,7 @@ gameStartEvent.Register(() => gameStartEvent.Trigger(); ``` -### 6. [`TypeEventSystem`](TypeEventSystem.cs) +### 5. [`EventBus`](EventBus.cs) 类型化事件系统,支持基于类型的事件发送和注册。 @@ -153,25 +143,25 @@ gameStartEvent.Trigger(); ```csharp // 使用全局事件系统 -var eventSystem = TypeEventSystem.Global; +var eventBus = new EventBus(); // 注册类型化事件 -eventSystem.Register(e => +eventBus.Register(e => { GD.Print($"Player died at position: {e.Position}"); }); -// 发送事件(自动创建实例) -eventSystem.Send(); - // 发送事件(传递实例) -eventSystem.Send(new PlayerDiedEvent +eventBus.Send(new PlayerDiedEvent { Position = new Vector3(10, 0, 5) }); + +// 发送事件(自动创建实例) +eventBus.Send(); ``` -### 7. [`DefaultUnRegister`](DefaultUnRegister.cs) +### 6. [`DefaultUnRegister`](DefaultUnRegister.cs) 默认注销器实现,封装注销回调。 @@ -185,7 +175,7 @@ var unregister = new DefaultUnRegister(onUnregister); unregister.UnRegister(); ``` -### 8. [`OrEvent`](OrEvent.cs) +### 7. [`OrEvent`](OrEvent.cs) 事件或运算组合器,当任意一个事件触发时触发。 @@ -204,6 +194,33 @@ onAnyInput.Register(() => }); ``` +### 8. [`UnRegisterList`](UnRegisterList.cs) + +批量管理注销对象的列表。 + +**使用示例:** + +```csharp +var unregisterList = new UnRegisterList(); + +// 添加到列表 +someEvent.Register(OnEvent).AddToUnregisterList(unregisterList); + +// 批量注销 +unregisterList.UnRegisterAll(); +``` + +### 9. [`ArchitectureEvents`](ArchitectureEvents.cs) + +定义了架构生命周期相关的事件。 + +**包含事件:** + +- `ArchitectureLifecycleReadyEvent` - 架构生命周期准备就绪 +- `ArchitectureDestroyingEvent` - 架构销毁中 +- `ArchitectureDestroyedEvent` - 架构已销毁 +- `ArchitectureFailedInitializationEvent` - 架构初始化失败 + ## 在架构中使用事件 ### 定义事件类 diff --git a/GFramework.Core/extensions/README.md b/GFramework.Core/extensions/README.md index 0bb2144..3681d11 100644 --- a/GFramework.Core/extensions/README.md +++ b/GFramework.Core/extensions/README.md @@ -6,64 +6,69 @@ Extensions 包提供了一系列扩展方法,简化了框架各个接口的使 ## 扩展方法类别 -### 1. 获取组件扩展 ([`CanGetExtensions.cs`](CanGetExtensions.cs)) +### 1. ContextAware 扩展 ([`ContextAwareExtensions.cs`](ContextAwareExtensions.cs)) -为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[ -`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。 +为 [`IContextAware`](../../GFramework.Core.Abstractions/rule/IContextAware.cs) +提供扩展方法,允许直接从实现了 [IContextAware](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs) +的对象获取架构组件。 -#### CanGetModelExtension +#### GetSystem 扩展方法 ```csharp -public static T GetModel(this ICanGetModel self) where T : class, IModel +public static TSystem? GetSystem(this IContextAware contextAware) + where TSystem : class, ISystem ``` **使用示例:** ```csharp -// 在 Controller、Command、Query 中使用 +// 在实现了 IContextAware 的类中使用 public class PlayerController : IController { public void UpdateUI() { // 直接通过 this 调用 + var playerSystem = this.GetSystem(); + var inventorySystem = this.GetSystem(); + } +} +``` + +#### GetModel 扩展方法 + +```csharp +public static TModel? GetModel(this IContextAware contextAware) + where TModel : class, IModel +``` + +**使用示例:** + +```csharp +public class PlayerController : IController +{ + public void UpdateStats() + { + // 获取模型 var playerModel = this.GetModel(); var inventoryModel = this.GetModel(); - } -} -``` - -#### CanGetSystemExtension - -```csharp -public static T GetSystem(this ICanGetSystem self) where T : class, ISystem -``` - -**使用示例:** - -```csharp -public class SaveCommand : AbstractCommand -{ - protected override void OnExecute() - { - // 获取系统 - var saveSystem = this.GetSystem(); - var networkSystem = this.GetSystem(); - saveSystem.SaveGame(); + // 使用模型数据 + playerModel.Health += 10; } } ``` -#### CanGetUtilityExtension +#### GetUtility 扩展方法 ```csharp -public static T GetUtility(this ICanGetUtility self) where T : class, IUtility +public static TUtility? GetUtility(this IContextAware contextAware) + where TUtility : class, IUtility ``` **使用示例:** ```csharp -public class GameModel : AbstractModel +public class GameModel : AbstractModel, IContextAware { protected override void OnInit() { @@ -74,24 +79,14 @@ public class GameModel : AbstractModel } ``` -### 2. 发送命令扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs)) - -为 [`ICanSendCommand`](../command/ICanSendCommand.cs) 提供扩展方法。 - -#### CanSendCommandExtension +#### SendCommand 扩展方法 ```csharp -// 发送无参命令(通过类型) -public static void SendCommand(this ICanSendCommand self) - where T : ICommand, new() - -// 发送命令实例 -public static void SendCommand(this ICanSendCommand self, T command) - where T : ICommand +// 发送无返回值的命令 +public static void SendCommand(this IContextAware contextAware, ICommand command) // 发送带返回值的命令 -public static TResult SendCommand(this ICanSendCommand self, - ICommand command) +public static TResult SendCommand(this IContextAware contextAware, ICommand command) ``` **使用示例:** @@ -101,36 +96,51 @@ public class GameController : IController { public void OnStartButtonClicked() { - // 方式1:通过类型发送(需要无参构造函数) - this.SendCommand(); + // 发送命令实例 + this.SendCommand(new StartGameCommand()); - // 方式2:发送命令实例 - this.SendCommand(new LoadLevelCommand(levelId: 1)); - - // 方式3:发送带返回值的命令 - var score = this.SendCommand(new CalculateScoreCommand()); + // 发送带返回值的命令 + var result = this.SendCommand(new CalculateScoreCommand()); } } ``` -### 3. 发送事件扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs)) - -为 [`ICanSendEvent`](../events/ICanSendEvent.cs) 提供扩展方法。 - -#### CanSendEventExtension +#### SendQuery 扩展方法 ```csharp -// 发送无参事件 -public static void SendEvent(this ICanSendEvent self) where T : new() - -// 发送事件实例 -public static void SendEvent(this ICanSendEvent self, T e) +public static TResult SendQuery(this IContextAware contextAware, IQuery query) ``` **使用示例:** ```csharp -public class PlayerModel : AbstractModel +public class InventoryController : IController +{ + public void ShowInventory() + { + // 发送查询获取数据 + var items = this.SendQuery(new GetInventoryItemsQuery()); + var gold = this.SendQuery(new GetPlayerGoldQuery()); + + UpdateInventoryUI(items, gold); + } +} +``` + +#### SendEvent 扩展方法 + +```csharp +// 发送无参事件 +public static void SendEvent(this IContextAware contextAware) where T : new() + +// 发送事件实例 +public static void SendEvent(this IContextAware contextAware, T e) where T : class +``` + +**使用示例:** + +```csharp +public class PlayerModel : AbstractModel, IContextAware { public void TakeDamage(int damage) { @@ -152,57 +162,20 @@ public class PlayerModel : AbstractModel } ``` -### 4. 发送查询扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs)) - -为 [`ICanSendQuery`](../query/ICanSendQuery.cs) 提供扩展方法。 - -#### CanSendQueryExtension +#### RegisterEvent 扩展方法 ```csharp -public static TResult SendQuery(this ICanSendQuery self, - IQuery query) +public static IUnRegister RegisterEvent(this IContextAware contextAware, Action handler) ``` **使用示例:** ```csharp -public class InventoryController : IController -{ - public void ShowInventory() - { - // 发送查询获取数据 - var items = this.SendQuery(new GetInventoryItemsQuery()); - var gold = this.SendQuery(new GetPlayerGoldQuery()); - - UpdateInventoryUI(items, gold); - } -} -``` - -### 5. 注册事件扩展 ([`CanRegisterEventExtensions.cs`](CanRegisterEventExtensions.cs)) - -为 [`ICanRegisterEvent`](../events/ICanRegisterEvent.cs) 提供扩展方法。 - -#### CanRegisterEventExtensions - -```csharp -// 注册事件 -public static IUnRegister RegisterEvent(this ICanRegisterEvent self, - Action onEvent) - -// 注销事件 -public static void UnRegisterEvent(this ICanRegisterEvent self, - Action onEvent) -``` - -**使用示例:** - -```csharp -public class GameController : Node, IController +public class GameController : IController { private IUnRegisterList _unregisterList = new UnRegisterList(); - public override void _Ready() + public void Initialize() { // 注册事件监听 this.RegisterEvent(OnGameStarted) @@ -217,14 +190,143 @@ public class GameController : Node, IController } ``` -### 6. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs)) +#### UnRegisterEvent 扩展方法 -为 [`IEasyEvent`](../events/IEasyEvent.cs) 提供事件组合功能。 +```csharp +public static void UnRegisterEvent(this IContextAware contextAware, Action onEvent) +``` + +### GetEnvironment 扩展方法 + +```csharp +public static T? GetEnvironment(this IContextAware contextAware) where T : class +public static IEnvironment GetEnvironment(this IContextAware contextAware) +``` + +### 2. Object 扩展 ([`ObjectExtensions.cs`](ObjectExtensions.cs)) + +提供基于运行时类型判断的对象扩展方法,用于简化类型分支、链式调用和架构分派逻辑。 + +#### IfType 扩展方法 + +```csharp +// 最简单的类型判断 +public static bool IfType(this object obj, Action action) + +// 带条件的类型判断 +public static bool IfType( + this object obj, + Func predicate, + Action action +) + +// 条件判断,带不匹配时的处理 +public static void IfType( + this object obj, + Action whenMatch, + Action? whenNotMatch = null +) +``` + +**使用示例:** + +```csharp +object obj = new MyRule(); + +// 简单类型判断 +bool executed = obj.IfType(rule => +{ + rule.Initialize(); +}); + +// 带条件的类型判断 +obj.IfType( + r => r.Enabled, // 条件 + r => r.Execute() // 执行动作 +); + +// 带不匹配处理的类型判断 +obj.IfType( + rule => rule.Execute(), + other => Logger.Warn($"Unsupported type: {other.GetType()}") +); +``` + +#### IfType 扩展方法 + +```csharp +public static TResult? IfType( + this object obj, + Func func +) +``` + +**使用示例:** + +```csharp +object obj = new MyRule { Name = "TestRule" }; + +string? name = obj.IfType(r => r.Name); +``` + +#### As 和 Do 扩展方法 + +```csharp +// 安全类型转换 +public static T? As(this object obj) where T : class + +// 流式调用 +public static T Do(this T obj, Action action) +``` + +**使用示例:** + +```csharp +// 安全类型转换 +obj.As() + ?.Execute(); + +// 流式调用 +obj.As() + ?.Do(r => r.Initialize()) + ?.Do(r => r.Execute()); + +// 组合使用 +obj.As() + ?.Do(rule => + { + if (rule.Enabled) + rule.Execute(); + }); +``` + +#### SwitchType 扩展方法 + +```csharp +public static void SwitchType( + this object obj, + params (Type type, Action action)[] handlers +) +``` + +**使用示例:** + +```csharp +obj.SwitchType( + (typeof(IRule), o => HandleRule((IRule)o)), + (typeof(ISystem), o => HandleSystem((ISystem)o)), + (typeof(IModel), o => HandleModel((IModel)o)) +); +``` + +### 3. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs)) + +为 [`IEvent`](../../GFramework.Core.Abstractions/events/IEvent.cs) 提供事件组合功能。 #### OrEventExtensions ```csharp -public static OrEvent Or(this IEasyEvent self, IEasyEvent e) +public static OrEvent Or(this IEvent self, IEvent e) ``` **使用示例:** @@ -244,41 +346,7 @@ var onAnyDamage = onPhysicalDamage .Or(onPoisonDamage); ``` -### 7. UnRegister 扩展 ([`UnRegisterExtension.cs`](UnRegisterExtension.cs)) - -为 [`IUnRegister`](../events/IUnRegister.cs) 提供 Godot 生命周期绑定。 - -#### UnRegisterExtension - -```csharp -public static IUnRegister UnRegisterWhenNodeExitTree(this IUnRegister unRegister, - Node node) -``` - -**使用示例:** - -```csharp -#if GODOT -public class PlayerController : Node, IController -{ - public override void _Ready() - { - // 当节点退出场景树时自动注销 - this.RegisterEvent(OnGameEvent) - .UnRegisterWhenNodeExitTree(this); - - this.GetModel() - .Health - .Register(OnHealthChanged) - .UnRegisterWhenNodeExitTree(this); - } - - // 不需要手动在 _ExitTree 中注销 -} -#endif -``` - -### 8. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs)) +### 4. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs)) 为 [`IUnRegister`](../events/IUnRegister.cs) 和 [`IUnRegisterList`](../events/IUnRegisterList.cs) 提供批量管理功能。 @@ -296,11 +364,11 @@ public static void UnRegisterAll(this IUnRegisterList self) **使用示例:** ```csharp -public class ComplexController : Node, IController +public class ComplexController : IController { private IUnRegisterList _unregisterList = new UnRegisterList(); - public override void _Ready() + public void Initialize() { // 所有注册都添加到列表中 this.RegisterEvent(OnEvent1) @@ -316,7 +384,7 @@ public class ComplexController : Node, IController .AddToUnregisterList(_unregisterList); } - public override void _ExitTree() + public void Cleanup() { // 一次性注销所有 _unregisterList.UnRegisterAll(); @@ -329,13 +397,11 @@ public class ComplexController : Node, IController ### Controller 示例 ```csharp -public partial class GameplayController : Node, IController +public partial class GameplayController : IController { private IUnRegisterList _unregisterList = new UnRegisterList(); - public IArchitecture GetArchitecture() => GameArchitecture.Interface; - - public override void _Ready() + public void Initialize() { // 使用扩展方法获取 Model var playerModel = this.GetModel(); @@ -348,34 +414,24 @@ public partial class GameplayController : Node, IController // 监听可绑定属性 playerModel.Health.Register(OnHealthChanged) .AddToUnregisterList(_unregisterList); - - // 或者使用 Godot 特定的自动注销 - gameModel.Score.Register(OnScoreChanged) - .UnRegisterWhenNodeExitTree(this); } - public override void _Process(double delta) + public void Process(double delta) { - if (Input.IsActionJustPressed("attack")) - { - // 使用扩展方法发送命令 - this.SendCommand(new AttackCommand(targetId: 1)); - } + // 发送命令 + this.SendCommand(new AttackCommand(targetId: 1)); - if (Input.IsActionJustPressed("use_item")) + // 发送查询 + var hasPotion = this.SendQuery(new HasItemQuery("health_potion")); + if (hasPotion) { - // 使用扩展方法发送查询 - var hasPotion = this.SendQuery(new HasItemQuery("health_potion")); - if (hasPotion) - { - this.SendCommand(); - } + this.SendCommand(); } } private void OnGameStarted(GameStartedEvent e) { - GD.Print("Game started!"); + Console.WriteLine("Game started!"); } private void OnHealthChanged(int health) @@ -383,12 +439,7 @@ public partial class GameplayController : Node, IController UpdateHealthBar(health); } - private void OnScoreChanged(int score) - { - UpdateScoreDisplay(score); - } - - public override void _ExitTree() + public void Cleanup() { _unregisterList.UnRegisterAll(); } @@ -467,7 +518,8 @@ public class AchievementSystem : AbstractSystem ## 扩展方法的优势 -1. **简洁的语法**:不需要显式调用 `GetArchitecture()` +1. **简洁的语法** + :不需要显式调用 [GetContext()](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L13-L15) 2. **类型安全**:编译时检查类型 3. **可读性高**:代码意图更清晰 4. **智能提示**:IDE 可以提供完整的自动补全 @@ -475,21 +527,17 @@ public class AchievementSystem : AbstractSystem ## 注意事项 -1. **确保引用命名空间**: +1. **确保引用命名空间:** ```csharp - using GFramework.framework.extensions; + using GFramework.Core.extensions; ``` -2. **理解扩展方法本质**: +2. **理解扩展方法本质:** - 扩展方法是静态方法的语法糖 - 不会改变原始类型的结构 - 仅在编译时解析 -3. **Godot 特定功能**: - - `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用 - - 使用 `#if GODOT` 编译指令控制 - -4. **性能考虑**: +3. **性能考虑:** - 扩展方法本身无性能开销 - 实际调用的是底层方法 diff --git a/GFramework.Core/ioc/README.md b/GFramework.Core/ioc/README.md index 00a89d9..8ba06f2 100644 --- a/GFramework.Core/ioc/README.md +++ b/GFramework.Core/ioc/README.md @@ -16,20 +16,24 @@ IoC 容器类,负责管理对象的注册和获取。 - 注册实例到容器 - 从容器中获取实例 - 类型安全的依赖管理 +- 线程安全操作 +- 容器冻结保护 ## 核心方法 -### 1. Register +### 1. Register 和 Register(Type, object) 注册一个实例到容器中。 ```csharp public void Register(T instance) +public void Register(Type type, object instance) ``` **参数:** - `instance`: 要注册的实例对象 +- `type`: 要注册的类型(重载方法) **使用示例:** @@ -42,29 +46,104 @@ container.Register(new GameSystem()); container.Register(new StorageUtility()); ``` -### 2. Get +### 2. RegisterSingleton + +注册单例实例到容器中。一个类型只允许一个实例。 + +```csharp +public void RegisterSingleton(T instance) +``` + +**参数:** + +- `instance`: 要注册的单例实例 + +**使用示例:** + +```csharp +var container = new IocContainer(); + +// 注册单例 +container.RegisterSingleton(new PlayerModel()); +``` + +### 3. RegisterPlurality + +注册多个实例,将实例注册到其实现的所有接口和具体类型上。 + +```csharp +public void RegisterPlurality(object instance) +``` + +**参数:** + +- `instance`: 要注册的实例 + +### 4. RegisterSystem + +注册系统实例,将其绑定到其所有实现的接口上。 + +```csharp +public void RegisterSystem(ISystem system) +``` + +**参数:** + +- `system`: 系统实例对象 + +### 5. Get 和 GetAll 从容器中获取指定类型的实例。 ```csharp -public T Get() where T : class +public T? Get() where T : class +public IReadOnlyList GetAll() where T : class ``` **返回值:** -- 返回指定类型的实例,如果未找到则返回 `null` +- `Get`: 返回指定类型的实例,如果未找到则返回 `null` +- `GetAll`: 返回指定类型的所有实例列表,如果未找到则返回空数组 **使用示例:** ```csharp // 获取已注册的实例 var playerModel = container.Get(); -var gameSystem = container.Get(); +var gameSystems = container.GetAll(); -// 如果类型未注册,返回 null +// 如果类型未注册,Get 返回 null,GetAll 返回空数组 var unknownService = container.Get(); // null ``` +### 6. GetRequired + +获取指定类型的必需实例,如果没有注册或注册了多个实例会抛出异常。 + +```csharp +public T GetRequired() where T : class +``` + +**返回值:** + +- 返回找到的唯一实例 + +### 7. GetAllSorted + +获取并排序(系统调度专用)。 + +```csharp +public IReadOnlyList GetAllSorted(Comparison comparison) where T : class +``` + +**参数:** + +- `comparison`: 比较器委托,定义排序规则 + +**返回值:** + +- 按指定方式排序后的实例列表 + ## 在框架中的使用 ### Architecture 中的应用 @@ -142,27 +221,66 @@ public class PlayerController : IController ```csharp public class IocContainer { - // 使用字典存储类型到实例的映射 - private readonly Dictionary _mInstances = new(); + // 使用字典存储类型到实例集合的映射 + private readonly Dictionary> _typeIndex = new(); + private readonly HashSet _objects = []; + private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion); + private volatile bool _frozen = false; public void Register(T instance) { - var key = typeof(T); - _mInstances[key] = instance; // 注册或覆盖 + // 获取写锁以确保线程安全 + _lock.EnterWriteLock(); + try + { + RegisterInternal(typeof(T), instance!); + } + finally + { + _lock.ExitWriteLock(); + } } - public T Get() where T : class + public T? Get() where T : class { - var key = typeof(T); - if (_mInstances.TryGetValue(key, out var retInstance)) + _lock.EnterReadLock(); + try { - return retInstance as T; // 类型转换 + if (_typeIndex.TryGetValue(typeof(T), out var set) && set.Count > 0) + { + var result = set.First() as T; + return result; + } + + return null; } - return null; + finally + { + _lock.ExitReadLock(); + } + } + + private void RegisterInternal(Type type, object instance) + { + if (_frozen) throw new InvalidOperationException("IocContainer is frozen"); + + _objects.Add(instance); + + if (!_typeIndex.TryGetValue(type, out var set)) + { + set = []; + _typeIndex[type] = set; + } + + set.Add(instance); } } ``` +### 线程安全机制 + +容器使用 [ReaderWriterLockSlim](xref:System.Threading.ReaderWriterLockSlim) 来确保线程安全操作,允许多个线程同时读取,但在写入时阻止其他线程访问。 + ### 注册流程 ``` @@ -172,7 +290,7 @@ RegisterSystem(system) ↓ IocContainer.Register(system) ↓ -Dictionary[typeof(T)] = system +加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet ``` ### 获取流程 @@ -186,9 +304,9 @@ Architecture.GetSystem() ↓ IocContainer.Get() ↓ -Dictionary.TryGetValue(typeof(T)) +加读锁 -> Dictionary.TryGetValue(typeof(T)) 获取HashSet ↓ -返回实例或 null +返回HashSet中第一个实例或 null ``` ## 使用示例 @@ -245,27 +363,28 @@ var dataService = container.Get(); dataService.SaveData("game data"); ``` -### 覆盖注册 +### 注册多个实现 ```csharp var container = new IocContainer(); -// 首次注册 -container.Register(new DefaultConfig()); +// 注册多个相同接口的不同实现 +container.Register(new LocalDataService()); +container.Register(new CloudDataService()); -// 后续注册会覆盖 -container.Register(new CustomConfig()); +// 获取单个实例(返回第一个) +var singleService = container.Get(); // 返回第一个注册的实例 -// 获取到的是最后注册的实例 -var config = container.Get(); // CustomConfig +// 获取所有实例 +var allServices = container.GetAll(); // 返回两个实例的列表 ``` ## 设计特点 ### 1. 简单轻量 -- 只有两个核心方法:`Register` 和 `Get` -- 基于字典实现,性能高效 +- 支持多种注册方式:普通注册、单例注册、多实例注册 +- 基于字典和哈希集实现,性能高效 - 无复杂的依赖解析逻辑 ### 2. 手动注册 @@ -274,11 +393,11 @@ var config = container.Get(); // CustomConfig - 不支持自动依赖注入 - 完全可控的组件生命周期 -### 3. 单例模式 +### 3. 多实例支持 -- 每个类型只能注册一个实例 -- 适合管理全局单例服务 -- 后续注册会覆盖前面的实例 +- 每个类型可以注册多个实例 +- 提供 [GetAll](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L244-L261) 方法获取所有实例 +- 提供 [Get](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L208-L225) 方法获取单个实例 ### 4. 类型安全 @@ -286,6 +405,17 @@ var config = container.Get(); // CustomConfig - 避免字符串键导致的错误 - IDE 友好,支持自动补全 +### 5. 线程安全 + +- 使用读写锁确保多线程环境下的安全操作 +- 读操作可以并发执行 +- 写操作独占锁,防止并发修改冲突 + +### 6. 容器冻结 + +- 提供 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372) 方法,防止进一步修改容器内容 +- 防止在初始化后意外修改注册内容 + ## 与其他 IoC 容器的区别 ### 本框架的 IocContainer @@ -301,6 +431,8 @@ var service = container.Get(); - ✅ 简单易用 - ✅ 性能高 +- ✅ 线程安全 +- ✅ 支持多实例 - ❌ 不支持构造函数注入 - ❌ 不支持自动解析依赖 - ❌ 不支持生命周期管理(Transient/Scoped/Singleton) @@ -394,56 +526,37 @@ else } ``` -## 注意事项 - -1. **类型唯一性** - - 每个类型只能注册一个实例 - - 重复注册会覆盖之前的实例 - -2. **手动管理依赖顺序** - - 组件的依赖关系需要手动保证 - - 先注册被依赖的组件 - -3. **无生命周期管理** - - 实例一旦注册就一直存在 - - 需要手动管理实例的生命周期 - -4. **线程安全** - - 当前实现非线程安全 - - 避免多线程同时访问 - -## 扩展可能性 - -如果需要更复杂的功能,可以扩展 `IocContainer`: +### 5. 合理使用容器冻结 ```csharp -// 支持工厂模式 -public class AdvancedIocContainer : IocContainer +// 在架构初始化完成后冻结容器,防止意外修改 +protected override void OnInit() { - private Dictionary> _factories = new(); + // 注册所有组件 + RegisterModel(new PlayerModel()); + RegisterSystem(new GameSystem()); + // ... - public void RegisterFactory(Func factory) where T : class - { - _factories[typeof(T)] = () => factory(); - } - - public new T Get() where T : class - { - // 先尝试获取实例 - var instance = base.Get(); - if (instance != null) return instance; - - // 如果没有实例,尝试使用工厂创建 - if (_factories.TryGetValue(typeof(T), out var factory)) - { - return factory() as T; - } - - return null; - } + // 冻结容器 + Container.Freeze(); // 此后无法再注册新组件 } ``` +## 注意事项 + +1. **线程安全操作**:容器内部使用读写锁确保线程安全,无需额外同步 + +2. **容器冻结**:一旦调用 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372) + 方法,将不能再注册新实例 + +3. **单例注册限制 + **:[RegisterSingleton](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L84-L106) + 方法确保一个类型只能有一个实例,重复注册会抛出异常 + +4. **内存管理**:容器持有的实例不会自动释放,需要注意内存泄漏问题 + +5. **注册顺序**:组件的依赖关系需要手动保证,先注册被依赖的组件 + ## 相关包 - [`architecture`](../architecture/README.md) - 使用 IoC 容器管理所有组件 diff --git a/GFramework.Core/logging/README.md b/GFramework.Core/logging/README.md index f78446c..ed06183 100644 --- a/GFramework.Core/logging/README.md +++ b/GFramework.Core/logging/README.md @@ -6,7 +6,7 @@ Logging 包提供了灵活的日志系统,支持多级别日志记录。默认 ## 核心接口 -### [`ILogger`](ILogger.cs) +### [ILogger](ILogger.cs) 日志记录器接口,定义了日志记录的基本功能。 @@ -62,7 +62,7 @@ void Fatal(string msg, Exception t); string Name(); ``` -### [`ILoggerFactory`](ILoggerFactory.cs) +### [ILoggerFactory](ILoggerFactory.cs) 日志工厂接口,用于创建日志记录器实例。 @@ -72,7 +72,7 @@ string Name(); ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info); ``` -### [`ILoggerFactoryProvider`](ILoggerFactory.cs) +### [ILoggerFactoryProvider](ILoggerFactoryProvider.cs) 日志工厂提供程序接口,用于获取日志工厂。 @@ -83,7 +83,7 @@ ILoggerFactory GetLoggerFactory(); ILogger CreateLogger(string name); ``` -### [`LogLevel`](LogLevel.cs) +### [LogLevel](LogLevel.cs) 日志级别枚举。 @@ -101,7 +101,7 @@ public enum LogLevel ## 核心类 -### [`AbstractLogger`](AbstractLogger.cs) +### [AbstractLogger](AbstractLogger.cs) 抽象日志基类,封装了日志级别判断、格式化与异常处理逻辑。平台日志器只需实现 `Write` 方法即可。 @@ -127,7 +127,7 @@ public class CustomLogger : AbstractLogger } ``` -### [`ConsoleLogger`](ConsoleLogger.cs) +### [ConsoleLogger](ConsoleLogger.cs) 控制台日志记录器实现,支持彩色输出。 @@ -162,7 +162,14 @@ logger.Fatal("致命错误"); - **Error**: 红色 - **Fatal**: 洋红色 -### [`ConsoleLoggerFactory`](ConsoleLoggerFactory.cs) +**构造函数参数:** + +- `name`:日志器名称,默认为 "ROOT" +- `minLevel`:最低日志级别,默认为 LogLevel.Info +- `writer`:TextWriter 输出流,默认为 Console.Out +- `useColors`:是否使用颜色,默认为 true(仅在输出到控制台时生效) + +### [ConsoleLoggerFactory](ConsoleLoggerFactory.cs) 控制台日志工厂,用于创建控制台日志记录器实例。 @@ -174,7 +181,7 @@ var logger = factory.GetLogger("MyModule", LogLevel.Debug); logger.Info("日志记录器创建成功"); ``` -### [`ConsoleLoggerFactoryProvider`](ConsoleLoggerFactoryProvider.cs) +### [ConsoleLoggerFactoryProvider](ConsoleLoggerFactoryProvider.cs) 控制台日志工厂提供程序实现。 @@ -182,11 +189,12 @@ logger.Info("日志记录器创建成功"); ```csharp var provider = new ConsoleLoggerFactoryProvider(); -var factory = provider.GetLoggerFactory(); -var logger = factory.GetLogger("MyApp", LogLevel.Info); +provider.MinLevel = LogLevel.Debug; // 设置最低日志级别 +var logger = provider.CreateLogger("MyApp"); +logger.Info("应用程序启动"); ``` -### [`LoggerFactoryResolver`](LoggerFactoryResolver.cs) +### [LoggerFactoryResolver](LoggerFactoryResolver.cs) 日志工厂提供程序解析器,用于管理和提供日志工厂提供程序实例。 @@ -270,9 +278,9 @@ public class DebugLogger : AbstractLogger // 只输出调试及更高级别的日志 if (level >= LogLevel.Debug) { - GD.Print($"[{level}] {message}"); + Console.WriteLine($"[{level}] {message}"); if (exception != null) - GD.Print(exception); + Console.WriteLine(exception); } } } @@ -341,15 +349,16 @@ public class DebugLogger : AbstractLogger - 支持字符串格式化参数 - 支持异常信息传递 -3. **自定义比较器**: - - 在 `BindableProperty` 中可以使用 `WithComparer` 设置自定义比较器 +3. **ConsoleLogger 的额外参数**: + - ConsoleLogger 现在支持自定义TextWriter输出流 + - 支持禁用颜色输出的功能(useColors参数) ## 相关包 -- [`architecture`](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件 -- [`property`](../property/README.md) - 可绑定属性基于事件系统实现 -- [`extensions`](../extensions/README.md) - 提供便捷的扩展方法 +- [architecture](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件 +- [property](../property/README.md) - 可绑定属性基于事件系统实现 +- [extensions](../extensions/README.md) - 提供便捷的扩展方法 --- -**许可证**: Apache 2.0 +**许可证**: Apache 2.0 \ No newline at end of file diff --git a/GFramework.Core/model/README.md b/GFramework.Core/model/README.md index 92f5027..df29049 100644 --- a/GFramework.Core/model/README.md +++ b/GFramework.Core/model/README.md @@ -13,14 +13,14 @@ Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的 **继承的能力接口:** -- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构引用 -- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具类 -- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件 +- [`IContextAware`](../rule/IContextAware.cs) - 上下文感知接口 +- [`ILogAware`](../rule/ILogAware.cs) - 日志感知接口 **核心方法:** ```csharp void Init(); // 初始化模型 +void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 ``` ### [`ICanGetModel`](ICanGetModel.cs) @@ -31,7 +31,8 @@ void Init(); // 初始化模型 ### [`AbstractModel`](AbstractModel.cs) -抽象模型基类,提供模型的基础实现。 +抽象模型基类,实现IModel接口,提供模型的基础实现。该类继承自[ +`ContextAwareBase`](file:///d:/Project/Rider/GFramework/GFramework.Core/rule/ContextAwareBase.cs#L11-L37),提供了上下文感知能力。 **使用示例:** @@ -55,6 +56,20 @@ public class PlayerModel : AbstractModel } }); } + + public override void OnArchitecturePhase(ArchitecturePhase phase) + { + switch (phase) + { + case ArchitecturePhase.Initializing: + // 架构初始化阶段的处理 + break; + case ArchitecturePhase.Ready: + // 架构就绪阶段的处理 + break; + // ... 其他阶段处理 + } + } } ``` @@ -66,6 +81,7 @@ public class PlayerModel : AbstractModel 2. **提供数据访问接口** 3. **监听自身属性变化并做出响应** 4. **发送数据变化事件** +5. **处理架构生命周期事件** ### ❌ 不应该做的事 @@ -99,6 +115,19 @@ public class PlayerModel : AbstractModel } }); } + + public override void OnArchitecturePhase(ArchitecturePhase phase) + { + switch (phase) + { + case ArchitecturePhase.Ready: + // 模型准备好后的处理 + _log?.Log("PlayerModel is ready."); + break; + default: + break; + } + } } ``` @@ -123,6 +152,18 @@ public class GameModel : AbstractModel } }); } + + public override void OnArchitecturePhase(ArchitecturePhase phase) + { + switch (phase) + { + case ArchitecturePhase.ShuttingDown: + // 游戏模型清理工作 + break; + default: + break; + } + } } ``` diff --git a/GFramework.Core/query/README.md b/GFramework.Core/query/README.md index aa53297..38633f1 100644 --- a/GFramework.Core/query/README.md +++ b/GFramework.Core/query/README.md @@ -7,17 +7,7 @@ Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。 ## 核心接口 -### [`ICanSendQuery`](ICanSendQuery.cs) - -标记接口,表示该类型可以发送查询。 - -**继承关系:** - -```csharp -public interface ICanSendQuery : IBelongToArchitecture -``` - -### [`IQuery`](IQuery.cs) +### IQuery 查询接口,定义了查询的基本契约。 @@ -27,26 +17,50 @@ public interface ICanSendQuery : IBelongToArchitecture TResult Do(); // 执行查询并返回结果 ``` -**继承的能力:** - -- `ICanSetArchitecture` - 可设置架构 -- `ICanGetModel` - 可获取 Model -- `ICanGetSystem` - 可获取 System -- `ICanSendQuery` - 可发送其他 Query - ## 核心类 -### [`AbstractQuery`](AbstractQuery.cs) +### [`AbstractQuery`](AbstractQuery.cs) -抽象查询基类,提供了查询的基础实现。 +抽象查询基类,提供了查询的基础实现。它接受一个泛型输入参数 TInput,该参数必须实现 IQueryInput 接口。 **使用方式:** ```csharp -public abstract class AbstractQuery : IQuery +public abstract class AbstractQuery(TInput input) : ContextAwareBase, IQuery + where TInput : IQueryInput { - public T Do() => OnDo(); - protected abstract T OnDo(); // 子类实现查询逻辑 + public TResult Do() => OnDo(input); // 执行查询,传入输入参数 + protected abstract TResult OnDo(TInput input); // 子类实现查询逻辑 +} +``` + +### [`EmptyQueryInput`](EmptyQueryInput.cs) + +空查询输入类,用于表示不需要任何输入参数的查询操作。 + +**使用方式:** + +```csharp +public sealed class EmptyQueryInput : IQueryInput +{ + // 作为占位符使用,适用于那些不需要额外输入参数的查询场景 +} +``` + +### [`QueryBus`](QueryBus.cs) + +查询总线实现,负责执行查询并返回结果。 + +**使用方式:** + +```csharp +public sealed class QueryBus : IQueryBus +{ + public TResult Send(IQuery query) + { + ArgumentNullException.ThrowIfNull(query); + return query.Do(); + } } ``` @@ -54,41 +68,58 @@ public abstract class AbstractQuery : IQuery ### 1. 定义查询 -```csharp +``csharp +// 定义查询输入参数 +public record GetPlayerGoldQueryInput : IQueryInput; + // 查询玩家金币数量 -public class GetPlayerGoldQuery : AbstractQuery +public class GetPlayerGoldQuery : AbstractQuery { - protected override int OnDo() +public GetPlayerGoldQuery() : base(new EmptyQueryInput()) +{ +} + + protected override int OnDo(GetPlayerGoldQueryInput input) { return this.GetModel().Gold.Value; } } // 查询玩家是否死亡 -public class IsPlayerDeadQuery : AbstractQuery +public record IsPlayerDeadQueryInput : IQueryInput; + +public class IsPlayerDeadQuery : AbstractQuery { - protected override bool OnDo() +public IsPlayerDeadQuery() : base(new EmptyQueryInput()) +{ +} + + protected override bool OnDo(IsPlayerDeadQueryInput input) { return this.GetModel().Health.Value <= 0; } } // 查询背包中指定物品的数量 -public class GetItemCountQuery : AbstractQuery +public record GetItemCountQueryInput(string ItemId) : IQueryInput; + +public class GetItemCountQuery : AbstractQuery { - public string ItemId { get; set; } - - protected override int OnDo() +public GetItemCountQuery(string itemId) : base(new GetItemCountQueryInput(itemId)) +{ +} + + protected override int OnDo(GetItemCountQueryInput input) { var inventory = this.GetModel(); - return inventory.GetItemCount(ItemId); + return inventory.GetItemCount(input.ItemId); } } ``` ### 2. 发送查询(在 Controller 中) -```csharp +``csharp public partial class ShopUI : Control, IController { [Export] private Button _buyButton; @@ -121,7 +152,7 @@ public partial class ShopUI : Control, IController ### 3. 在 System 中使用 -```csharp +``csharp public class CombatSystem : AbstractSystem { protected override void OnInit() @@ -148,7 +179,7 @@ public class CombatSystem : AbstractSystem ### 1. 带参数的复杂查询 -```csharp +``csharp // 查询指定范围内的敌人列表 public class GetEnemiesInRangeQuery : AbstractQuery> { @@ -172,7 +203,7 @@ var enemies = this.SendQuery(new GetEnemiesInRangeQuery ### 2. 组合查询 -```csharp +``csharp // 查询玩家是否可以使用技能 public class CanUseSkillQuery : AbstractQuery { @@ -215,7 +246,7 @@ public class IsSkillOnCooldownQuery : AbstractQuery ### 3. 聚合数据查询 -```csharp +``csharp // 查询玩家战斗力 public class GetPlayerPowerQuery : AbstractQuery { @@ -254,7 +285,7 @@ public class GetPlayerInfoQuery : AbstractQuery ### 4. 跨 System 查询 -```csharp +``csharp // 在 AI System 中查询玩家状态 public class EnemyAISystem : AbstractSystem { @@ -303,7 +334,7 @@ public class EnemyAISystem : AbstractSystem - **返回值**:有返回值 - **示例**:获取金币数量、检查技能冷却、查询玩家位置 -```csharp +``csharp // ❌ 错误:在 Query 中修改状态 public class BadQuery : AbstractQuery { @@ -349,7 +380,7 @@ public class AddGoldCommand : AbstractCommand ### 1. 缓存查询结果 -```csharp +``csharp // 在 Model 中缓存复杂计算 public class PlayerModel : AbstractModel { @@ -379,7 +410,7 @@ public class PlayerModel : AbstractModel ### 2. 批量查询 -```csharp +``csharp // 一次查询多个数据,而不是多次单独查询 public class GetMultipleItemCountsQuery : AbstractQuery> { diff --git a/GFramework.Core/rule/README.md b/GFramework.Core/rule/README.md index 8da3b87..367f21a 100644 --- a/GFramework.Core/rule/README.md +++ b/GFramework.Core/rule/README.md @@ -6,85 +6,84 @@ Rule 包定义了框架的核心规则接口,这些接口规定了框架各个 ## 核心接口 -### [`IBelongToArchitecture`](IBelongToArchitecture.cs) +### IContextAware -标记接口,表示某个对象属于特定的架构体系。 +标记接口,表示该类型可以感知架构上下文。 **接口定义:** ```csharp -public interface IBelongToArchitecture +public interface IContextAware { - IArchitecture GetArchitecture(); + void SetContext(IArchitectureContext context); + IArchitectureContext GetContext(); } ``` **实现此接口的类型:** - -- Controller - System +- Query - Model - Command -- Query -- Event 处理器 +- 以及其他需要感知架构上下文的组件 **作用:** -所有实现此接口的类型都能够获取其所属的架构实例,从而访问架构提供的各种能力。 +所有实现此接口的类型都能够获取其所属的架构上下文实例,从而访问架构提供的各种能力。 -### [`ICanSetArchitecture`](ICanSetArchitecture.cs) +## 核心类 -定义可以设置架构实例的能力。 +### [`ContextAwareBase`](ContextAwareBase.cs) -**接口定义:** +上下文感知基类,实现了 IContextAware 接口,为需要感知架构上下文的类提供基础实现。 + +**使用方式:** ```csharp -public interface ICanSetArchitecture +public abstract class ContextAwareBase : IContextAware { - void SetArchitecture(IArchitecture architecture); + protected IArchitectureContext? Context { get; set; } + + void IContextAware.SetContext(IArchitectureContext context) + { + Context = context; + OnContextReady(); // 上下文准备好后调用此方法 + } + + IArchitectureContext IContextAware.GetContext() + { + Context ??= GameContext.GetFirstArchitectureContext(); + return Context; + } + + protected virtual void OnContextReady() // 子类可以重写此方法进行初始化 + { + } } ``` -**实现此接口的类型:** - -- Command -- Query - -**作用:** -在 Command 和 Query 执行前,框架会自动调用 `SetArchitecture` 方法注入架构实例,使其能够访问 Model、System 等组件。 - ## 接口关系图 ``` -IBelongToArchitecture (属于架构) - ↓ 被继承 - ├── ICanGetModel (可获取 Model) - ├── ICanGetSystem (可获取 System) - ├── ICanGetUtility (可获取 Utility) - ├── ICanSendCommand (可发送 Command) - ├── ICanSendEvent (可发送 Event) - ├── ICanSendQuery (可发送 Query) - └── ICanRegisterEvent (可注册 Event) - -ICanSetArchitecture (可设置架构) - ↓ 被继承 - ├── ICommand (命令接口) - └── IQuery (查询接口) +IContextAware (上下文感知接口) + ↓ 被继承于 + ├── AbstractSystem (抽象系统基类) + ├── AbstractQuery (抽象查询基类) + ├── AbstractModel (抽象模型基类) + └── AbstractCommand (抽象命令基类) ``` ## 使用场景 -### 1. Controller 实现 IBelongToArchitecture +### 1. Component 继承 ContextAwareBase ```csharp -// Controller 通过实现 IBelongToArchitecture 获得架构访问能力 +// 组件通过继承 ContextAwareBase 获得架构上下文访问能力 public partial class PlayerController : Node, IController { - // 实现 IBelongToArchitecture 接口 - public IArchitecture GetArchitecture() => GameArchitecture.Interface; - + // 不再需要手动实现 IContextAware,基类已处理 + // 可以直接使用扩展方法 public override void _Ready() { - // 因为实现了 IBelongToArchitecture,所以可以使用扩展方法 var playerModel = this.GetModel(); this.SendCommand(new InitPlayerCommand()); this.RegisterEvent(OnPlayerDied); @@ -97,17 +96,17 @@ public partial class PlayerController : Node, IController } ``` -### 2. Command 实现 ICanSetArchitecture +### 2. Command 继承 AbstractCommand (IContextAware) ```csharp -// Command 实现 ICanSetArchitecture,框架会自动注入架构 +// Command 继承 AbstractCommand,自动成为 IContextAware public class BuyItemCommand : AbstractCommand { public string ItemId { get; set; } protected override void OnExecute() { - // 框架已经通过 SetArchitecture 注入了架构实例 + // 框架或上下文系统会自动注入 IArchitectureContext // 所以这里可以直接使用 this.GetModel var playerModel = this.GetModel(); var shopModel = this.GetModel(); @@ -125,21 +124,15 @@ public class BuyItemCommand : AbstractCommand ### 3. 自定义组件遵循规则 ```csharp -// 自定义管理器遵循框架规则 -public class SaveManager : IBelongToArchitecture +// 自定义管理器遵循框架规则,继承 ContextAwareBase +public class SaveManager : ContextAwareBase { - private IArchitecture _architecture; - - public SaveManager(IArchitecture architecture) - { - _architecture = architecture; - } - - public IArchitecture GetArchitecture() => _architecture; + // 不再需要手动构造函数传参,上下文由框架注入 + // protected override void OnContextReady() 可用于初始化 public void SaveGame() { - // 因为实现了 IBelongToArchitecture,可以使用扩展方法 + // 因为继承了 ContextAwareBase,可以使用扩展方法 var playerModel = this.GetModel(); var saveData = new SaveData { @@ -219,9 +212,10 @@ Rule 接口体现了依赖注入(DI)的思想: ```csharp // 接口定义了"需要什么" -public interface IBelongToArchitecture +public interface IContextAware { - IArchitecture GetArchitecture(); + void SetContext(IArchitectureContext context); + IArchitectureContext GetContext(); } // 框架负责"提供什么" @@ -230,8 +224,8 @@ public static class CanSendExtensions public static void SendCommand(this ICanSendCommand self, T command) where T : ICommand { - // 自动注入架构依赖 - command.SetArchitecture(self.GetArchitecture()); + // 自动注入架构上下文依赖 + command.SetContext(self.GetContext()); command.Execute(); } } @@ -245,8 +239,8 @@ Rule 接口遵循接口隔离原则,每个接口职责单一: // ❌ 不好的设计:一个大接口包含所有能力 public interface IBigInterface { - IArchitecture GetArchitecture(); - void SetArchitecture(IArchitecture architecture); + void SetContext(IArchitectureContext context); + IArchitectureContext GetContext(); T GetModel() where T : class, IModel; T GetSystem() where T : class, ISystem; void SendCommand(T command) where T : ICommand; @@ -254,8 +248,7 @@ public interface IBigInterface } // ✅ 好的设计:小接口组合 -public interface IBelongToArchitecture { ... } // 只负责获取架构 -public interface ICanSetArchitecture { ... } // 只负责设置架构 +public interface IContextAware { ... } // 只负责上下文的设置与获取 public interface ICanGetModel { ... } // 只负责获取 Model public interface ICanSendCommand { ... } // 只负责发送 Command ``` @@ -271,14 +264,14 @@ public interface IController : ICanGetModel, ICanGetSystem, ICanSendCommand, { } -// Command 需要设置架构和获取 Model/System -public interface ICommand : ICanSetArchitecture, ICanGetModel, ICanGetSystem, +// Command 需要上下文感知和获取 Model/System +public interface ICommand : IContextAware, ICanGetModel, ICanGetSystem, ICanSendEvent, ICanSendQuery { } // System 只需要获取其他组件 -public interface ISystem : ICanGetModel, ICanGetUtility, ICanGetSystem, +public interface ISystem : IContextAware, ICanGetModel, ICanGetUtility, ICanGetSystem, ICanRegisterEvent, ICanSendEvent, ICanSendQuery { } @@ -324,10 +317,10 @@ public class DatabaseCommand : AbstractCommand, ICanAccessDatabase ## 相关包 -- [`architecture`](../architecture/README.md) - 定义 IArchitecture 接口 -- [`command`](../command/README.md) - Command 实现 ICanSetArchitecture -- [`query`](../query/README.md) - Query 实现 ICanSetArchitecture -- [`controller`](../controller/README.md) - Controller 实现 IBelongToArchitecture -- [`system`](../system/README.md) - System 实现 IBelongToArchitecture -- [`model`](../model/README.md) - Model 实现 IBelongToArchitecture +- [`architecture`](../architecture/README.md) - 定义 IArchitectureContext 接口 +- [`command`](../command/README.md) - Command 继承 AbstractCommand (IContextAware) +- [`query`](../query/README.md) - Query 继承 AbstractQuery (IContextAware) +- [`controller`](../controller/README.md) - Controller 实现 ICanSendCommand 等接口 +- [`system`](../system/README.md) - System 继承 AbstractSystem (IContextAware) +- [`model`](../model/README.md) - Model 继承 AbstractModel (IContextAware) - [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法 \ No newline at end of file diff --git a/GFramework.Core/system/README.md b/GFramework.Core/system/README.md index 1cefe54..2f57d94 100644 --- a/GFramework.Core/system/README.md +++ b/GFramework.Core/system/README.md @@ -24,6 +24,8 @@ System 接口,定义了系统的基本行为。 ```csharp void Init(); // 系统初始化方法 +void Destroy(); // 系统销毁方法 +void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 ``` **继承的能力:** @@ -39,15 +41,18 @@ void Init(); // 系统初始化方法 ### [`AbstractSystem`](AbstractSystem.cs) -抽象 System 基类,提供了 System 的基础实现。 +抽象 System 基类,提供了 System 的基础实现。继承自 ContextAwareBase,具有上下文感知能力。 **使用方式:** ```csharp -public abstract class AbstractSystem : ISystem +public abstract class AbstractSystem : ContextAwareBase, ISystem { - void ISystem.Init() => OnInit(); + void ISystem.Init() => OnInit(); // 系统初始化,内部调用抽象方法 OnInit() + void ISystem.Destroy() => OnDestroy(); // 系统销毁,内部调用 OnDestroy() protected abstract void OnInit(); // 子类实现初始化逻辑 + protected virtual void OnDestroy(); // 子类可选择重写销毁逻辑 + public virtual void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件 } ``` @@ -99,6 +104,12 @@ public class CombatSystem : AbstractSystem { return Math.Max(1, attackPower - defense / 2); } + + protected override void OnDestroy() + { + // 清理资源 + base.OnDestroy(); + } } ``` diff --git a/GFramework.Core/utility/README.md b/GFramework.Core/utility/README.md index 5446cc6..bdcfeb8 100644 --- a/GFramework.Core/utility/README.md +++ b/GFramework.Core/utility/README.md @@ -7,17 +7,7 @@ Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如 ## 核心接口 -### [`ICanGetUtility`](ICanGetUtility.cs) - -标记接口,表示该类型可以获取 Utility。 - -**继承关系:** - -```csharp -public interface ICanGetUtility : IBelongToArchitecture -``` - -### [`IUtility`](IUtility.cs) +### IUtility Utility 标记接口,所有工具类都应实现此接口。 @@ -30,64 +20,98 @@ public interface IUtility } ``` +### IContextUtility + +上下文工具接口,扩展了IUtility接口,为需要感知架构上下文的工具类提供基础能力。 + +**接口定义:** + +```csharp +public interface IContextUtility : IUtility +{ + void Init(); // 初始化上下文工具 +} +``` + +## 核心类 + +### [`AbstractContextUtility`](AbstractContextUtility.cs) + +抽象上下文工具类,提供上下文相关的通用功能实现。继承自 ContextAwareBase 并实现 IContextUtility 接口。 + +**使用方式:** + +```csharp +public abstract class AbstractContextUtility : ContextAwareBase, IContextUtility +{ + protected ILogger Logger = null!; + + void IContextUtility.Init() + { + var name = GetType().Name; + Logger = LoggerFactoryResolver.Provider.CreateLogger(name); + Logger.Debug($"Initializing Context Utility: {name}"); + + OnInit(); // 子类实现初始化逻辑 + + Logger.Info($"Context Utility initialized: {name}"); + } + + protected abstract void OnInit(); // 子类实现具体的初始化逻辑 +} +``` + ## 基本使用 ### 1. 定义 Utility ```csharp -// 存储工具类 -public class StorageUtility : IUtility +// 存储工具类,继承自AbstractContextUtility +public class StorageUtility : AbstractContextUtility { private const string SavePath = "user://save_data.json"; - public void Save(T data) + protected override void OnInit() { - string json = Json.Stringify(data); - using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write); - file.StoreString(json); + Logger.Info("StorageUtility initialized"); } - public T Load() where T : new() + public void Save(T data) { - if (!FileAccess.FileExists(SavePath)) - return new T(); + string json = JsonSerializer.Serialize(data); + // 实际保存逻辑 + File.WriteAllText(SavePath, json); + } + + public T Load() + { + if (!File.Exists(SavePath)) + return default(T); - using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read); - string json = file.GetAsText(); - return Json.Parse(json); + string json = File.ReadAllText(SavePath); + return JsonSerializer.Deserialize(json); } public void Delete() { - if (FileAccess.FileExists(SavePath)) + if (File.Exists(SavePath)) { - DirAccess.RemoveAbsolute(SavePath); + File.Delete(SavePath); } } } -// 数学工具类 +// 数学工具类,作为普通Utility public class MathUtility : IUtility { public float Lerp(float a, float b, float t) { - return a + (b - a) * Mathf.Clamp(t, 0f, 1f); + return a + (b - a) * Math.Clamp(t, 0f, 1f); } - public Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, float t) + public bool IsInRange(float value, float min, float max) { - float u = 1 - t; - return u * u * p0 + 2 * u * t * p1 + t * t * p2; - } - - public bool IsInRange(Vector3 point, Vector3 center, float radius) - { - return point.DistanceTo(center) <= radius; - } - - public int RollDice(int sides) - { - return GD.RandRange(1, sides); + return value >= min && value <= max; } }