From 82713e34f044bb937b12bd81a840803cffb39f2d Mon Sep 17 00:00:00 2001 From: GwWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Fri, 12 Dec 2025 21:10:21 +0800 Subject: [PATCH] =?UTF-8?q?refactor(core):=20=E9=87=8D=E6=9E=84=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E5=92=8C=E5=91=BD=E4=BB=A4=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整了 Architecture 类中字段和方法的布局,提升可读性 - 优化了命令执行逻辑,明确区分有无返回值的命令处理 - 规范了接口和抽象类的注释格式,增强文档清晰度 - 统一了代码风格,对齐缩进与换行符使用 - 补充了事件系统中泛型事件类的功能实现 - 完善了 README 文档中的条目结构和内容表述 --- .../GFramework.Core.Godot.csproj | 7 +- .../component/DragDropComponent.cs | 181 ++++++++++++ .../extensions/NodeExtensions.cs | 99 +++---- .../extensions/UnRegisterExtension.cs | 4 +- GFramework.Core/README.md | 92 ++++-- GFramework.Core/architecture/Architecture.cs | 262 ++++++++++-------- GFramework.Core/architecture/IArchitecture.cs | 32 +-- GFramework.Core/architecture/README.md | 14 +- GFramework.Core/command/AbstractCommand.cs | 52 ++-- GFramework.Core/command/ICanSendCommand.cs | 6 +- GFramework.Core/command/ICommand.cs | 18 +- GFramework.Core/command/README.md | 5 + GFramework.Core/controller/IController.cs | 8 +- GFramework.Core/controller/README.md | 65 ++--- GFramework.Core/events/DefaultUnRegister.cs | 9 +- GFramework.Core/events/EasyEvent.cs | 20 +- GFramework.Core/events/EasyEventGeneric.cs | 146 ++++++---- GFramework.Core/events/EasyEvents.cs | 60 ++-- GFramework.Core/events/ICanRegisterEvent.cs | 8 +- GFramework.Core/events/ICanSendEvent.cs | 6 +- GFramework.Core/events/IEasyEvent.cs | 4 +- GFramework.Core/events/IUnRegister.cs | 6 +- GFramework.Core/events/IUnRegisterList.cs | 6 +- GFramework.Core/events/OrEvent.cs | 31 ++- GFramework.Core/events/README.md | 49 ++-- GFramework.Core/events/TypeEventSystem.cs | 20 +- GFramework.Core/events/UnRegisterList.cs | 24 +- .../extensions/CanGetExtensions.cs | 30 +- .../extensions/CanRegisterEventExtensions.cs | 18 +- .../extensions/CanSendExtensions.cs | 49 ++-- .../extensions/OrEventExtensions.cs | 9 +- GFramework.Core/extensions/README.md | 17 +- .../extensions/UnRegisterListExtension.cs | 18 +- GFramework.Core/ioc/IocContainer.cs | 13 +- GFramework.Core/ioc/README.md | 24 +- GFramework.Core/model/AbstractModel.cs | 29 +- GFramework.Core/model/ICanGetModel.cs | 6 +- GFramework.Core/model/IModel.cs | 6 +- GFramework.Core/model/README.md | 5 +- GFramework.Core/property/BindableProperty.cs | 96 ++++--- .../property/BindablePropertyUnRegister.cs | 11 +- GFramework.Core/property/IBindableProperty.cs | 10 +- .../property/IReadonlyBindableProperty.cs | 17 +- GFramework.Core/property/README.md | 2 + GFramework.Core/query/AbstractQuery.cs | 47 ++-- GFramework.Core/query/ICanSendQuery.cs | 6 +- GFramework.Core/query/IQuery.cs | 7 +- GFramework.Core/query/README.md | 9 +- GFramework.Core/rule/IBelongToArchitecture.cs | 9 +- GFramework.Core/rule/ICanSetArchitecture.cs | 6 +- GFramework.Core/rule/README.md | 4 + GFramework.Core/system/AbstractSystem.cs | 29 +- GFramework.Core/system/ICanGetSystem.cs | 6 +- GFramework.Core/system/ISystem.cs | 10 +- GFramework.Core/system/README.md | 6 + GFramework.Core/utility/ICanGetUtility.cs | 6 +- GFramework.Core/utility/IUtility.cs | 6 +- GFramework.Core/utility/README.md | 7 +- .../enums/EnumExtensionsAttribute.cs | 9 +- .../GFramework.Generator.csproj | 8 +- GFramework.Generator/README.md | 1 + .../enums/EnumExtensionsGenerator.cs | 193 +++++++------ GFramework.csproj | 38 +-- README.md | 2 + 64 files changed, 1195 insertions(+), 808 deletions(-) create mode 100644 GFramework.Core.Godot/component/DragDropComponent.cs diff --git a/GFramework.Core.Godot/GFramework.Core.Godot.csproj b/GFramework.Core.Godot/GFramework.Core.Godot.csproj index 9526647..26631d2 100644 --- a/GFramework.Core.Godot/GFramework.Core.Godot.csproj +++ b/GFramework.Core.Godot/GFramework.Core.Godot.csproj @@ -9,15 +9,16 @@ - + + - + - + diff --git a/GFramework.Core.Godot/component/DragDropComponent.cs b/GFramework.Core.Godot/component/DragDropComponent.cs new file mode 100644 index 0000000..c637820 --- /dev/null +++ b/GFramework.Core.Godot/component/DragDropComponent.cs @@ -0,0 +1,181 @@ +using GFramework.Core.architecture; +using GFramework.Core.controller; +using GFramework.Core.events; +using GFramework.Core.extensions; +using GFramework.Core.Godot.extensions; +using Godot; + +namespace GFramework.Core.Godot.component; + +/// +/// 拖拽组件类,用于处理节点的拖放逻辑。 +/// 实现了 IController 接口以支持架构通信,并通过信号通知拖拽事件的发生。 +/// +public abstract partial class DragDropComponent : Node, IController +{ + /// + /// 当拖拽被取消时触发的信号。 + /// + /// 拖拽起始位置。 + [Signal] + public delegate void DragCanceledEventHandler(Vector2 startingPosition); + + /// + /// 当拖拽开始时触发的信号。 + /// + [Signal] + public delegate void DragStartedEventHandler(); + + /// + /// 当拖拽结束并放置时触发的信号。 + /// + /// 拖拽起始位置。 + [Signal] + public delegate void DroppedEventHandler(Vector2 startingPosition); + + /// + /// 取消注册列表,用于管理需要在节点销毁时取消注册的对象 + /// + private readonly IUnRegisterList _unRegisterList = new UnRegisterList(); + + private bool _isDragging; + private Vector2 _offset = Vector2.Zero; + + private Vector2 _startingPosition; + + /// + /// 目标区域,通常是可交互的游戏对象(如单位或物品)所在的碰撞区域。 + /// + public required Area2D Target { get; set; } + + /// + /// 是否启用拖拽功能。若为 false,则忽略所有输入事件。 + /// + public bool Enable { get; set; } + + public string GroupName { get; set; } = "dragging"; + + public int ZIndexMax { get; set; } = 99; + public int ZIndexMin { get; set; } = 0; + + /// + /// 获取游戏架构实例 + /// + /// 返回游戏架构接口实例 + public abstract IArchitecture GetArchitecture(); + + /// + /// 节点准备就绪时的回调方法。 + /// 在节点添加到场景树后调用,绑定目标区域的输入事件处理器。 + /// + public override void _Ready() + { + Target = GetParent() as Area2D ?? throw new InvalidOperationException("Target must be an Area2D node."); + Target.InputEvent += OnTargetInputEvent; + } + + /// + /// 处理输入事件的方法 + /// + /// 输入事件对象 + public override void _Input(InputEvent @event) + { + // 处理取消拖拽操作:当正在拖拽且按下取消拖拽按键时,执行取消拖拽逻辑 + if (!_isDragging || Target.IsInvalidNode() || !@event.IsActionPressed("cancel_drag")) return; + CancelDragging(); + // 设置输入为处理,防止输入穿透 + this.SetInputAsHandled(); + } + + /// + /// 处理目标区域接收到的输入事件。 + /// 根据当前是否处于拖拽状态以及用户操作决定执行开始、取消或完成拖拽的动作。 + /// + /// 接收输入事件的视图端口节点。 + /// 发生的输入事件。 + /// 事件索引(未使用)。 + private void OnTargetInputEvent(Node viewport, InputEvent @event, long _) + { + if (!Enable) return; + + // 获取当前正在拖拽的对象 + var draggingObj = GetTree().GetFirstNodeInGroup(GroupName); + switch (_isDragging) + { + // 如果当前没有拖拽操作且已有其他对象正在拖拽,则直接返回 + case false when draggingObj is not null: + return; + // 处理开始拖拽操作:当未在拖拽状态且按下选择按键时,开始拖拽 + case false when @event.IsActionPressed("select"): + StartDragging(); + break; + // 处理放置操作:当正在拖拽且释放选择按键时,执行放置逻辑 + case true when @event.IsActionReleased("select"): + Drop(); + break; + } + } + + /// + /// 每帧更新逻辑,在拖拽过程中持续更新目标的位置。 + /// + /// 与上一帧的时间间隔(秒)。 + public override void _Process(double delta) + { + if (_isDragging && Target.IsValidNode()) Target.GlobalPosition = Target.GetGlobalMousePosition() + _offset; + } + + /// + /// 结束拖拽流程的基础方法。 + /// 清除拖拽标志位并将目标从拖拽组中移除,恢复其层级顺序。 + /// + private void EndDragging() + { + _isDragging = false; + Target.RemoveFromGroup(GroupName); + Target.ZIndex = ZIndexMin; + } + + /// + /// 执行取消拖拽的操作。 + /// 调用 EndDragging 并发出 DragCanceled 信号。 + /// + private void CancelDragging() + { + EndDragging(); + EmitSignalDragCanceled(_startingPosition); + } + + /// + /// 开始拖拽操作。 + /// 设置初始位置和偏移量,将目标加入拖拽组并提升显示层级,最后发出 DragStarted 信号。 + /// + private void StartDragging() + { + _isDragging = true; + _startingPosition = Target.GlobalPosition; + Target.AddToGroup(GroupName); + Target.ZIndex = ZIndexMax; + _offset = Target.GlobalPosition - Target.GetGlobalMousePosition(); + EmitSignalDragStarted(); + } + + /// + /// 完成一次拖拽操作。 + /// 调用 EndDragging 方法并发出 Dropped 信号。 + /// + private void Drop() + { + EndDragging(); + EmitSignalDropped(_startingPosition); + } + + /// + /// 节点退出场景树时的回调方法。 + /// 在节点从场景树移除前调用,用于清理资源。 + /// + public override void _ExitTree() + { + _unRegisterList.UnRegisterAll(); + } +} \ No newline at end of file diff --git a/GFramework.Core.Godot/extensions/NodeExtensions.cs b/GFramework.Core.Godot/extensions/NodeExtensions.cs index 5b5522d..d124e29 100644 --- a/GFramework.Core.Godot/extensions/NodeExtensions.cs +++ b/GFramework.Core.Godot/extensions/NodeExtensions.cs @@ -1,87 +1,64 @@ -using System; -using System.Threading.Tasks; -using Godot; +using Godot; namespace GFramework.Core.Godot.extensions; /// -/// 节点扩展方法类,提供对Godot节点的扩展功能 +/// 节点扩展方法类,提供对Godot节点的扩展功能 /// public static class NodeExtensions { /// - /// 安全地将节点加入删除队列,在下一帧开始时释放节点资源 + /// 安全地将节点加入删除队列,在下一帧开始时释放节点资源 /// /// 要释放的节点实例 public static void QueueFreeX(this Node? node) { // 检查节点是否为空 - if (node is null) - { - return; - } + if (node is null) return; // 检查节点实例是否有效 - if (!GodotObject.IsInstanceValid(node)) - { - return; - } + if (!GodotObject.IsInstanceValid(node)) return; // 检查节点是否已经加入删除队列 - if (node.IsQueuedForDeletion()) - { - return; - } + if (node.IsQueuedForDeletion()) return; // 延迟调用QueueFree方法,避免在当前帧中直接删除节点 node.CallDeferred(Node.MethodName.QueueFree); } /// - /// 立即释放节点资源,不等待下一帧 + /// 立即释放节点资源,不等待下一帧 /// /// 要立即释放的节点实例 public static void FreeX(this Node? node) { // 检查节点是否为空 - if (node is null) - { - return; - } + if (node is null) return; // 检查节点实例是否有效 - if (!GodotObject.IsInstanceValid(node)) - { - return; - } + if (!GodotObject.IsInstanceValid(node)) return; // 检查节点是否已经加入删除队列 - if (node.IsQueuedForDeletion()) - { - return; - } + if (node.IsQueuedForDeletion()) return; // 立即释放节点资源 node.Free(); } /// - /// 如果节点尚未进入场景树,则等待 ready 信号。 - /// 如果已经在场景树中,则立刻返回。 + /// 如果节点尚未进入场景树,则等待 ready 信号。 + /// 如果已经在场景树中,则立刻返回。 /// public static async Task WaitUntilReady(this Node node) { - if (!node.IsInsideTree()) - { - await node.ToSignal(node, Node.SignalName.Ready); - } + if (!node.IsInsideTree()) await node.ToSignal(node, Node.SignalName.Ready); } /// - /// 检查节点是否有效: - /// 1. 非 null - /// 2. Godot 实例仍然存在(未被释放) - /// 3. 已经加入 SceneTree + /// 检查节点是否有效: + /// 1. 非 null + /// 2. Godot 实例仍然存在(未被释放) + /// 3. 已经加入 SceneTree /// public static bool IsValidNode(this Node? node) { @@ -91,12 +68,11 @@ public static class NodeExtensions } /// - /// 检查节点是否无效: - /// 1. 为 null,或者 - /// 2. Godot 实例已被释放,或者 - /// 3. 尚未加入 SceneTree - /// - /// 返回 true 表示该节点不可用。 + /// 检查节点是否无效: + /// 1. 为 null,或者 + /// 2. Godot 实例已被释放,或者 + /// 3. 尚未加入 SceneTree + /// 返回 true 表示该节点不可用。 /// public static bool IsInvalidNode(this Node? node) { @@ -106,7 +82,7 @@ public static class NodeExtensions } /// - /// 将当前节点的输入事件标记为已处理,防止事件继续向父节点传播。 + /// 将当前节点的输入事件标记为已处理,防止事件继续向父节点传播。 /// /// 要处理输入事件的节点实例 public static void SetInputAsHandled(this Node node) @@ -116,7 +92,7 @@ public static class NodeExtensions } /// - /// 设置节点所在场景树的暂停状态 + /// 设置节点所在场景树的暂停状态 /// /// 要操作的节点对象 /// 暂停状态标识,默认为true表示暂停,false表示恢复运行 @@ -127,7 +103,7 @@ public static class NodeExtensions } /// - /// 查找指定名称的子节点并将其转换为指定类型 + /// 查找指定名称的子节点并将其转换为指定类型 /// /// 要转换到的目标节点类型 /// 要在其子节点中进行查找的父节点 @@ -137,12 +113,12 @@ public static class NodeExtensions public static T? FindChildX(this Node node, string name, bool recursive = true) where T : Node { - var child = node.FindChild(name, recursive, owned: false); + var child = node.FindChild(name, recursive, false); return child as T; } /// - /// 获取指定路径的节点,如果不存在则创建一个新的节点 + /// 获取指定路径的节点,如果不存在则创建一个新的节点 /// /// 节点类型,必须继承自Node且具有无参构造函数 /// 父节点 @@ -163,7 +139,7 @@ public static class NodeExtensions } /// - /// 异步添加子节点并等待其准备就绪 + /// 异步添加子节点并等待其准备就绪 /// /// 父节点 /// 要添加的子节点 @@ -175,7 +151,7 @@ public static class NodeExtensions } /// - /// 获取父节点并将其转换为指定类型 + /// 获取父节点并将其转换为指定类型 /// /// 要转换到的目标节点类型 /// 当前节点 @@ -184,8 +160,9 @@ public static class NodeExtensions { return node.GetParent() as T; } + /// - /// 获取场景树的根节点的第一个子节点 + /// 获取场景树的根节点的第一个子节点 /// /// 扩展方法的目标节点 /// 根节点的第一个子节点 @@ -195,7 +172,7 @@ public static class NodeExtensions } /// - /// 遍历节点的所有子节点,并对指定类型的子节点执行特定操作 + /// 遍历节点的所有子节点,并对指定类型的子节点执行特定操作 /// /// 要筛选的节点类型 /// 扩展方法的目标节点 @@ -208,7 +185,7 @@ public static class NodeExtensions } /// - /// 禁用节点所在场景树的输入处理功能 + /// 禁用节点所在场景树的输入处理功能 /// /// 扩展方法的目标节点 public static void DisableInput(this Node node) @@ -219,7 +196,7 @@ public static class NodeExtensions } /// - /// 启用节点所在场景树的输入处理功能 + /// 启用节点所在场景树的输入处理功能 /// /// 扩展方法的目标节点 public static void EnableInput(this Node node) @@ -230,7 +207,7 @@ public static class NodeExtensions } /// - /// 打印节点的路径信息到控制台 + /// 打印节点的路径信息到控制台 /// /// 扩展方法的目标节点 public static void LogNodePath(this Node node) @@ -239,7 +216,7 @@ public static class NodeExtensions } /// - /// 以树形结构递归打印节点及其所有子节点的名称 + /// 以树形结构递归打印节点及其所有子节点的名称 /// /// 扩展方法的目标节点 /// 缩进字符串,用于显示层级关系 @@ -253,7 +230,7 @@ public static class NodeExtensions } /// - /// 安全地延迟调用指定方法,确保节点有效后再执行 + /// 安全地延迟调用指定方法,确保节点有效后再执行 /// /// 扩展方法的目标节点 /// 要延迟调用的方法名 @@ -263,6 +240,4 @@ public static class NodeExtensions if (node != null && GodotObject.IsInstanceValid(node)) node.CallDeferred(method); } - - } \ No newline at end of file diff --git a/GFramework.Core.Godot/extensions/UnRegisterExtension.cs b/GFramework.Core.Godot/extensions/UnRegisterExtension.cs index cc6e2a8..7d3d580 100644 --- a/GFramework.Core.Godot/extensions/UnRegisterExtension.cs +++ b/GFramework.Core.Godot/extensions/UnRegisterExtension.cs @@ -4,12 +4,12 @@ using Godot; namespace GFramework.Core.Godot.extensions; /// -/// 提供取消注册扩展方法的静态类 +/// 提供取消注册扩展方法的静态类 /// public static class UnRegisterExtension { /// - /// 当节点退出场景树时自动取消注册监听器 + /// 当节点退出场景树时自动取消注册监听器 /// /// 需要在节点退出时被取消注册的监听器接口实例 /// Godot节点对象,当该节点退出场景树时触发取消注册操作 diff --git a/GFramework.Core/README.md b/GFramework.Core/README.md index c3cf1be..5d1ad2b 100644 --- a/GFramework.Core/README.md +++ b/GFramework.Core/README.md @@ -102,6 +102,7 @@ Event ──┘ ### 为什么需要这个框架? 在传统的 Godot 开发中,我们经常遇到这些问题: + - 💔 **代码耦合严重**:UI 直接访问游戏逻辑,逻辑直接操作 UI - 🔄 **难以维护**:修改一个功能需要改动多个文件 - 🐛 **难以测试**:业务逻辑和 UI 混在一起无法独立测试 @@ -133,6 +134,7 @@ public class GameArchitecture : Architecture ``` **优势**: + - ✅ **单例模式**:通过 `GameArchitecture.Interface` 全局访问 - ✅ **自动初始化**:注册时自动调用组件的 Init 方法 - ✅ **依赖注入**:组件自动获得架构引用,无需手动传递 @@ -173,12 +175,14 @@ public class PlayerModel : AbstractModel ``` **优势**: + - ✅ **数据响应式**:BindableProperty 让数据变化自动通知 UI - ✅ **职责单一**:只存储数据,不包含复杂业务逻辑 - ✅ **易于测试**:可以独立测试数据逻辑 - ✅ **数据持久化**:可以轻松序列化整个 Model 进行存档 **为什么不在 Model 中写业务逻辑?** + - 保持 Model 简单纯粹,业务逻辑应该在 System 中处理 - Model 应该是"被动"的,等待其他组件修改它 @@ -209,12 +213,14 @@ public class CombatSystem : AbstractSystem ``` **优势**: + - ✅ **事件驱动**:通过事件解耦,不同 System 之间松耦合 - ✅ **可组合**:多个 System 协同工作,每个专注自己的领域 - ✅ **易于扩展**:新增功能只需添加新的 System 和事件监听 - ✅ **独立测试**:可以模拟事件来测试 System 的逻辑 **System 与 Model 的关系**: + - System 读取和修改 Model 的数据 - System 不应该存储重要的游戏状态(状态应在 Model 中) - System 可以存储临时的计算结果或缓存 @@ -251,18 +257,21 @@ public partial class PlayerController : Node, IController ``` **优势**: + - ✅ **自动更新 UI**:通过 BindableProperty,数据变化自动反映到界面 - ✅ **生命周期管理**:自动注销监听,避免内存泄漏 - ✅ **分离关注点**:UI 逻辑和业务逻辑完全分离 - ✅ **易于修改 UI**:改变 UI 不影响业务逻辑 **为什么使用 RegisterWithInitValue?** + - 注册监听时立即获得当前值,避免 UI 显示空白 - 后续数据变化会自动触发更新 ### 完成!现在你有了一个完整的架构 这 4 步完成后,你就拥有了: + - 📦 **清晰的数据层**(Model) - 🧠 **独立的业务逻辑**(System) - 🎮 **灵活的控制层**(Controller) @@ -277,20 +286,20 @@ public partial class PlayerController : Node, IController ## 包说明 -| 包名 | 职责 | 文档 | -|-----|------|------| +| 包名 | 职责 | 文档 | +|------------------|-----------------|---------------------------------| | **architecture** | 架构核心,管理所有组件生命周期 | [📖 查看](architecture/README.md) | -| **model** | 数据模型层,存储游戏状态 | [📖 查看](model/README.md) | -| **system** | 业务逻辑层,处理游戏系统 | [📖 查看](system/README.md) | -| **controller** | 控制器层,连接视图和逻辑 | [📖 查看](controller/README.md) | -| **utility** | 工具类层,提供无状态工具 | [📖 查看](utility/README.md) | -| **command** | 命令模式,封装写操作 | [📖 查看](command/README.md) | -| **query** | 查询模式,封装读操作 | [📖 查看](query/README.md) | -| **events** | 事件系统,组件间通信 | [📖 查看](events/README.md) | -| **property** | 可绑定属性,响应式编程 | [📖 查看](property/README.md) | -| **ioc** | IoC 容器,依赖注入 | [📖 查看](ioc/README.md) | -| **rule** | 规则接口,定义组件约束 | [📖 查看](rule/README.md) | -| **extensions** | 扩展方法,简化 API 调用 | [📖 查看](extensions/README.md) | +| **model** | 数据模型层,存储游戏状态 | [📖 查看](model/README.md) | +| **system** | 业务逻辑层,处理游戏系统 | [📖 查看](system/README.md) | +| **controller** | 控制器层,连接视图和逻辑 | [📖 查看](controller/README.md) | +| **utility** | 工具类层,提供无状态工具 | [📖 查看](utility/README.md) | +| **command** | 命令模式,封装写操作 | [📖 查看](command/README.md) | +| **query** | 查询模式,封装读操作 | [📖 查看](query/README.md) | +| **events** | 事件系统,组件间通信 | [📖 查看](events/README.md) | +| **property** | 可绑定属性,响应式编程 | [📖 查看](property/README.md) | +| **ioc** | IoC 容器,依赖注入 | [📖 查看](ioc/README.md) | +| **rule** | 规则接口,定义组件约束 | [📖 查看](rule/README.md) | +| **extensions** | 扩展方法,简化 API 调用 | [📖 查看](extensions/README.md) | ## 组件联动 @@ -346,6 +355,7 @@ Controller: Health.RegisterWithInitValue(hp => UpdateUI(hp)) #### ✅ 好的实践 vs ❌ 坏的实践 **Model 层**: + ```csharp // ✅ 好:只存储数据 public class PlayerModel : AbstractModel @@ -367,6 +377,7 @@ public class PlayerModel : AbstractModel ``` **System 层**: + ```csharp // ✅ 好:处理业务逻辑 public class CombatSystem : AbstractSystem @@ -398,6 +409,7 @@ public class CombatSystem : AbstractSystem ``` **Controller 层**: + ```csharp // ✅ 好:只处理 UI 和用户输入 public partial class AttackButton : Button, IController @@ -430,12 +442,12 @@ public partial class AttackButton : Button, IController 不同的通信方式适用于不同场景,选对方式能让代码更优雅。 -| 通信方式 | 使用场景 | 示例 | 优势 | -|---------|---------|------|------| -| **Command** | 用户操作、修改状态 | 购买物品、攻击敌人 | 可撤销、可记录 | -| **Query** | 查询数据、检查条件 | 查询金币数量、检查是否可购买 | 明确只读意图 | -| **Event** | 通知其他组件 | 玩家死亡、物品拾取 | 松耦合、可扩展 | -| **BindableProperty** | 数据→UI 自动同步 | 生命值变化更新血条 | 自动化、不会遗漏 | +| 通信方式 | 使用场景 | 示例 | 优势 | +|----------------------|------------|----------------|----------| +| **Command** | 用户操作、修改状态 | 购买物品、攻击敌人 | 可撤销、可记录 | +| **Query** | 查询数据、检查条件 | 查询金币数量、检查是否可购买 | 明确只读意图 | +| **Event** | 通知其他组件 | 玩家死亡、物品拾取 | 松耦合、可扩展 | +| **BindableProperty** | 数据→UI 自动同步 | 生命值变化更新血条 | 自动化、不会遗漏 | **实战示例:购物系统** @@ -527,6 +539,7 @@ public partial class GoldDisplay : Label, IController **为什么需要注销?** 忘记注销监听器会导致: + - 💥 **内存泄漏**:对象无法被 GC 回收 - 🐛 **逻辑错误**:已销毁的对象仍在响应事件 - 📉 **性能下降**:无用的监听器消耗资源 @@ -726,6 +739,7 @@ public class CombatSystemTests ``` **优势**: + - 不需要启动游戏就能测试逻辑 - 可以快速验证各种边界情况 - 易于进行回归测试 @@ -840,6 +854,7 @@ protected override void Init() ``` **优势总结**: + - 数据驱动,易于存档 - 事件解耦,易于扩展 - UI 自动化,不会遗漏更新 @@ -854,12 +869,14 @@ protected override void Init() **理念**:每个类只负责一件事,只有一个改变的理由。 **在框架中的体现**: + - **Model**:只负责存储数据 - **System**:只负责处理业务逻辑 - **Controller**:只负责 UI 交互 - **Utility**:只负责提供工具方法 **好处**: + - 代码更容易理解和维护 - 修改一个功能不会影响其他功能 - 单元测试更简单 @@ -869,11 +886,13 @@ protected override void Init() **理念**:对扩展开放,对修改封闭。 **在框架中的实现**: + - 通过**事件系统**添加新功能,无需修改现有代码 - 新的 System 可以监听现有事件,插入自己的逻辑 - 符合"插件式"的架构设计 **示例**: + ```csharp // 现有:战斗系统 public class CombatSystem : AbstractSystem @@ -908,11 +927,13 @@ public class CriticalSystem : AbstractSystem **理念**:依赖抽象(接口)而非具体实现。 **在框架中的应用**: + - 所有组件通过接口交互 - 通过 IoC 容器注入依赖 - 易于替换实现和编写测试 **好处**: + - 降低耦合度 - 易于进行单元测试(可以 mock) - 可以灵活替换实现 @@ -922,6 +943,7 @@ public class CriticalSystem : AbstractSystem **理念**:使用多个专门的接口,而不是一个庞大的接口。 **在框架中的体现**: + ```csharp // 小而专注的接口 public interface ICanGetModel : IBelongToArchitecture { } @@ -936,6 +958,7 @@ public interface IController : ``` **优势**: + - 类只需要实现它真正需要的方法 - 接口更容易理解和使用 - 减少不必要的依赖 @@ -945,11 +968,13 @@ public interface IController : **理念**:通过接口组合获得能力,而不是通过继承。 **传统继承的问题**: + - 继承层次深,难以理解 - 子类与父类紧密耦合 - 难以灵活组合能力 **框架的解决方案**: + ```csharp // ✅ 通过接口组合能力 public interface IController : @@ -966,39 +991,44 @@ public class BattleController : GameController { } ### 框架核心设计模式 🎨 -| 设计模式 | 应用位置 | 解决的问题 | 带来的好处 | -|---------|---------|-----------|-----------| -| **单例模式** | Architecture | 全局唯一的架构实例 | 统一的组件管理 | -| **工厂模式** | IoC 容器 | 组件的创建和管理 | 解耦创建逻辑 | -| **观察者模式** | Event 系统 | 组件间的通信 | 松耦合通信 | -| **命令模式** | Command | 封装操作请求 | 支持撤销重做 | -| **策略模式** | System | 不同的业务逻辑 | 易于切换策略 | -| **依赖注入** | 整体架构 | 组件间的依赖 | 自动管理依赖 | -| **模板方法** | Abstract 类 | 定义算法骨架 | 统一流程规范 | +| 设计模式 | 应用位置 | 解决的问题 | 带来的好处 | +|-----------|--------------|-----------|---------| +| **单例模式** | Architecture | 全局唯一的架构实例 | 统一的组件管理 | +| **工厂模式** | IoC 容器 | 组件的创建和管理 | 解耦创建逻辑 | +| **观察者模式** | Event 系统 | 组件间的通信 | 松耦合通信 | +| **命令模式** | Command | 封装操作请求 | 支持撤销重做 | +| **策略模式** | System | 不同的业务逻辑 | 易于切换策略 | +| **依赖注入** | 整体架构 | 组件间的依赖 | 自动管理依赖 | +| **模板方法** | Abstract 类 | 定义算法骨架 | 统一流程规范 | ### 为什么这样设计?🤔 #### 1. 降低学习成本 + - 遵循业界标准模式和原则 - 有经验的开发者能快速上手 - 清晰的分层易于理解 #### 2. 提高代码质量 + - 强制分层,避免意大利面代码 - 职责明确,减少 bug - 易于 Code Review #### 3. 便于团队协作 + - 清晰的职责划分,减少冲突 - 统一的代码风格 - 新人容易融入项目 #### 4. 易于维护扩展 + - 符合 SOLID 原则 - 通过事件添加功能无需修改现有代码 - 模块化设计,易于替换 #### 5. 支持单元测试 + - 依赖注入让 mock 变得简单 - 接口设计便于测试 - 业务逻辑与 UI 分离,可独立测试 @@ -1006,16 +1036,19 @@ public class BattleController : GameController { } ### 架构演进建议 🚀 #### 小型项目(< 5000 行代码) + - 使用基础的 MVC 分层 - 适度使用 Command 和 Query - 事件系统用于关键通知 #### 中型项目(5000-20000 行) + - 完整使用框架所有特性 - 细分 System 的职责 - 引入更多 Utility 复用代码 #### 大型项目(> 20000 行) + - 考虑按功能模块拆分多个 Architecture - 使用事件总线连接不同模块 - 引入领域驱动设计(DDD)概念 @@ -1023,6 +1056,7 @@ public class BattleController : GameController { } ### 常见反模式 ⚠️ #### 反模式 1:上帝类(God Class) + ```csharp // ❌ 一个类做所有事情 public class GameManager @@ -1042,6 +1076,7 @@ public class SaveSystem { /* 只负责存档 */ } ``` #### 反模式 2:循环依赖 + ```csharp // ❌ A 依赖 B,B 又依赖 A public class SystemA @@ -1071,6 +1106,7 @@ public class SystemB ``` #### 反模式 3:过度设计 + ```csharp // ❌ 简单功能过度抽象 public interface IClickable { } diff --git a/GFramework.Core/architecture/Architecture.cs b/GFramework.Core/architecture/Architecture.cs index 9ca82e4..2abab1a 100644 --- a/GFramework.Core/architecture/Architecture.cs +++ b/GFramework.Core/architecture/Architecture.cs @@ -9,43 +9,23 @@ using GFramework.Core.utility; namespace GFramework.Core.architecture; /// -/// 架构基类,提供系统、模型、工具等组件的注册与管理功能。 -/// 使用单例模式确保全局唯一实例,并支持命令、查询和事件机制。 +/// 架构基类,提供系统、模型、工具等组件的注册与管理功能。 +/// 使用单例模式确保全局唯一实例,并支持命令、查询和事件机制。 /// /// 派生类类型,用于实现单例 public abstract class Architecture : IArchitecture where T : Architecture, new() { /// - /// 标记架构是否已初始化完成 - /// - private bool _mInited; - - /// - /// 存储尚未初始化的系统集合,在初始化阶段统一调用Init方法 - /// - private readonly HashSet _mSystems = []; - - /// - /// 存储尚未初始化的模型集合,在初始化阶段统一调用Init方法 - /// - private readonly HashSet _mModels = []; - - /// - /// 注册补丁委托,允许在架构创建后执行额外逻辑 - /// - public static Action OnRegisterPatch { get; set; } = _ => { }; - - /// - /// 静态只读字段,用于延迟初始化架构实例 - /// 使用Lazy确保线程安全的单例模式实现 + /// 静态只读字段,用于延迟初始化架构实例 + /// 使用Lazy确保线程安全的单例模式实现 /// /// - /// 初始化过程包括: - /// 1. 创建T类型的实例 - /// 2. 调用用户自定义的Init方法 - /// 3. 执行注册的补丁逻辑 - /// 4. 初始化所有已注册的模型和系统 - /// 5. 清理临时集合并标记初始化完成 + /// 初始化过程包括: + /// 1. 创建T类型的实例 + /// 2. 调用用户自定义的Init方法 + /// 3. 执行注册的补丁逻辑 + /// 4. 初始化所有已注册的模型和系统 + /// 5. 清理临时集合并标记初始化完成 /// /// T类型的架构实例 private static readonly Lazy MArchitectureLazy = new(() => @@ -58,18 +38,12 @@ public abstract class Architecture : IArchitecture where T : Architecture, OnRegisterPatch?.Invoke(arch); // 初始化所有已注册但尚未初始化的模型 - foreach (var model in arch._mModels) - { - model.Init(); - } + foreach (var model in arch._mModels) model.Init(); arch._mModels.Clear(); // 初始化所有已注册但尚未初始化的系统 - foreach (var system in arch._mSystems) - { - system.Init(); - } + foreach (var system in arch._mSystems) system.Init(); arch._mSystems.Clear(); @@ -78,33 +52,52 @@ public abstract class Architecture : IArchitecture where T : Architecture, }, LazyThreadSafetyMode.ExecutionAndPublication); /// - /// 获取架构实例的受保护静态属性 - /// 通过Lazy初始化确保只创建一个实例 + /// 控制反转容器,用于存储和获取各种服务(如系统、模型、工具) + /// + private readonly IocContainer _mContainer = new(); + + /// + /// 存储尚未初始化的模型集合,在初始化阶段统一调用Init方法 + /// + private readonly HashSet _mModels = []; + + /// + /// 存储尚未初始化的系统集合,在初始化阶段统一调用Init方法 + /// + private readonly HashSet _mSystems = []; + + /// + /// 类型化事件系统,负责事件的发布与订阅管理 + /// + private readonly TypeEventSystem _mTypeEventSystem = new(); + + /// + /// 标记架构是否已初始化完成 + /// + private bool _mInited; + + /// + /// 注册补丁委托,允许在架构创建后执行额外逻辑 + /// + public static Action OnRegisterPatch { get; set; } = _ => { }; + + /// + /// 获取架构实例的受保护静态属性 + /// 通过Lazy初始化确保只创建一个实例 /// /// T类型的架构实例 protected static T MArchitecture => MArchitectureLazy.Value; /// - /// 获取架构实例的公共静态属性,以接口形式暴露 - /// 提供对外访问架构功能的标准接口 + /// 获取架构实例的公共静态属性,以接口形式暴露 + /// 提供对外访问架构功能的标准接口 /// /// IArchitecture接口类型的架构实例 public static IArchitecture Interface => MArchitectureLazy.Value; - /// - /// 抽象初始化方法,由子类重写以进行自定义初始化操作 - /// - protected abstract void Init(); - - /// - /// 控制反转容器,用于存储和获取各种服务(如系统、模型、工具) - /// - private readonly IocContainer _mContainer = new(); - - /// - /// 注册一个系统到架构中。 - /// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该系统。 + /// 注册一个系统到架构中。 + /// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该系统。 /// /// 要注册的系统类型 /// 要注册的系统实例 @@ -114,18 +107,14 @@ public abstract class Architecture : IArchitecture where T : Architecture, _mContainer.Register(system); if (!_mInited) - { _mSystems.Add(system); - } else - { system.Init(); - } } /// - /// 注册一个模型到架构中。 - /// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该模型。 + /// 注册一个模型到架构中。 + /// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该模型。 /// /// 要注册的模型类型 /// 要注册的模型实例 @@ -135,62 +124,132 @@ public abstract class Architecture : IArchitecture where T : Architecture, _mContainer.Register(model); if (!_mInited) - { _mModels.Add(model); - } else - { model.Init(); - } } /// - /// 注册一个工具到架构中。 - /// 工具不会被延迟初始化,直接放入IOC容器供后续使用。 + /// 注册一个工具到架构中。 + /// 工具不会被延迟初始化,直接放入IOC容器供后续使用。 /// /// 要注册的工具类型 /// 要注册的工具实例 - public void RegisterUtility(TUtility utility) where TUtility : IUtility => + public void RegisterUtility(TUtility utility) where TUtility : IUtility + { _mContainer.Register(utility); + } /// - /// 从IOC容器中获取指定类型的系统实例 + /// 从IOC容器中获取指定类型的系统实例 /// /// 目标系统类型 /// 对应的系统实例 - public TSystem GetSystem() where TSystem : class, ISystem => _mContainer.Get(); + public TSystem GetSystem() where TSystem : class, ISystem + { + return _mContainer.Get(); + } /// - /// 从IOC容器中获取指定类型的模型实例 + /// 从IOC容器中获取指定类型的模型实例 /// /// 目标模型类型 /// 对应的模型实例 - public TModel GetModel() where TModel : class, IModel => _mContainer.Get(); + public TModel GetModel() where TModel : class, IModel + { + return _mContainer.Get(); + } /// - /// 从IOC容器中获取指定类型的工具实例 + /// 从IOC容器中获取指定类型的工具实例 /// /// 目标工具类型 /// 对应的工具实例 - public TUtility GetUtility() where TUtility : class, IUtility => _mContainer.Get(); + public TUtility GetUtility() where TUtility : class, IUtility + { + return _mContainer.Get(); + } /// - /// 发送一个带返回结果的命令请求 + /// 发送一个带返回结果的命令请求 /// /// 命令执行后的返回值类型 /// 要发送的命令对象 /// 命令执行的结果 - public TResult SendCommand(ICommand command) => ExecuteCommand(command); + public TResult SendCommand(ICommand command) + { + return ExecuteCommand(command); + } /// - /// 发送一个无返回结果的命令请求 + /// 发送一个无返回结果的命令请求 /// /// 命令的具体类型 /// 要发送的命令对象 - public void SendCommand(TCommand command) where TCommand : ICommand => ExecuteCommand(command); + public void SendCommand(TCommand command) where TCommand : ICommand + { + ExecuteCommand(command); + } /// - /// 执行一个带返回结果的命令 + /// 发起一次查询请求并获得其结果 + /// + /// 查询结果的数据类型 + /// 要发起的查询对象 + /// 查询得到的结果数据 + public TResult SendQuery(IQuery query) + { + return DoQuery(query); + } + + /// + /// 发布一个默认构造的新事件对象 + /// + /// 事件类型 + public void SendEvent() where TEvent : new() + { + _mTypeEventSystem.Send(); + } + + /// + /// 发布一个具体的事件对象 + /// + /// 事件类型 + /// 要发布的事件实例 + public void SendEvent(TEvent e) + { + _mTypeEventSystem.Send(e); + } + + /// + /// 订阅某个特定类型的事件 + /// + /// 事件类型 + /// 当事件发生时触发的动作 + /// 可用于取消订阅的对象 + public IUnRegister RegisterEvent(Action onEvent) + { + return _mTypeEventSystem.Register(onEvent); + } + + /// + /// 取消对某类型事件的监听 + /// + /// 事件类型 + /// 之前绑定的事件处理器 + public void UnRegisterEvent(Action onEvent) + { + _mTypeEventSystem.UnRegister(onEvent); + } + + + /// + /// 抽象初始化方法,由子类重写以进行自定义初始化操作 + /// + protected abstract void Init(); + + /// + /// 执行一个带返回结果的命令 /// /// 命令执行后的返回值类型 /// 要执行的命令对象 @@ -202,7 +261,7 @@ public abstract class Architecture : IArchitecture where T : Architecture, } /// - /// 执行一个无返回结果的命令 + /// 执行一个无返回结果的命令 /// /// 要执行的命令对象 protected virtual void ExecuteCommand(ICommand command) @@ -212,15 +271,7 @@ public abstract class Architecture : IArchitecture where T : Architecture, } /// - /// 发起一次查询请求并获得其结果 - /// - /// 查询结果的数据类型 - /// 要发起的查询对象 - /// 查询得到的结果数据 - public TResult SendQuery(IQuery query) => DoQuery(query); - - /// - /// 实际执行查询逻辑的方法 + /// 实际执行查询逻辑的方法 /// /// 查询结果的数据类型 /// 要处理的查询对象 @@ -230,37 +281,4 @@ public abstract class Architecture : IArchitecture where T : Architecture, query.SetArchitecture(this); return query.Do(); } - - /// - /// 类型化事件系统,负责事件的发布与订阅管理 - /// - private readonly TypeEventSystem _mTypeEventSystem = new(); - - /// - /// 发布一个默认构造的新事件对象 - /// - /// 事件类型 - public void SendEvent() where TEvent : new() => _mTypeEventSystem.Send(); - - /// - /// 发布一个具体的事件对象 - /// - /// 事件类型 - /// 要发布的事件实例 - public void SendEvent(TEvent e) => _mTypeEventSystem.Send(e); - - /// - /// 订阅某个特定类型的事件 - /// - /// 事件类型 - /// 当事件发生时触发的动作 - /// 可用于取消订阅的对象 - public IUnRegister RegisterEvent(Action onEvent) => _mTypeEventSystem.Register(onEvent); - - /// - /// 取消对某类型事件的监听 - /// - /// 事件类型 - /// 之前绑定的事件处理器 - public void UnRegisterEvent(Action onEvent) => _mTypeEventSystem.UnRegister(onEvent); } \ No newline at end of file diff --git a/GFramework.Core/architecture/IArchitecture.cs b/GFramework.Core/architecture/IArchitecture.cs index 642d677..dd5c54d 100644 --- a/GFramework.Core/architecture/IArchitecture.cs +++ b/GFramework.Core/architecture/IArchitecture.cs @@ -8,62 +8,62 @@ using GFramework.Core.utility; namespace GFramework.Core.architecture; /// -/// 架构接口,定义了应用程序架构的核心功能,包括系统、模型、工具的注册和获取, -/// 以及命令、查询、事件的发送和处理机制 +/// 架构接口,定义了应用程序架构的核心功能,包括系统、模型、工具的注册和获取, +/// 以及命令、查询、事件的发送和处理机制 /// public interface IArchitecture { /// - /// 注册系统实例到架构中 + /// 注册系统实例到架构中 /// /// 系统类型,必须实现ISystem接口 /// 要注册的系统实例 void RegisterSystem(T system) where T : ISystem; /// - /// 注册模型实例到架构中 + /// 注册模型实例到架构中 /// /// 模型类型,必须实现IModel接口 /// 要注册的模型实例 void RegisterModel(T model) where T : IModel; /// - /// 注册工具实例到架构中 + /// 注册工具实例到架构中 /// /// 工具类型,必须实现IUtility接口 /// 要注册的工具实例 void RegisterUtility(T utility) where T : IUtility; /// - /// 从架构中获取指定类型的系统实例 + /// 从架构中获取指定类型的系统实例 /// /// 系统类型,必须是class且实现ISystem接口 /// 指定类型的系统实例 T GetSystem() where T : class, ISystem; /// - /// 从架构中获取指定类型的模型实例 + /// 从架构中获取指定类型的模型实例 /// /// 模型类型,必须是class且实现IModel接口 /// 指定类型的模型实例 T GetModel() where T : class, IModel; /// - /// 从架构中获取指定类型的工具实例 + /// 从架构中获取指定类型的工具实例 /// /// 工具类型,必须是class且实现IUtility接口 /// 指定类型的工具实例 T GetUtility() where T : class, IUtility; /// - /// 发送并执行指定的命令 + /// 发送并执行指定的命令 /// /// 命令类型,必须实现ICommand接口 /// 要执行的命令实例 void SendCommand(T command) where T : ICommand; /// - /// 发送并执行带有返回值的命令 + /// 发送并执行带有返回值的命令 /// /// 命令执行结果的类型 /// 要执行的命令实例 @@ -71,7 +71,7 @@ public interface IArchitecture TResult SendCommand(ICommand command); /// - /// 发送并执行查询操作 + /// 发送并执行查询操作 /// /// 查询结果的类型 /// 要执行的查询实例 @@ -79,20 +79,20 @@ public interface IArchitecture TResult SendQuery(IQuery query); /// - /// 发送无参事件 + /// 发送无参事件 /// /// 事件类型,必须具有无参构造函数 void SendEvent() where T : new(); /// - /// 发送指定的事件实例 + /// 发送指定的事件实例 /// /// 事件类型 /// 要发送的事件实例 void SendEvent(T e); /// - /// 注册事件监听器 + /// 注册事件监听器 /// /// 事件类型 /// 事件触发时的回调方法 @@ -100,9 +100,9 @@ public interface IArchitecture IUnRegister RegisterEvent(Action onEvent); /// - /// 取消注册事件监听器 + /// 取消注册事件监听器 /// /// 事件类型 /// 要取消注册的事件回调方法 void UnRegisterEvent(Action onEvent); -} +} \ No newline at end of file diff --git a/GFramework.Core/architecture/README.md b/GFramework.Core/architecture/README.md index 939aa8f..96a75b4 100644 --- a/GFramework.Core/architecture/README.md +++ b/GFramework.Core/architecture/README.md @@ -11,6 +11,7 @@ Architecture 包是整个框架的核心,提供了基于 MVC 架构模式的 架构接口,定义了框架的核心功能契约。 **主要职责:** + - 组件注册:注册 System、Model、Utility - 组件获取:从容器中获取已注册的组件 - 命令处理:发送并执行命令 @@ -49,6 +50,7 @@ void UnRegisterEvent(Action onEvent); 架构基类,实现了 [`IArchitecture`](IArchitecture.cs) 接口,提供完整的架构功能实现。 **特性:** + - **单例模式**:使用泛型和 `Lazy` 确保全局唯一实例 - **线程安全**:采用 `LazyThreadSafetyMode.ExecutionAndPublication` 保证多线程安全 - **生命周期管理**:自动管理 System 和 Model 的初始化顺序 @@ -56,6 +58,7 @@ void UnRegisterEvent(Action onEvent); - **事件系统**:集成类型化事件系统,支持类型安全的事件通信 **初始化流程:** + 1. 创建架构实例 2. 调用用户自定义的 `Init()` 方法 3. 执行注册的补丁逻辑(`OnRegisterPatch`) @@ -133,27 +136,32 @@ GameArchitecture.Interface.RegisterSystem(newSystem); ## 设计模式 ### 单例模式 + 通过泛型约束和 `Lazy` 实现类型安全的单例。 ### 依赖注入(IoC) + 使用内置 IoC 容器管理组件生命周期和依赖关系。 ### 命令模式 + 通过 [`ICommand`](../command/ICommand.cs) 封装所有用户操作。 ### 查询模式(CQRS) + 通过 [`IQuery`](../query/IQuery.cs) 分离查询和命令操作。 ### 观察者模式 + 通过事件系统实现组件间的松耦合通信。 ## 最佳实践 1. **保持架构类简洁**:只在 `Init()` 中注册组件,不要包含业务逻辑 2. **合理划分职责**: - - Model:数据和状态 - - System:业务逻辑和规则 - - Utility:无状态的工具方法 + - Model:数据和状态 + - System:业务逻辑和规则 + - Utility:无状态的工具方法 3. **使用接口访问**:通过 `Interface` 属性访问架构,便于测试 4. **事件命名规范**:使用过去式命名事件类,如 `PlayerDiedEvent` 5. **避免循环依赖**:System 不应直接引用 System,应通过事件通信 diff --git a/GFramework.Core/command/AbstractCommand.cs b/GFramework.Core/command/AbstractCommand.cs index 36c86fa..9618bab 100644 --- a/GFramework.Core/command/AbstractCommand.cs +++ b/GFramework.Core/command/AbstractCommand.cs @@ -4,37 +4,46 @@ using GFramework.Core.rule; namespace GFramework.Core.command; /// -/// 抽象命令类,实现 ICommand 接口,为具体命令提供基础架构支持 +/// 抽象命令类,实现 ICommand 接口,为具体命令提供基础架构支持 /// public abstract class AbstractCommand : ICommand { private IArchitecture _mArchitecture; /// - /// 获取命令所属的架构实例 + /// 获取命令所属的架构实例 /// /// IArchitecture 架构接口实例 - IArchitecture IBelongToArchitecture.GetArchitecture() => _mArchitecture; + IArchitecture IBelongToArchitecture.GetArchitecture() + { + return _mArchitecture; + } /// - /// 设置命令所属的架构实例 + /// 设置命令所属的架构实例 /// /// 要设置的架构实例 - void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture; + void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) + { + _mArchitecture = architecture; + } /// - /// 执行命令,调用抽象方法 OnExecute 来实现具体逻辑 + /// 执行命令,调用抽象方法 OnExecute 来实现具体逻辑 /// - void ICommand.Execute() => OnExecute(); + void ICommand.Execute() + { + OnExecute(); + } /// - /// 抽象方法,由子类实现具体的命令执行逻辑 + /// 抽象方法,由子类实现具体的命令执行逻辑 /// protected abstract void OnExecute(); } /// -/// 带返回值的抽象命令类,实现 ICommand{TResult} 接口,为需要返回结果的命令提供基础架构支持 +/// 带返回值的抽象命令类,实现 ICommand{TResult} 接口,为需要返回结果的命令提供基础架构支持 /// /// 命令执行后返回的结果类型 public abstract class AbstractCommand : ICommand @@ -42,26 +51,35 @@ public abstract class AbstractCommand : ICommand private IArchitecture _mArchitecture; /// - /// 获取命令所属的架构实例 + /// 获取命令所属的架构实例 /// /// IArchitecture 架构接口实例 - IArchitecture IBelongToArchitecture.GetArchitecture() => _mArchitecture; + IArchitecture IBelongToArchitecture.GetArchitecture() + { + return _mArchitecture; + } /// - /// 设置命令所属的架构实例 + /// 设置命令所属的架构实例 /// /// 要设置的架构实例 - void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture; + void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) + { + _mArchitecture = architecture; + } /// - /// 执行命令,调用抽象方法 OnExecute 来实现具体逻辑并返回结果 + /// 执行命令,调用抽象方法 OnExecute 来实现具体逻辑并返回结果 /// /// TResult 类型的执行结果 - TResult ICommand.Execute() => OnExecute(); + TResult ICommand.Execute() + { + return OnExecute(); + } /// - /// 抽象方法,由子类实现具体的命令执行逻辑 + /// 抽象方法,由子类实现具体的命令执行逻辑 /// /// TResult 类型的执行结果 protected abstract TResult OnExecute(); -} +} \ No newline at end of file diff --git a/GFramework.Core/command/ICanSendCommand.cs b/GFramework.Core/command/ICanSendCommand.cs index f863581..47d3d08 100644 --- a/GFramework.Core/command/ICanSendCommand.cs +++ b/GFramework.Core/command/ICanSendCommand.cs @@ -3,7 +3,7 @@ using GFramework.Core.rule; namespace GFramework.Core.command; /// -/// 定义一个可以发送命令的接口,继承自IBelongToArchitecture接口。 -/// 该接口用于标识那些具备发送命令能力的架构组件。 +/// 定义一个可以发送命令的接口,继承自IBelongToArchitecture接口。 +/// 该接口用于标识那些具备发送命令能力的架构组件。 /// -public interface ICanSendCommand : IBelongToArchitecture; +public interface ICanSendCommand : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/command/ICommand.cs b/GFramework.Core/command/ICommand.cs index b0ec155..047e749 100644 --- a/GFramework.Core/command/ICommand.cs +++ b/GFramework.Core/command/ICommand.cs @@ -8,22 +8,22 @@ using GFramework.Core.utility; namespace GFramework.Core.command; /// -/// 命令接口,定义了无返回值命令的基本契约 -/// 该接口继承了多个框架能力接口,使命令可以访问架构、系统、模型、工具,并能够发送事件、命令和查询 +/// 命令接口,定义了无返回值命令的基本契约 +/// 该接口继承了多个框架能力接口,使命令可以访问架构、系统、模型、工具,并能够发送事件、命令和查询 /// public interface ICommand : ICanSetArchitecture, ICanGetSystem, ICanGetModel, ICanGetUtility, ICanSendEvent, ICanSendCommand, ICanSendQuery { /// - /// 执行命令的核心方法 - /// 该方法不接受参数且无返回值,具体实现由派生类完成 + /// 执行命令的核心方法 + /// 该方法不接受参数且无返回值,具体实现由派生类完成 /// void Execute(); } /// -/// 带返回值的命令接口,定义了有返回值命令的基本契约 -/// 该接口继承了多个框架能力接口,使命令可以访问架构、系统、模型、工具,并能够发送事件、命令和查询 +/// 带返回值的命令接口,定义了有返回值命令的基本契约 +/// 该接口继承了多个框架能力接口,使命令可以访问架构、系统、模型、工具,并能够发送事件、命令和查询 /// /// 命令执行后返回的结果类型 public interface ICommand : ICanSetArchitecture, ICanGetSystem, ICanGetModel, @@ -31,9 +31,9 @@ public interface ICommand : ICanSetArchitecture, ICanGetSystem, ICa ICanSendEvent, ICanSendCommand, ICanSendQuery { /// - /// 执行命令的核心方法 - /// 该方法不接受参数,但会返回指定类型的结果 + /// 执行命令的核心方法 + /// 该方法不接受参数,但会返回指定类型的结果 /// /// 命令执行后的结果,类型为 TResult TResult Execute(); -} +} \ No newline at end of file diff --git a/GFramework.Core/command/README.md b/GFramework.Core/command/README.md index 931763b..f91e041 100644 --- a/GFramework.Core/command/README.md +++ b/GFramework.Core/command/README.md @@ -11,6 +11,7 @@ Command 包实现了命令模式(Command Pattern),用于封装用户操作 无返回值命令接口,定义了命令的基本契约。 **继承的能力接口:** + - [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构 - [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统 - [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型 @@ -20,6 +21,7 @@ Command 包实现了命令模式(Command Pattern),用于封装用户操作 - [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询 **核心方法:** + ```csharp void Execute(); // 执行命令 ``` @@ -29,6 +31,7 @@ void Execute(); // 执行命令 带返回值的命令接口,用于需要返回执行结果的命令。 **核心方法:** + ```csharp TResult Execute(); // 执行命令并返回结果 ``` @@ -232,12 +235,14 @@ public class LoginCommand : AbstractCommand ## 命令 vs 系统方法 **何时使用命令:** + - 需要参数化操作 - 需要记录操作历史(用于撤销/重做) - 操作需要跨多个系统协调 - 用户触发的离散操作 **何时使用系统方法:** + - 持续运行的逻辑(如每帧更新) - 系统内部的私有逻辑 - 不需要外部调用的功能 diff --git a/GFramework.Core/controller/IController.cs b/GFramework.Core/controller/IController.cs index 3b5e9c6..1bc4733 100644 --- a/GFramework.Core/controller/IController.cs +++ b/GFramework.Core/controller/IController.cs @@ -8,9 +8,9 @@ using GFramework.Core.utility; namespace GFramework.Core.controller; /// -/// 控制器接口,定义了控制器需要实现的所有功能契约 -/// 该接口继承了多个框架核心接口,用于支持控制器的各种能力 -/// 包括架构归属、命令发送、系统获取、模型获取、事件注册、查询发送和工具获取等功能 +/// 控制器接口,定义了控制器需要实现的所有功能契约 +/// 该接口继承了多个框架核心接口,用于支持控制器的各种能力 +/// 包括架构归属、命令发送、系统获取、模型获取、事件注册、查询发送和工具获取等功能 /// public interface IController : ICanSendCommand, ICanGetSystem, ICanGetModel, - ICanRegisterEvent, ICanSendQuery, ICanGetUtility; + ICanRegisterEvent, ICanSendQuery, ICanGetUtility; \ No newline at end of file diff --git a/GFramework.Core/controller/README.md b/GFramework.Core/controller/README.md index 15d2f59..98378f5 100644 --- a/GFramework.Core/controller/README.md +++ b/GFramework.Core/controller/README.md @@ -2,7 +2,8 @@ ## 概述 -Controller 包定义了控制器(Controller)的接口规范。控制器是 MVC 架构中的 C 层,负责处理用户交互、协调视图和模型,是连接表现层和业务层的桥梁。在本框架中,Controller 通常对应 Godot 的节点脚本。 +Controller 包定义了控制器(Controller)的接口规范。控制器是 MVC 架构中的 C +层,负责处理用户交互、协调视图和模型,是连接表现层和业务层的桥梁。在本框架中,Controller 通常对应 Godot 的节点脚本。 ## 核心接口 @@ -11,6 +12,7 @@ Controller 包定义了控制器(Controller)的接口规范。控制器是 M 控制器接口,定义了控制器需要实现的所有功能契约。 **继承的能力接口:** + - [`ICanSendCommand`](../command/ICanSendCommand.cs) - 可发送命令 - [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统 - [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型 @@ -21,6 +23,7 @@ Controller 包定义了控制器(Controller)的接口规范。控制器是 M **能力说明:** 控制器拥有框架中最全面的能力集合,可以: + 1. 发送命令执行业务逻辑 2. 获取系统调用服务 3. 获取模型读写数据 @@ -224,40 +227,40 @@ public partial class CombatController : Node, IController ### ✅ 应该做的事 1. **处理用户输入** - - 键盘、鼠标、触摸输入 - - UI 按钮点击 - - 手势识别 + - 键盘、鼠标、触摸输入 + - UI 按钮点击 + - 手势识别 2. **协调视图和模型** - - 监听模型变化更新视图 - - 将用户操作转换为命令 + - 监听模型变化更新视图 + - 将用户操作转换为命令 3. **管理界面逻辑** - - UI 元素的显示/隐藏 - - 动画播放控制 - - 视觉反馈 + - UI 元素的显示/隐藏 + - 动画播放控制 + - 视觉反馈 4. **事件监听** - - 注册关心的事件 - - 响应事件更新界面 + - 注册关心的事件 + - 响应事件更新界面 ### ❌ 不应该做的事 1. **不包含业务逻辑** - - 业务逻辑应该在 System 中 - - 控制器只负责调用和协调 + - 业务逻辑应该在 System 中 + - 控制器只负责调用和协调 2. **不直接修改模型** - - 应该通过 Command 修改模型 - - 避免直接访问 Model 的 setter + - 应该通过 Command 修改模型 + - 避免直接访问 Model 的 setter 3. **不处理复杂计算** - - 复杂计算应该在 Query 或 System 中 - - 控制器保持简洁 + - 复杂计算应该在 Query 或 System 中 + - 控制器保持简洁 4. **不保存核心状态** - - 核心状态应该在 Model 中 - - 控制器可以保存临时 UI 状态 + - 核心状态应该在 Model 中 + - 控制器可以保存临时 UI 状态 ## 生命周期管理 @@ -326,28 +329,28 @@ public partial class GameController : Node, IController ## 最佳实践 1. **一个控制器对应一个视图** - - 每个 Godot 场景/节点有对应的控制器 - - 避免一个控制器管理多个不相关的视图 + - 每个 Godot 场景/节点有对应的控制器 + - 避免一个控制器管理多个不相关的视图 2. **使用依赖注入获取依赖** - - 通过 `GetModel()`、`GetSystem()` 获取依赖 - - 不要在构造函数中获取,应在 `_Ready()` 中 + - 通过 `GetModel()`、`GetSystem()` 获取依赖 + - 不要在构造函数中获取,应在 `_Ready()` 中 3. **保持控制器轻量** - - 复杂逻辑放在 Command、Query、System 中 - - 控制器只做协调和转发 + - 复杂逻辑放在 Command、Query、System 中 + - 控制器只做协调和转发 4. **合理使用缓存** - - 频繁使用的 Model、System 可以缓存引用 - - 平衡性能和内存占用 + - 频繁使用的 Model、System 可以缓存引用 + - 平衡性能和内存占用 5. **统一管理事件注销** - - 使用 `IUnRegisterList` 统一管理 - - 在 `_ExitTree()` 中统一注销 + - 使用 `IUnRegisterList` 统一管理 + - 在 `_ExitTree()` 中统一注销 6. **命名规范** - - 控制器类名:`XxxController` - - 继承 Godot 节点:`Node`、`Control`、`Node2D` 等 + - 控制器类名:`XxxController` + - 继承 Godot 节点:`Node`、`Control`、`Node2D` 等 ## 常见模式 diff --git a/GFramework.Core/events/DefaultUnRegister.cs b/GFramework.Core/events/DefaultUnRegister.cs index e71da4a..78cb95c 100644 --- a/GFramework.Core/events/DefaultUnRegister.cs +++ b/GFramework.Core/events/DefaultUnRegister.cs @@ -1,16 +1,15 @@ namespace GFramework.Core.events; - /// -/// 默认注销器类,用于执行注销操作 +/// 默认注销器类,用于执行注销操作 /// /// 注销时要执行的回调函数 -public class DefaultUnRegister(Action onUnRegister): IUnRegister +public class DefaultUnRegister(Action onUnRegister) : IUnRegister { private Action _mOnUnRegister = onUnRegister; /// - /// 执行注销操作,调用注册的回调函数并清理引用 + /// 执行注销操作,调用注册的回调函数并清理引用 /// public void UnRegister() { @@ -18,4 +17,4 @@ public class DefaultUnRegister(Action onUnRegister): IUnRegister _mOnUnRegister.Invoke(); _mOnUnRegister = null; } -} +} \ No newline at end of file diff --git a/GFramework.Core/events/EasyEvent.cs b/GFramework.Core/events/EasyEvent.cs index f2a446f..cfc9aca 100644 --- a/GFramework.Core/events/EasyEvent.cs +++ b/GFramework.Core/events/EasyEvent.cs @@ -1,14 +1,14 @@ namespace GFramework.Core.events; /// -/// 简单事件类,用于注册、注销和触发无参事件回调 +/// 简单事件类,用于注册、注销和触发无参事件回调 /// public class EasyEvent { private Action _mOnEvent = () => { }; /// - /// 注册事件回调函数 + /// 注册事件回调函数 /// /// 要注册的事件回调函数 /// 用于注销事件的 unregister 对象 @@ -19,13 +19,19 @@ public class EasyEvent } /// - /// 注销已注册的事件回调函数 + /// 注销已注册的事件回调函数 /// /// 要注销的事件回调函数 - public void UnRegister(Action onEvent) => _mOnEvent -= onEvent; + public void UnRegister(Action onEvent) + { + _mOnEvent -= onEvent; + } /// - /// 触发所有已注册的事件回调函数 + /// 触发所有已注册的事件回调函数 /// - public void Trigger() => _mOnEvent?.Invoke(); -} + public void Trigger() + { + _mOnEvent?.Invoke(); + } +} \ No newline at end of file diff --git a/GFramework.Core/events/EasyEventGeneric.cs b/GFramework.Core/events/EasyEventGeneric.cs index 4f7d17c..129d0f5 100644 --- a/GFramework.Core/events/EasyEventGeneric.cs +++ b/GFramework.Core/events/EasyEventGeneric.cs @@ -1,20 +1,36 @@ namespace GFramework.Core.events; /// -/// 泛型事件类,支持一个泛型参数 T 的事件注册、注销与触发。 -/// 实现了 IEasyEvent 接口以提供统一的事件操作接口。 +/// 泛型事件类,支持一个泛型参数 T 的事件注册、注销与触发。 +/// 实现了 IEasyEvent 接口以提供统一的事件操作接口。 /// /// 事件回调函数的第一个参数类型。 public class EasyEvent : IEasyEvent { /// - /// 存储已注册的事件处理委托。 - /// 默认为空操作(no-op)委托,避免 null 检查。 + /// 存储已注册的事件处理委托。 + /// 默认为空操作(no-op)委托,避免 null 检查。 /// private Action _mOnEvent = e => { }; /// - /// 注册一个事件监听器,并返回可用于取消注册的对象。 + /// 显式实现 IEasyEvent 接口中的 Register 方法。 + /// 允许使用无参 Action 来订阅当前带参事件。 + /// + /// 无参事件处理方法。 + /// IUnRegister 对象,用于稍后注销该事件监听器。 + IUnRegister IEasyEvent.Register(Action onEvent) + { + return Register(Action); + + void Action(T _) + { + onEvent(); + } + } + + /// + /// 注册一个事件监听器,并返回可用于取消注册的对象。 /// /// 要注册的事件处理方法。 /// IUnRegister 对象,用于稍后注销该事件监听器。 @@ -25,46 +41,56 @@ public class EasyEvent : IEasyEvent } /// - /// 取消指定的事件监听器。 + /// 取消指定的事件监听器。 /// /// 需要被注销的事件处理方法。 - public void UnRegister(Action onEvent) => _mOnEvent -= onEvent; + public void UnRegister(Action onEvent) + { + _mOnEvent -= onEvent; + } /// - /// 触发所有已注册的事件处理程序,并传递参数 t。 + /// 触发所有已注册的事件处理程序,并传递参数 t。 /// /// 传递给事件处理程序的参数。 - public void Trigger(T t) => _mOnEvent?.Invoke(t); - - /// - /// 显式实现 IEasyEvent 接口中的 Register 方法。 - /// 允许使用无参 Action 来订阅当前带参事件。 - /// - /// 无参事件处理方法。 - /// IUnRegister 对象,用于稍后注销该事件监听器。 - IUnRegister IEasyEvent.Register(Action onEvent) + public void Trigger(T t) { - return Register(Action); - void Action(T _) => onEvent(); + _mOnEvent?.Invoke(t); } } /// -/// 支持两个泛型参数 T 和 TK 的事件类。 -/// 提供事件注册、注销和触发功能。 +/// 支持两个泛型参数 T 和 TK 的事件类。 +/// 提供事件注册、注销和触发功能。 /// /// 第一个参数类型。 /// 第二个参数类型。 public class EasyEvent : IEasyEvent { /// - /// 存储已注册的双参数事件处理委托。 - /// 默认为空操作(no-op)委托。 + /// 存储已注册的双参数事件处理委托。 + /// 默认为空操作(no-op)委托。 /// private Action _mOnEvent = (_, _) => { }; /// - /// 注册一个接受两个参数的事件监听器,并返回可用于取消注册的对象。 + /// 显式实现 IEasyEvent 接口中的 Register 方法。 + /// 允许使用无参 Action 来订阅当前带参事件。 + /// + /// 无参事件处理方法。 + /// IUnRegister 对象,用于稍后注销该事件监听器。 + IUnRegister IEasyEvent.Register(Action onEvent) + { + return Register(Action); + + void Action(T _, TK __) + { + onEvent(); + } + } + + /// + /// 注册一个接受两个参数的事件监听器,并返回可用于取消注册的对象。 /// /// 要注册的事件处理方法。 /// IUnRegister 对象,用于稍后注销该事件监听器。 @@ -75,34 +101,28 @@ public class EasyEvent : IEasyEvent } /// - /// 取消指定的双参数事件监听器。 + /// 取消指定的双参数事件监听器。 /// /// 需要被注销的事件处理方法。 - public void UnRegister(Action onEvent) => _mOnEvent -= onEvent; + public void UnRegister(Action onEvent) + { + _mOnEvent -= onEvent; + } /// - /// 触发所有已注册的事件处理程序,并传递参数 t 和 k。 + /// 触发所有已注册的事件处理程序,并传递参数 t 和 k。 /// /// 第一个参数。 /// 第二个参数。 - public void Trigger(T t, TK k) => _mOnEvent?.Invoke(t, k); - - /// - /// 显式实现 IEasyEvent 接口中的 Register 方法。 - /// 允许使用无参 Action 来订阅当前带参事件。 - /// - /// 无参事件处理方法。 - /// IUnRegister 对象,用于稍后注销该事件监听器。 - IUnRegister IEasyEvent.Register(Action onEvent) + public void Trigger(T t, TK k) { - return Register(Action); - void Action(T _, TK __) => onEvent(); + _mOnEvent?.Invoke(t, k); } } /// -/// 支持三个泛型参数 T、TK 和 TS 的事件类。 -/// 提供事件注册、注销和触发功能。 +/// 支持三个泛型参数 T、TK 和 TS 的事件类。 +/// 提供事件注册、注销和触发功能。 /// /// 第一个参数类型。 /// 第二个参数类型。 @@ -110,13 +130,29 @@ public class EasyEvent : IEasyEvent public class EasyEvent : IEasyEvent { /// - /// 存储已注册的三参数事件处理委托。 - /// 默认为空操作(no-op)委托。 + /// 存储已注册的三参数事件处理委托。 + /// 默认为空操作(no-op)委托。 /// private Action _mOnEvent = (_, _, _) => { }; /// - /// 注册一个接受三个参数的事件监听器,并返回可用于取消注册的对象。 + /// 显式实现 IEasyEvent 接口中的 Register 方法。 + /// 允许使用无参 Action 来订阅当前带参事件。 + /// + /// 无参事件处理方法。 + /// IUnRegister 对象,用于稍后注销该事件监听器。 + IUnRegister IEasyEvent.Register(Action onEvent) + { + return Register(Action); + + void Action(T _, TK __, TS ___) + { + onEvent(); + } + } + + /// + /// 注册一个接受三个参数的事件监听器,并返回可用于取消注册的对象。 /// /// 要注册的事件处理方法。 /// IUnRegister 对象,用于稍后注销该事件监听器。 @@ -127,28 +163,22 @@ public class EasyEvent : IEasyEvent } /// - /// 取消指定的三参数事件监听器。 + /// 取消指定的三参数事件监听器。 /// /// 需要被注销的事件处理方法。 - public void UnRegister(Action onEvent) => _mOnEvent -= onEvent; + public void UnRegister(Action onEvent) + { + _mOnEvent -= onEvent; + } /// - /// 触发所有已注册的事件处理程序,并传递参数 t、k 和 s。 + /// 触发所有已注册的事件处理程序,并传递参数 t、k 和 s。 /// /// 第一个参数。 /// 第二个参数。 /// 第三个参数。 - public void Trigger(T t, TK k, TS s) => _mOnEvent?.Invoke(t, k, s); - - /// - /// 显式实现 IEasyEvent 接口中的 Register 方法。 - /// 允许使用无参 Action 来订阅当前带参事件。 - /// - /// 无参事件处理方法。 - /// IUnRegister 对象,用于稍后注销该事件监听器。 - IUnRegister IEasyEvent.Register(Action onEvent) + public void Trigger(T t, TK k, TS s) { - return Register(Action); - void Action(T _, TK __, TS ___) => onEvent(); + _mOnEvent?.Invoke(t, k, s); } -} +} \ No newline at end of file diff --git a/GFramework.Core/events/EasyEvents.cs b/GFramework.Core/events/EasyEvents.cs index 6d2b00e..32d979c 100644 --- a/GFramework.Core/events/EasyEvents.cs +++ b/GFramework.Core/events/EasyEvents.cs @@ -1,42 +1,51 @@ namespace GFramework.Core.events; /// -/// EasyEvents事件管理器类,用于全局事件的注册、获取和管理 -/// 提供了类型安全的事件系统,支持泛型事件的自动创建和检索 +/// EasyEvents事件管理器类,用于全局事件的注册、获取和管理 +/// 提供了类型安全的事件系统,支持泛型事件的自动创建和检索 /// public class EasyEvents { /// - /// 全局单例事件管理器实例 + /// 全局单例事件管理器实例 /// private static readonly EasyEvents MGlobalEvents = new(); /// - /// 获取指定类型的全局事件实例 - /// - /// 事件类型,必须实现IEasyEvent接口 - /// 指定类型的事件实例,如果未注册则返回默认值 - public static T Get() where T : IEasyEvent => MGlobalEvents.GetEvent(); - - /// - /// 注册指定类型的全局事件 - /// - /// 事件类型,必须实现IEasyEvent接口且具有无参构造函数 - public static void Register() where T : IEasyEvent, new() => MGlobalEvents.AddEvent(); - - /// - /// 存储事件类型与事件实例映射关系的字典 + /// 存储事件类型与事件实例映射关系的字典 /// private readonly Dictionary _mTypeEvents = new(); /// - /// 添加指定类型的事件到事件字典中 + /// 获取指定类型的全局事件实例 /// - /// 事件类型,必须实现IEasyEvent接口且具有无参构造函数 - public void AddEvent() where T : IEasyEvent, new() => _mTypeEvents.Add(typeof(T), new T()); + /// 事件类型,必须实现IEasyEvent接口 + /// 指定类型的事件实例,如果未注册则返回默认值 + public static T Get() where T : IEasyEvent + { + return MGlobalEvents.GetEvent(); + } /// - /// 获取指定类型的事件实例 + /// 注册指定类型的全局事件 + /// + /// 事件类型,必须实现IEasyEvent接口且具有无参构造函数 + public static void Register() where T : IEasyEvent, new() + { + MGlobalEvents.AddEvent(); + } + + /// + /// 添加指定类型的事件到事件字典中 + /// + /// 事件类型,必须实现IEasyEvent接口且具有无参构造函数 + public void AddEvent() where T : IEasyEvent, new() + { + _mTypeEvents.Add(typeof(T), new T()); + } + + /// + /// 获取指定类型的事件实例 /// /// 事件类型,必须实现IEasyEvent接口 /// 指定类型的事件实例,如果不存在则返回默认值 @@ -46,7 +55,7 @@ public class EasyEvents } /// - /// 获取指定类型的事件实例,如果不存在则创建并添加到事件字典中 + /// 获取指定类型的事件实例,如果不存在则创建并添加到事件字典中 /// /// 事件类型,必须实现IEasyEvent接口且具有无参构造函数 /// 指定类型的事件实例 @@ -54,14 +63,11 @@ public class EasyEvents { var eType = typeof(T); // 尝试从字典中获取事件实例 - if (_mTypeEvents.TryGetValue(eType, out var e)) - { - return (T)e; - } + if (_mTypeEvents.TryGetValue(eType, out var e)) return (T)e; // 如果不存在则创建新实例并添加到字典中 var t = new T(); _mTypeEvents.Add(eType, t); return t; } -} +} \ No newline at end of file diff --git a/GFramework.Core/events/ICanRegisterEvent.cs b/GFramework.Core/events/ICanRegisterEvent.cs index f3fc0e5..4f81c32 100644 --- a/GFramework.Core/events/ICanRegisterEvent.cs +++ b/GFramework.Core/events/ICanRegisterEvent.cs @@ -3,8 +3,8 @@ using GFramework.Core.rule; namespace GFramework.Core.events; /// -/// 定义一个可以注册事件的接口,继承自IBelongToArchitecture接口。 -/// 该接口用于标识那些能够注册事件的对象,通常在框架的事件系统中使用。 -/// 实现此接口的类型表明它属于某个架构组件,并具备事件注册的能力。 +/// 定义一个可以注册事件的接口,继承自IBelongToArchitecture接口。 +/// 该接口用于标识那些能够注册事件的对象,通常在框架的事件系统中使用。 +/// 实现此接口的类型表明它属于某个架构组件,并具备事件注册的能力。 /// -public interface ICanRegisterEvent:IBelongToArchitecture; +public interface ICanRegisterEvent : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/events/ICanSendEvent.cs b/GFramework.Core/events/ICanSendEvent.cs index 26a0b86..0710c4e 100644 --- a/GFramework.Core/events/ICanSendEvent.cs +++ b/GFramework.Core/events/ICanSendEvent.cs @@ -3,7 +3,7 @@ using GFramework.Core.rule; namespace GFramework.Core.events; /// -/// 定义一个可以发送事件的接口,继承自IBelongToArchitecture接口。 -/// 该接口用于标识那些具备发送事件能力的类型,通常作为架构中事件发送功能的基础接口。 +/// 定义一个可以发送事件的接口,继承自IBelongToArchitecture接口。 +/// 该接口用于标识那些具备发送事件能力的类型,通常作为架构中事件发送功能的基础接口。 /// -public interface ICanSendEvent : IBelongToArchitecture; +public interface ICanSendEvent : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/events/IEasyEvent.cs b/GFramework.Core/events/IEasyEvent.cs index 1fdac52..ce4dd3d 100644 --- a/GFramework.Core/events/IEasyEvent.cs +++ b/GFramework.Core/events/IEasyEvent.cs @@ -1,12 +1,12 @@ namespace GFramework.Core.events; /// -/// 事件接口,定义了事件注册的基本功能 +/// 事件接口,定义了事件注册的基本功能 /// public interface IEasyEvent { /// - /// 注册事件处理函数 + /// 注册事件处理函数 /// /// 事件触发时要执行的回调函数 /// 用于取消注册的句柄对象 diff --git a/GFramework.Core/events/IUnRegister.cs b/GFramework.Core/events/IUnRegister.cs index 3498702..a086e8b 100644 --- a/GFramework.Core/events/IUnRegister.cs +++ b/GFramework.Core/events/IUnRegister.cs @@ -1,12 +1,12 @@ namespace GFramework.Core.events; /// -/// 提供注销功能的接口 +/// 提供注销功能的接口 /// public interface IUnRegister { /// - /// 执行注销操作 + /// 执行注销操作 /// void UnRegister(); -} +} \ No newline at end of file diff --git a/GFramework.Core/events/IUnRegisterList.cs b/GFramework.Core/events/IUnRegisterList.cs index 35869b4..3edbc88 100644 --- a/GFramework.Core/events/IUnRegisterList.cs +++ b/GFramework.Core/events/IUnRegisterList.cs @@ -1,12 +1,12 @@ namespace GFramework.Core.events; /// -/// 提供统一注销功能的接口,用于管理需要注销的对象列表 +/// 提供统一注销功能的接口,用于管理需要注销的对象列表 /// public interface IUnRegisterList { /// - /// 获取需要注销的对象列表 + /// 获取需要注销的对象列表 /// List UnregisterList { get; } -} +} \ No newline at end of file diff --git a/GFramework.Core/events/OrEvent.cs b/GFramework.Core/events/OrEvent.cs index db46a8a..2744693 100644 --- a/GFramework.Core/events/OrEvent.cs +++ b/GFramework.Core/events/OrEvent.cs @@ -3,12 +3,19 @@ using GFramework.Core.extensions; namespace GFramework.Core.events; /// -/// OrEvent类用于实现事件的或逻辑组合,当任意一个注册的事件触发时,都会触发OrEvent本身 +/// OrEvent类用于实现事件的或逻辑组合,当任意一个注册的事件触发时,都会触发OrEvent本身 /// public class OrEvent : IUnRegisterList { + private Action _mOnEvent = () => { }; + /// - /// 将指定的事件与当前OrEvent进行或逻辑组合 + /// 获取取消注册列表 + /// + public List UnregisterList { get; } = new(); + + /// + /// 将指定的事件与当前OrEvent进行或逻辑组合 /// /// 要组合的事件对象 /// 返回当前OrEvent实例,支持链式调用 @@ -18,10 +25,8 @@ public class OrEvent : IUnRegisterList return this; } - private Action _mOnEvent = () => { }; - /// - /// 注册事件处理函数 + /// 注册事件处理函数 /// /// 要注册的事件处理函数 /// 返回一个可取消注册的对象 @@ -32,7 +37,7 @@ public class OrEvent : IUnRegisterList } /// - /// 取消注册指定的事件处理函数 + /// 取消注册指定的事件处理函数 /// /// 要取消注册的事件处理函数 public void UnRegister(Action onEvent) @@ -42,12 +47,10 @@ public class OrEvent : IUnRegisterList } /// - /// 触发所有已注册的事件处理函数 + /// 触发所有已注册的事件处理函数 /// - private void Trigger() => _mOnEvent?.Invoke(); - - /// - /// 获取取消注册列表 - /// - public List UnregisterList { get; } = new List(); -} + private void Trigger() + { + _mOnEvent?.Invoke(); + } +} \ No newline at end of file diff --git a/GFramework.Core/events/README.md b/GFramework.Core/events/README.md index 2b03473..80651d6 100644 --- a/GFramework.Core/events/README.md +++ b/GFramework.Core/events/README.md @@ -11,6 +11,7 @@ Events 包提供了一套完整的事件系统,实现了观察者模式(Obse 基础事件接口,定义了事件注册的基本功能。 **核心方法:** + ```csharp IUnRegister Register(Action onEvent); // 注册事件处理函数 ``` @@ -20,6 +21,7 @@ IUnRegister Register(Action onEvent); // 注册事件处理函数 注销接口,用于取消事件注册。 **核心方法:** + ```csharp void UnRegister(); // 执行注销操作 ``` @@ -29,6 +31,7 @@ void UnRegister(); // 执行注销操作 注销列表接口,用于批量管理注销对象。 **属性:** + ```csharp List UnregisterList { get; } // 获取注销列表 ``` @@ -456,42 +459,42 @@ public override void _Ready() ## 最佳实践 1. **事件命名规范** - - 使用过去式:`PlayerDiedEvent`、`LevelCompletedEvent` - - 使用 `Event` 后缀:便于识别 - - 使用结构体:减少内存分配 + - 使用过去式:`PlayerDiedEvent`、`LevelCompletedEvent` + - 使用 `Event` 后缀:便于识别 + - 使用结构体:减少内存分配 2. **事件数据设计** - - 只包含必要信息 - - 使用值类型(struct)提高性能 - - 避免传递可变引用 + - 只包含必要信息 + - 使用值类型(struct)提高性能 + - 避免传递可变引用 3. **避免事件循环** - - 事件处理器中谨慎发送新事件 - - 使用命令打破循环依赖 + - 事件处理器中谨慎发送新事件 + - 使用命令打破循环依赖 4. **合理使用事件** - - 用于通知状态变化 - - 用于跨模块通信 - - 不用于返回数据(使用 Query) + - 用于通知状态变化 + - 用于跨模块通信 + - 不用于返回数据(使用 Query) 5. **注销管理** - - 始终注销事件监听 - - 使用 `IUnRegisterList` 批量管理 - - 利用 Godot 节点生命周期 + - 始终注销事件监听 + - 使用 `IUnRegisterList` 批量管理 + - 利用 Godot 节点生命周期 6. **性能考虑** - - 避免频繁触发的事件(如每帧) - - 事件处理器保持轻量 - - 使用结构体事件减少 GC + - 避免频繁触发的事件(如每帧) + - 事件处理器保持轻量 + - 使用结构体事件减少 GC ## 事件 vs 其他通信方式 -| 方式 | 适用场景 | 优点 | 缺点 | -|------|---------|------|------| -| **Event** | 状态变化通知、跨模块通信 | 松耦合、一对多 | 难以追踪调用链 | -| **Command** | 执行操作、修改状态 | 封装逻辑、可撤销 | 单向通信 | -| **Query** | 查询数据 | 职责清晰、有返回值 | 同步调用 | -| **BindableProperty** | UI 数据绑定 | 自动更新、响应式 | 仅限单一属性 | +| 方式 | 适用场景 | 优点 | 缺点 | +|----------------------|--------------|-----------|---------| +| **Event** | 状态变化通知、跨模块通信 | 松耦合、一对多 | 难以追踪调用链 | +| **Command** | 执行操作、修改状态 | 封装逻辑、可撤销 | 单向通信 | +| **Query** | 查询数据 | 职责清晰、有返回值 | 同步调用 | +| **BindableProperty** | UI 数据绑定 | 自动更新、响应式 | 仅限单一属性 | ## 相关包 diff --git a/GFramework.Core/events/TypeEventSystem.cs b/GFramework.Core/events/TypeEventSystem.cs index 287a83c..6ffbb0f 100644 --- a/GFramework.Core/events/TypeEventSystem.cs +++ b/GFramework.Core/events/TypeEventSystem.cs @@ -1,19 +1,27 @@ namespace GFramework.Core.events; /// -/// TypeEventSystem +/// TypeEventSystem /// public class TypeEventSystem { + public static readonly TypeEventSystem Global = new(); private readonly EasyEvents _mEvents = new(); - public static readonly TypeEventSystem Global = new(); + public void Send() where T : new() + { + _mEvents.GetEvent>()?.Trigger(new T()); + } - public void Send() where T : new() => _mEvents.GetEvent>()?.Trigger(new T()); + public void Send(T e) + { + _mEvents.GetEvent>()?.Trigger(e); + } - public void Send(T e) => _mEvents.GetEvent>()?.Trigger(e); - - public IUnRegister Register(Action onEvent) => _mEvents.GetOrAddEvent>().Register(onEvent); + public IUnRegister Register(Action onEvent) + { + return _mEvents.GetOrAddEvent>().Register(onEvent); + } public void UnRegister(Action onEvent) { diff --git a/GFramework.Core/events/UnRegisterList.cs b/GFramework.Core/events/UnRegisterList.cs index f5c4b8c..c1bf0b0 100644 --- a/GFramework.Core/events/UnRegisterList.cs +++ b/GFramework.Core/events/UnRegisterList.cs @@ -1,15 +1,19 @@ namespace GFramework.Core.events; - /// -/// 取消注册列表类,用于管理多个需要取消注册的对象 +/// 取消注册列表类,用于管理多个需要取消注册的对象 /// public class UnRegisterList : IUnRegisterList { private readonly List _unRegisterList = []; /// - /// 向取消注册列表中添加一个新的可取消注册对象 + /// 获取取消注册列表的只读属性 + /// + public List UnregisterList { get; } + + /// + /// 向取消注册列表中添加一个新的可取消注册对象 /// /// 需要添加到列表中的可取消注册对象 public void Add(IUnRegister unRegister) @@ -18,22 +22,14 @@ public class UnRegisterList : IUnRegisterList } /// - /// 对列表中的所有对象执行取消注册操作,并清空列表 + /// 对列表中的所有对象执行取消注册操作,并清空列表 /// public void UnRegisterAll() { // 遍历所有注册项并执行取消注册 - foreach (var t in _unRegisterList) - { - t.UnRegister(); - } + foreach (var t in _unRegisterList) t.UnRegister(); // 清空列表 _unRegisterList.Clear(); } - - /// - /// 获取取消注册列表的只读属性 - /// - public List UnregisterList { get; } -} +} \ No newline at end of file diff --git a/GFramework.Core/extensions/CanGetExtensions.cs b/GFramework.Core/extensions/CanGetExtensions.cs index de0d448..335580c 100644 --- a/GFramework.Core/extensions/CanGetExtensions.cs +++ b/GFramework.Core/extensions/CanGetExtensions.cs @@ -5,46 +5,52 @@ using GFramework.Core.utility; namespace GFramework.Core.extensions; /// -/// 提供获取模型对象扩展方法的静态类 +/// 提供获取模型对象扩展方法的静态类 /// public static class CanGetModelExtension { /// - /// 从架构中获取指定类型的模型实例 + /// 从架构中获取指定类型的模型实例 /// /// 要获取的模型类型,必须实现IModel接口 /// 实现ICanGetModel接口的对象实例 /// 指定类型的模型实例 - public static T GetModel(this ICanGetModel self) where T : class, IModel => - self.GetArchitecture().GetModel(); + public static T GetModel(this ICanGetModel self) where T : class, IModel + { + return self.GetArchitecture().GetModel(); + } } /// -/// 提供获取系统对象扩展方法的静态类 +/// 提供获取系统对象扩展方法的静态类 /// public static class CanGetSystemExtension { /// - /// 从架构中获取指定类型的系统实例 + /// 从架构中获取指定类型的系统实例 /// /// 要获取的系统类型,必须实现ISystem接口 /// 实现ICanGetSystem接口的对象实例 /// 指定类型的系统实例 - public static T GetSystem(this ICanGetSystem self) where T : class, ISystem => - self.GetArchitecture().GetSystem(); + public static T GetSystem(this ICanGetSystem self) where T : class, ISystem + { + return self.GetArchitecture().GetSystem(); + } } /// -/// 提供获取工具对象扩展方法的静态类 +/// 提供获取工具对象扩展方法的静态类 /// public static class CanGetUtilityExtension { /// - /// 从架构中获取指定类型的工具实例 + /// 从架构中获取指定类型的工具实例 /// /// 要获取的工具类型,必须实现IUtility接口 /// 实现ICanGetUtility接口的对象实例 /// 指定类型的工具实例 - public static T GetUtility(this ICanGetUtility self) where T : class, IUtility => - self.GetArchitecture().GetUtility(); + public static T GetUtility(this ICanGetUtility self) where T : class, IUtility + { + return self.GetArchitecture().GetUtility(); + } } \ No newline at end of file diff --git a/GFramework.Core/extensions/CanRegisterEventExtensions.cs b/GFramework.Core/extensions/CanRegisterEventExtensions.cs index b3a13d6..e059a62 100644 --- a/GFramework.Core/extensions/CanRegisterEventExtensions.cs +++ b/GFramework.Core/extensions/CanRegisterEventExtensions.cs @@ -3,26 +3,30 @@ namespace GFramework.Core.extensions; /// -/// 事件注册扩展类,提供ICanRegisterEvent接口的扩展方法用于注册和注销事件 +/// 事件注册扩展类,提供ICanRegisterEvent接口的扩展方法用于注册和注销事件 /// public static class CanRegisterEventExtensions { /// - /// 注册指定类型的事件处理函数 + /// 注册指定类型的事件处理函数 /// /// 事件数据类型 /// 实现ICanRegisterEvent接口的对象实例 /// 事件处理回调函数 /// 返回事件注销器接口,可用于后续注销事件 - public static IUnRegister RegisterEvent(this ICanRegisterEvent self, Action onEvent) => - self.GetArchitecture().RegisterEvent(onEvent); + public static IUnRegister RegisterEvent(this ICanRegisterEvent self, Action onEvent) + { + return self.GetArchitecture().RegisterEvent(onEvent); + } /// - /// 注销指定类型的事件处理函数 + /// 注销指定类型的事件处理函数 /// /// 事件数据类型 /// 实现ICanRegisterEvent接口的对象实例 /// 要注销的事件处理回调函数 - public static void UnRegisterEvent(this ICanRegisterEvent self, Action onEvent) => + public static void UnRegisterEvent(this ICanRegisterEvent self, Action onEvent) + { self.GetArchitecture().UnRegisterEvent(onEvent); -} + } +} \ No newline at end of file diff --git a/GFramework.Core/extensions/CanSendExtensions.cs b/GFramework.Core/extensions/CanSendExtensions.cs index 2e4b78b..f72c216 100644 --- a/GFramework.Core/extensions/CanSendExtensions.cs +++ b/GFramework.Core/extensions/CanSendExtensions.cs @@ -5,72 +5,85 @@ using GFramework.Core.query; namespace GFramework.Core.extensions; /// -/// 提供发送命令功能的扩展类 +/// 提供发送命令功能的扩展类 /// public static class CanSendCommandExtension { /// - /// 发送指定类型的命令,该命令类型必须实现ICommand接口且具有无参构造函数 + /// 发送指定类型的命令,该命令类型必须实现ICommand接口且具有无参构造函数 /// /// 命令类型,必须实现ICommand接口且具有无参构造函数 /// 实现ICanSendCommand接口的对象实例 - public static void SendCommand(this ICanSendCommand self) where T : ICommand, new() => + public static void SendCommand(this ICanSendCommand self) where T : ICommand, new() + { self.GetArchitecture().SendCommand(new T()); + } /// - /// 发送指定的命令实例 + /// 发送指定的命令实例 /// /// 命令类型,必须实现ICommand接口 /// 实现ICanSendCommand接口的对象实例 /// 要发送的命令实例 - public static void SendCommand(this ICanSendCommand self, T command) where T : ICommand => + public static void SendCommand(this ICanSendCommand self, T command) where T : ICommand + { self.GetArchitecture().SendCommand(command); + } /// - /// 发送带有返回值的命令并获取执行结果 + /// 发送带有返回值的命令并获取执行结果 /// /// 命令执行结果的类型 /// 实现ICanSendCommand接口的对象实例 /// 要发送的命令实例,必须实现ICommand<TResult>接口 /// 命令执行后的返回结果 - public static TResult SendCommand(this ICanSendCommand self, ICommand command) => - self.GetArchitecture().SendCommand(command); + public static TResult SendCommand(this ICanSendCommand self, ICommand command) + { + return self.GetArchitecture().SendCommand(command); + } } /// -/// 提供发送事件功能的扩展类 +/// 提供发送事件功能的扩展类 /// public static class CanSendEventExtension { /// - /// 发送指定类型的事件,该事件类型必须具有无参构造函数 + /// 发送指定类型的事件,该事件类型必须具有无参构造函数 /// /// 事件类型,必须具有无参构造函数 /// 实现ICanSendEvent接口的对象实例 - public static void SendEvent(this ICanSendEvent self) where T : new() => + public static void SendEvent(this ICanSendEvent self) where T : new() + { self.GetArchitecture().SendEvent(); + } /// - /// 发送指定的事件实例 + /// 发送指定的事件实例 /// /// 事件类型 /// 实现ICanSendEvent接口的对象实例 /// 要发送的事件实例 - public static void SendEvent(this ICanSendEvent self, T e) => self.GetArchitecture().SendEvent(e); + public static void SendEvent(this ICanSendEvent self, T e) + { + self.GetArchitecture().SendEvent(e); + } } /// -/// 提供发送查询功能的扩展类 +/// 提供发送查询功能的扩展类 /// public static class CanSendQueryExtension { /// - /// 发送查询请求并获取查询结果 + /// 发送查询请求并获取查询结果 /// /// 查询结果的类型 /// 实现ICanSendQuery接口的对象实例 /// 要发送的查询实例,必须实现IQuery<TResult>接口 /// 查询操作的返回结果 - public static TResult SendQuery(this ICanSendQuery self, IQuery query) => - self.GetArchitecture().SendQuery(query); -} + public static TResult SendQuery(this ICanSendQuery self, IQuery query) + { + return self.GetArchitecture().SendQuery(query); + } +} \ No newline at end of file diff --git a/GFramework.Core/extensions/OrEventExtensions.cs b/GFramework.Core/extensions/OrEventExtensions.cs index b61bdda..49a880f 100644 --- a/GFramework.Core/extensions/OrEventExtensions.cs +++ b/GFramework.Core/extensions/OrEventExtensions.cs @@ -3,15 +3,18 @@ namespace GFramework.Core.extensions; /// -/// 提供Or事件扩展方法的静态类 +/// 提供Or事件扩展方法的静态类 /// public static class OrEventExtensions { /// - /// 创建一个OrEvent实例,将当前事件与指定事件进行逻辑或运算组合 + /// 创建一个OrEvent实例,将当前事件与指定事件进行逻辑或运算组合 /// /// 当前的IEasyEvent事件实例 /// 要与当前事件进行或运算的另一个IEasyEvent事件实例 /// 返回一个新的OrEvent实例,表示两个事件的或运算结果 - public static OrEvent Or(this IEasyEvent self, IEasyEvent e) => new OrEvent().Or(self).Or(e); + public static OrEvent Or(this IEasyEvent self, IEasyEvent e) + { + return new OrEvent().Or(self).Or(e); + } } \ No newline at end of file diff --git a/GFramework.Core/extensions/README.md b/GFramework.Core/extensions/README.md index dad7ab7..0bb2144 100644 --- a/GFramework.Core/extensions/README.md +++ b/GFramework.Core/extensions/README.md @@ -8,7 +8,8 @@ Extensions 包提供了一系列扩展方法,简化了框架各个接口的使 ### 1. 获取组件扩展 ([`CanGetExtensions.cs`](CanGetExtensions.cs)) -为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。 +为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[ +`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。 #### CanGetModelExtension @@ -480,17 +481,17 @@ public class AchievementSystem : AbstractSystem ``` 2. **理解扩展方法本质**: - - 扩展方法是静态方法的语法糖 - - 不会改变原始类型的结构 - - 仅在编译时解析 + - 扩展方法是静态方法的语法糖 + - 不会改变原始类型的结构 + - 仅在编译时解析 3. **Godot 特定功能**: - - `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用 - - 使用 `#if GODOT` 编译指令控制 + - `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用 + - 使用 `#if GODOT` 编译指令控制 4. **性能考虑**: - - 扩展方法本身无性能开销 - - 实际调用的是底层方法 + - 扩展方法本身无性能开销 + - 实际调用的是底层方法 ## 相关包 diff --git a/GFramework.Core/extensions/UnRegisterListExtension.cs b/GFramework.Core/extensions/UnRegisterListExtension.cs index c0ef67d..6a0a56d 100644 --- a/GFramework.Core/extensions/UnRegisterListExtension.cs +++ b/GFramework.Core/extensions/UnRegisterListExtension.cs @@ -2,33 +2,31 @@ namespace GFramework.Core.extensions; - /// -/// 扩展方法类,为IUnRegister和IUnRegisterList接口提供便捷的注册和注销功能 +/// 扩展方法类,为IUnRegister和IUnRegisterList接口提供便捷的注册和注销功能 /// public static class UnRegisterListExtension { /// - /// 将指定的可注销对象添加到注销列表中 + /// 将指定的可注销对象添加到注销列表中 /// /// 要添加的可注销对象 /// 目标注销列表 - public static void AddToUnregisterList(this IUnRegister self, IUnRegisterList unRegisterList) => + public static void AddToUnregisterList(this IUnRegister self, IUnRegisterList unRegisterList) + { unRegisterList.UnregisterList.Add(self); + } /// - /// 注销列表中的所有对象并清空列表 + /// 注销列表中的所有对象并清空列表 /// /// 包含注销列表的对象 public static void UnRegisterAll(this IUnRegisterList self) { // 遍历注销列表中的所有对象并执行注销操作 - foreach (var unRegister in self.UnregisterList) - { - unRegister.UnRegister(); - } + foreach (var unRegister in self.UnregisterList) unRegister.UnRegister(); // 清空注销列表 self.UnregisterList.Clear(); } -} +} \ No newline at end of file diff --git a/GFramework.Core/ioc/IocContainer.cs b/GFramework.Core/ioc/IocContainer.cs index fa92113..824abcc 100644 --- a/GFramework.Core/ioc/IocContainer.cs +++ b/GFramework.Core/ioc/IocContainer.cs @@ -1,14 +1,14 @@ namespace GFramework.Core.ioc; /// -/// IOC容器类,用于管理对象的注册和获取 +/// IOC容器类,用于管理对象的注册和获取 /// public class IocContainer { private readonly Dictionary _mInstances = new(); /// - /// 注册一个实例到IOC容器中 + /// 注册一个实例到IOC容器中 /// /// 实例的类型 /// 要注册的实例对象 @@ -20,7 +20,7 @@ public class IocContainer } /// - /// 从IOC容器中获取指定类型的实例 + /// 从IOC容器中获取指定类型的实例 /// /// 要获取的实例类型 /// 返回指定类型的实例,如果未找到则返回null @@ -29,11 +29,8 @@ public class IocContainer var key = typeof(T); // 尝试从字典中获取实例 - if (_mInstances.TryGetValue(key, out var retInstance)) - { - return retInstance as T; - } + if (_mInstances.TryGetValue(key, out var retInstance)) return retInstance as T; return null; } -} +} \ No newline at end of file diff --git a/GFramework.Core/ioc/README.md b/GFramework.Core/ioc/README.md index b70c6fc..00a89d9 100644 --- a/GFramework.Core/ioc/README.md +++ b/GFramework.Core/ioc/README.md @@ -2,7 +2,8 @@ ## 概述 -IoC(Inversion of Control,控制反转)包提供了一个轻量级的依赖注入容器,用于管理框架中各种组件的注册和获取。通过 IoC 容器,可以实现组件间的解耦,便于测试和维护。 +IoC(Inversion of Control,控制反转)包提供了一个轻量级的依赖注入容器,用于管理框架中各种组件的注册和获取。通过 IoC +容器,可以实现组件间的解耦,便于测试和维护。 ## 核心类 @@ -11,6 +12,7 @@ IoC(Inversion of Control,控制反转)包提供了一个轻量级的依赖 IoC 容器类,负责管理对象的注册和获取。 **主要功能:** + - 注册实例到容器 - 从容器中获取实例 - 类型安全的依赖管理 @@ -26,6 +28,7 @@ public void Register(T instance) ``` **参数:** + - `instance`: 要注册的实例对象 **使用示例:** @@ -48,6 +51,7 @@ public T Get() where T : class ``` **返回值:** + - 返回指定类型的实例,如果未找到则返回 `null` **使用示例:** @@ -294,6 +298,7 @@ var service = container.Get(); ``` **特点:** + - ✅ 简单易用 - ✅ 性能高 - ❌ 不支持构造函数注入 @@ -314,6 +319,7 @@ var controller = container.Resolve(); ``` **特点:** + - ✅ 自动依赖注入 - ✅ 生命周期管理 - ✅ 复杂场景支持 @@ -391,20 +397,20 @@ else ## 注意事项 1. **类型唯一性** - - 每个类型只能注册一个实例 - - 重复注册会覆盖之前的实例 + - 每个类型只能注册一个实例 + - 重复注册会覆盖之前的实例 2. **手动管理依赖顺序** - - 组件的依赖关系需要手动保证 - - 先注册被依赖的组件 + - 组件的依赖关系需要手动保证 + - 先注册被依赖的组件 3. **无生命周期管理** - - 实例一旦注册就一直存在 - - 需要手动管理实例的生命周期 + - 实例一旦注册就一直存在 + - 需要手动管理实例的生命周期 4. **线程安全** - - 当前实现非线程安全 - - 避免多线程同时访问 + - 当前实现非线程安全 + - 避免多线程同时访问 ## 扩展可能性 diff --git a/GFramework.Core/model/AbstractModel.cs b/GFramework.Core/model/AbstractModel.cs index 99ced7a..b34b932 100644 --- a/GFramework.Core/model/AbstractModel.cs +++ b/GFramework.Core/model/AbstractModel.cs @@ -3,34 +3,43 @@ namespace GFramework.Core.model; /// -/// 抽象模型基类,实现IModel接口,提供模型的基本架构支持 +/// 抽象模型基类,实现IModel接口,提供模型的基本架构支持 /// public abstract class AbstractModel : IModel { /// - /// 模型所属的架构实例 + /// 模型所属的架构实例 /// protected IArchitecture Architecture; /// - /// 获取模型所属的架构实例 + /// 获取模型所属的架构实例 /// /// 返回当前模型关联的架构对象 - public IArchitecture GetArchitecture() => Architecture; + public IArchitecture GetArchitecture() + { + return Architecture; + } /// - /// 设置模型所属的架构实例 + /// 设置模型所属的架构实例 /// /// 要关联到此模型的架构实例 - public void SetArchitecture(IArchitecture architecture) => Architecture = architecture; + public void SetArchitecture(IArchitecture architecture) + { + Architecture = architecture; + } /// - /// 初始化模型,调用抽象方法OnInit执行具体初始化逻辑 + /// 初始化模型,调用抽象方法OnInit执行具体初始化逻辑 /// - void IModel.Init() => OnInit(); + void IModel.Init() + { + OnInit(); + } /// - /// 抽象初始化方法,由子类实现具体的初始化逻辑 + /// 抽象初始化方法,由子类实现具体的初始化逻辑 /// protected abstract void OnInit(); -} +} \ No newline at end of file diff --git a/GFramework.Core/model/ICanGetModel.cs b/GFramework.Core/model/ICanGetModel.cs index b038882..5f1215c 100644 --- a/GFramework.Core/model/ICanGetModel.cs +++ b/GFramework.Core/model/ICanGetModel.cs @@ -3,7 +3,7 @@ namespace GFramework.Core.model; /// -/// 定义一个接口,表示可以获取模型的架构组件。 -/// 该接口继承自IBelongToArchitecture,表明实现此接口的类型属于特定架构。 +/// 定义一个接口,表示可以获取模型的架构组件。 +/// 该接口继承自IBelongToArchitecture,表明实现此接口的类型属于特定架构。 /// -public interface ICanGetModel : IBelongToArchitecture; +public interface ICanGetModel : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/model/IModel.cs b/GFramework.Core/model/IModel.cs index a657b2b..fdde852 100644 --- a/GFramework.Core/model/IModel.cs +++ b/GFramework.Core/model/IModel.cs @@ -5,12 +5,12 @@ using GFramework.Core.utility; namespace GFramework.Core.model; /// -/// 模型接口,定义了模型的基本行为和功能 +/// 模型接口,定义了模型的基本行为和功能 /// public interface IModel : ICanSetArchitecture, ICanGetUtility, ICanSendEvent { /// - /// 初始化模型 + /// 初始化模型 /// void Init(); -} +} \ No newline at end of file diff --git a/GFramework.Core/model/README.md b/GFramework.Core/model/README.md index 82461ae..92f5027 100644 --- a/GFramework.Core/model/README.md +++ b/GFramework.Core/model/README.md @@ -2,7 +2,8 @@ ## 概述 -Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的 M 层,负责管理应用程序的数据和状态。Model 层应该只包含数据和简单的数据逻辑,不包含复杂的业务逻辑。 +Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的 M 层,负责管理应用程序的数据和状态。Model +层应该只包含数据和简单的数据逻辑,不包含复杂的业务逻辑。 ## 核心接口 @@ -11,11 +12,13 @@ Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的 模型接口,定义了模型的基本行为和功能。 **继承的能力接口:** + - [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构引用 - [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具类 - [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件 **核心方法:** + ```csharp void Init(); // 初始化模型 ``` diff --git a/GFramework.Core/property/BindableProperty.cs b/GFramework.Core/property/BindableProperty.cs index 0e4daba..6cac10b 100644 --- a/GFramework.Core/property/BindableProperty.cs +++ b/GFramework.Core/property/BindableProperty.cs @@ -2,34 +2,23 @@ using GFramework.Core.events; namespace GFramework.Core.property; - /// -/// 可绑定属性类,用于实现数据绑定功能 +/// 可绑定属性类,用于实现数据绑定功能 /// /// 属性值的类型 /// 属性的默认值 public class BindableProperty(T defaultValue = default!) : IBindableProperty { + private Action? _mOnValueChanged; protected T MValue = defaultValue; /// - /// 获取或设置属性值比较器,默认使用Equals方法进行比较 + /// 获取或设置属性值比较器,默认使用Equals方法进行比较 /// public static Func Comparer { get; set; } = (a, b) => a!.Equals(b)!; /// - /// 设置自定义比较器 - /// - /// 用于比较两个值是否相等的函数 - /// 当前可绑定属性实例 - public BindableProperty WithComparer(Func comparer) - { - Comparer = comparer; - return this; - } - - /// - /// 获取或设置属性值,当值发生变化时会触发注册的回调事件 + /// 获取或设置属性值,当值发生变化时会触发注册的回调事件 /// public T Value { @@ -37,12 +26,12 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< set { // 使用 default(T) 替代 null 比较,避免 SonarQube 警告 - if (EqualityComparer.Default.Equals(value, default!) && - EqualityComparer.Default.Equals(MValue, default!)) + if (EqualityComparer.Default.Equals(value, default!) && + EqualityComparer.Default.Equals(MValue, default!)) return; // 若新值与旧值相等则不执行后续操作 - if (!EqualityComparer.Default.Equals(value, default!) && Comparer(value, MValue)) + if (!EqualityComparer.Default.Equals(value, default!) && Comparer(value, MValue)) return; SetValue(value); @@ -51,27 +40,16 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< } /// - /// 设置属性值的虚方法,可在子类中重写 + /// 直接设置属性值而不触发事件 /// /// 新的属性值 - protected virtual void SetValue(T newValue) => MValue = newValue; + public void SetValueWithoutEvent(T newValue) + { + MValue = newValue; + } /// - /// 获取属性值的虚方法,可在子类中重写 - /// - /// 当前属性值 - protected virtual T GetValue() => MValue; - - /// - /// 直接设置属性值而不触发事件 - /// - /// 新的属性值 - public void SetValueWithoutEvent(T newValue) => MValue = newValue; - - private Action? _mOnValueChanged = null; - - /// - /// 注册属性值变化事件回调 + /// 注册属性值变化事件回调 /// /// 属性值变化时的回调函数 /// 可用于取消注册的接口 @@ -82,7 +60,7 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< } /// - /// 注册属性值变化事件回调,并立即调用回调函数传递当前值 + /// 注册属性值变化事件回调,并立即调用回调函数传递当前值 /// /// 属性值变化时的回调函数 /// 可用于取消注册的接口 @@ -93,19 +71,23 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< } /// - /// 取消注册属性值变化事件回调 + /// 取消注册属性值变化事件回调 /// /// 要取消注册的回调函数 - public void UnRegister(Action onValueChanged) => _mOnValueChanged -= onValueChanged; + public void UnRegister(Action onValueChanged) + { + _mOnValueChanged -= onValueChanged; + } /// - /// 实现IEasyEvent接口的注册方法,将无参事件转换为有参事件处理 + /// 实现IEasyEvent接口的注册方法,将无参事件转换为有参事件处理 /// /// 无参事件回调 /// 可用于取消注册的接口 IUnRegister IEasyEvent.Register(Action onEvent) { return Register(Action); + void Action(T _) { onEvent(); @@ -113,8 +95,40 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< } /// - /// 返回属性值的字符串表示形式 + /// 设置自定义比较器 + /// + /// 用于比较两个值是否相等的函数 + /// 当前可绑定属性实例 + public BindableProperty WithComparer(Func comparer) + { + Comparer = comparer; + return this; + } + + /// + /// 设置属性值的虚方法,可在子类中重写 + /// + /// 新的属性值 + protected virtual void SetValue(T newValue) + { + MValue = newValue; + } + + /// + /// 获取属性值的虚方法,可在子类中重写 + /// + /// 当前属性值 + protected virtual T GetValue() + { + return MValue; + } + + /// + /// 返回属性值的字符串表示形式 /// /// 属性值的字符串表示 - public override string ToString() => Value?.ToString() ?? string.Empty; + public override string ToString() + { + return Value?.ToString() ?? string.Empty; + } } \ No newline at end of file diff --git a/GFramework.Core/property/BindablePropertyUnRegister.cs b/GFramework.Core/property/BindablePropertyUnRegister.cs index 7255b4a..b3526ad 100644 --- a/GFramework.Core/property/BindablePropertyUnRegister.cs +++ b/GFramework.Core/property/BindablePropertyUnRegister.cs @@ -2,9 +2,8 @@ using GFramework.Core.events; namespace GFramework.Core.property; - /// -/// 可绑定属性注销器类,用于取消注册可绑定属性的值变化监听 +/// 可绑定属性注销器类,用于取消注册可绑定属性的值变化监听 /// /// 可绑定属性的值类型 /// 需要注销的可绑定属性实例 @@ -13,17 +12,17 @@ public class BindablePropertyUnRegister(BindableProperty bindableProperty, : IUnRegister { /// - /// 获取或设置可绑定属性实例 + /// 获取或设置可绑定属性实例 /// public BindableProperty BindableProperty { get; set; } = bindableProperty; /// - /// 获取或设置值变化时的回调函数 + /// 获取或设置值变化时的回调函数 /// public Action OnValueChanged { get; set; } = onValueChanged; /// - /// 执行注销操作,取消注册值变化监听并清理引用 + /// 执行注销操作,取消注册值变化监听并清理引用 /// public void UnRegister() { @@ -34,4 +33,4 @@ public class BindablePropertyUnRegister(BindableProperty bindableProperty, // 清理回调函数引用 OnValueChanged = null; } -} +} \ No newline at end of file diff --git a/GFramework.Core/property/IBindableProperty.cs b/GFramework.Core/property/IBindableProperty.cs index c5a5a6e..68a659f 100644 --- a/GFramework.Core/property/IBindableProperty.cs +++ b/GFramework.Core/property/IBindableProperty.cs @@ -1,19 +1,19 @@ namespace GFramework.Core.property; /// -/// 可绑定属性接口,继承自只读可绑定属性接口,提供可读写的属性绑定功能 +/// 可绑定属性接口,继承自只读可绑定属性接口,提供可读写的属性绑定功能 /// /// 属性值的类型 public interface IBindableProperty : IReadonlyBindableProperty { /// - /// 获取或设置属性的值 + /// 获取或设置属性的值 /// new T Value { get; set; } - + /// - /// 设置属性值但不触发事件通知 + /// 设置属性值但不触发事件通知 /// /// 要设置的新值 void SetValueWithoutEvent(T newValue); -} +} \ No newline at end of file diff --git a/GFramework.Core/property/IReadonlyBindableProperty.cs b/GFramework.Core/property/IReadonlyBindableProperty.cs index e99793e..a9fc3ad 100644 --- a/GFramework.Core/property/IReadonlyBindableProperty.cs +++ b/GFramework.Core/property/IReadonlyBindableProperty.cs @@ -2,35 +2,34 @@ using GFramework.Core.events; namespace GFramework.Core.property; - /// -/// 只读可绑定属性接口,提供属性值的读取和变更监听功能 +/// 只读可绑定属性接口,提供属性值的读取和变更监听功能 /// /// 属性值的类型 public interface IReadonlyBindableProperty : IEasyEvent { /// - /// 获取属性的当前值 + /// 获取属性的当前值 /// T Value { get; } /// - /// 注册属性值变更回调,并立即执行一次初始值的回调 + /// 注册属性值变更回调,并立即执行一次初始值的回调 /// /// 属性值变更时执行的回调函数,参数为新的属性值 /// 用于取消注册的句柄对象 IUnRegister RegisterWithInitValue(Action action); - + /// - /// 取消注册属性值变更回调 + /// 取消注册属性值变更回调 /// /// 要取消注册的回调函数 void UnRegister(Action onValueChanged); - + /// - /// 注册属性值变更回调 + /// 注册属性值变更回调 /// /// 属性值变更时执行的回调函数,参数为新的属性值 /// 用于取消注册的句柄对象 IUnRegister Register(Action onValueChanged); -} +} \ No newline at end of file diff --git a/GFramework.Core/property/README.md b/GFramework.Core/property/README.md index 1f3db01..e9dc6f4 100644 --- a/GFramework.Core/property/README.md +++ b/GFramework.Core/property/README.md @@ -11,6 +11,7 @@ Property 包提供了可绑定属性(BindableProperty)的实现,支持属 只读可绑定属性接口,提供属性值的读取和变更监听功能。 **核心成员:** + ```csharp T Value { get; } // 获取属性值 IUnRegister Register(Action onValueChanged); // 注册监听 @@ -23,6 +24,7 @@ void UnRegister(Action onValueChanged); // 取消监听 可绑定属性接口,继承自只读接口,增加了修改能力。 **核心成员:** + ```csharp new T Value { get; set; } // 可读写的属性值 void SetValueWithoutEvent(T newValue); // 设置值但不触发事件 diff --git a/GFramework.Core/query/AbstractQuery.cs b/GFramework.Core/query/AbstractQuery.cs index 2eba9f6..756c0d6 100644 --- a/GFramework.Core/query/AbstractQuery.cs +++ b/GFramework.Core/query/AbstractQuery.cs @@ -3,34 +3,43 @@ using GFramework.Core.architecture; namespace GFramework.Core.query; /// -/// 抽象查询类,提供查询操作的基础实现 +/// 抽象查询类,提供查询操作的基础实现 /// /// 查询结果的类型 public abstract class AbstractQuery : IQuery { - /// - /// 执行查询操作 - /// - /// 查询结果 - public T Do() => OnDo(); - - /// - /// 抽象方法,由子类实现具体的查询逻辑 - /// - /// 查询结果 - protected abstract T OnDo(); - private IArchitecture _mArchitecture; /// - /// 获取架构实例 + /// 执行查询操作 /// - /// 架构实例 - public IArchitecture GetArchitecture() => _mArchitecture; + /// 查询结果 + public T Do() + { + return OnDo(); + } /// - /// 设置架构实例 + /// 获取架构实例 + /// + /// 架构实例 + public IArchitecture GetArchitecture() + { + return _mArchitecture; + } + + /// + /// 设置架构实例 /// /// 要设置的架构实例 - public void SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture; -} + public void SetArchitecture(IArchitecture architecture) + { + _mArchitecture = architecture; + } + + /// + /// 抽象方法,由子类实现具体的查询逻辑 + /// + /// 查询结果 + protected abstract T OnDo(); +} \ No newline at end of file diff --git a/GFramework.Core/query/ICanSendQuery.cs b/GFramework.Core/query/ICanSendQuery.cs index 291bdae..947c887 100644 --- a/GFramework.Core/query/ICanSendQuery.cs +++ b/GFramework.Core/query/ICanSendQuery.cs @@ -3,7 +3,7 @@ using GFramework.Core.rule; namespace GFramework.Core.query; /// -/// 定义一个可以发送查询的接口契约 -/// 该接口继承自IBelongToArchitecture,表示实现此接口的类型属于某个架构组件 +/// 定义一个可以发送查询的接口契约 +/// 该接口继承自IBelongToArchitecture,表示实现此接口的类型属于某个架构组件 /// -public interface ICanSendQuery : IBelongToArchitecture; +public interface ICanSendQuery : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/query/IQuery.cs b/GFramework.Core/query/IQuery.cs index 5170de5..f99ec0e 100644 --- a/GFramework.Core/query/IQuery.cs +++ b/GFramework.Core/query/IQuery.cs @@ -4,17 +4,16 @@ using GFramework.Core.system; namespace GFramework.Core.query; - /// -/// 查询接口,定义了执行查询操作的契约 +/// 查询接口,定义了执行查询操作的契约 /// /// 查询结果的类型 public interface IQuery : ICanSetArchitecture, ICanGetModel, ICanGetSystem, ICanSendQuery { /// - /// 执行查询操作并返回结果 + /// 执行查询操作并返回结果 /// /// 查询的结果,类型为 TResult TResult Do(); -} +} \ No newline at end of file diff --git a/GFramework.Core/query/README.md b/GFramework.Core/query/README.md index 9f8d849..aa53297 100644 --- a/GFramework.Core/query/README.md +++ b/GFramework.Core/query/README.md @@ -2,7 +2,8 @@ ## 概述 -Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。Query 用于封装数据查询逻辑,与 Command 不同的是,Query 有返回值且不应该修改系统状态。 +Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。Query 用于封装数据查询逻辑,与 Command 不同的是,Query +有返回值且不应该修改系统状态。 ## 核心接口 @@ -11,6 +12,7 @@ Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。 标记接口,表示该类型可以发送查询。 **继承关系:** + ```csharp public interface ICanSendQuery : IBelongToArchitecture ``` @@ -20,11 +22,13 @@ public interface ICanSendQuery : IBelongToArchitecture 查询接口,定义了查询的基本契约。 **核心成员:** + ```csharp TResult Do(); // 执行查询并返回结果 ``` **继承的能力:** + - `ICanSetArchitecture` - 可设置架构 - `ICanGetModel` - 可获取 Model - `ICanGetSystem` - 可获取 System @@ -37,6 +41,7 @@ TResult Do(); // 执行查询并返回结果 抽象查询基类,提供了查询的基础实现。 **使用方式:** + ```csharp public abstract class AbstractQuery : IQuery { @@ -287,11 +292,13 @@ public class EnemyAISystem : AbstractSystem ## Command vs Query ### Command(命令) + - **用途**:修改系统状态 - **返回值**:无返回值(void) - **示例**:购买物品、造成伤害、升级角色 ### Query(查询) + - **用途**:读取数据,不修改状态 - **返回值**:有返回值 - **示例**:获取金币数量、检查技能冷却、查询玩家位置 diff --git a/GFramework.Core/rule/IBelongToArchitecture.cs b/GFramework.Core/rule/IBelongToArchitecture.cs index 1c69927..eb98da4 100644 --- a/GFramework.Core/rule/IBelongToArchitecture.cs +++ b/GFramework.Core/rule/IBelongToArchitecture.cs @@ -2,16 +2,15 @@ using GFramework.Core.architecture; namespace GFramework.Core.rule; - /// -/// 定义一个接口,用于标识某个对象属于特定的架构体系。 -/// 实现此接口的对象可以通过GetArchitecture方法获取其所属的架构实例。 +/// 定义一个接口,用于标识某个对象属于特定的架构体系。 +/// 实现此接口的对象可以通过GetArchitecture方法获取其所属的架构实例。 /// public interface IBelongToArchitecture { /// - /// 获取当前对象所属的架构实例。 + /// 获取当前对象所属的架构实例。 /// /// 返回实现IArchitecture接口的架构实例 IArchitecture GetArchitecture(); -} +} \ No newline at end of file diff --git a/GFramework.Core/rule/ICanSetArchitecture.cs b/GFramework.Core/rule/ICanSetArchitecture.cs index 977311c..9fdfd66 100644 --- a/GFramework.Core/rule/ICanSetArchitecture.cs +++ b/GFramework.Core/rule/ICanSetArchitecture.cs @@ -3,13 +3,13 @@ using GFramework.Core.architecture; namespace GFramework.Core.rule; /// -/// 定义一个接口,用于设置架构实例 +/// 定义一个接口,用于设置架构实例 /// public interface ICanSetArchitecture { /// - /// 设置架构实例 + /// 设置架构实例 /// /// 要设置的架构实例 void SetArchitecture(IArchitecture architecture); -} +} \ No newline at end of file diff --git a/GFramework.Core/rule/README.md b/GFramework.Core/rule/README.md index 6704ce0..8da3b87 100644 --- a/GFramework.Core/rule/README.md +++ b/GFramework.Core/rule/README.md @@ -11,6 +11,7 @@ Rule 包定义了框架的核心规则接口,这些接口规定了框架各个 标记接口,表示某个对象属于特定的架构体系。 **接口定义:** + ```csharp public interface IBelongToArchitecture { @@ -19,6 +20,7 @@ public interface IBelongToArchitecture ``` **实现此接口的类型:** + - Controller - System - Model @@ -34,6 +36,7 @@ public interface IBelongToArchitecture 定义可以设置架构实例的能力。 **接口定义:** + ```csharp public interface ICanSetArchitecture { @@ -42,6 +45,7 @@ public interface ICanSetArchitecture ``` **实现此接口的类型:** + - Command - Query diff --git a/GFramework.Core/system/AbstractSystem.cs b/GFramework.Core/system/AbstractSystem.cs index bbc68a9..927c34a 100644 --- a/GFramework.Core/system/AbstractSystem.cs +++ b/GFramework.Core/system/AbstractSystem.cs @@ -4,32 +4,41 @@ using GFramework.Core.rule; namespace GFramework.Core.system; /// -/// 抽象系统基类,实现系统接口的基本功能 -/// 提供架构关联和初始化机制 +/// 抽象系统基类,实现系统接口的基本功能 +/// 提供架构关联和初始化机制 /// public abstract class AbstractSystem : ISystem { private IArchitecture _mArchitecture; /// - /// 获取当前系统所属的架构实例 + /// 获取当前系统所属的架构实例 /// /// 返回系统关联的架构对象 - IArchitecture IBelongToArchitecture.GetArchitecture() => _mArchitecture; + IArchitecture IBelongToArchitecture.GetArchitecture() + { + return _mArchitecture; + } /// - /// 设置系统所属的架构实例 + /// 设置系统所属的架构实例 /// /// 要关联的架构对象 - void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture; + void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) + { + _mArchitecture = architecture; + } /// - /// 系统初始化方法,调用抽象初始化方法 + /// 系统初始化方法,调用抽象初始化方法 /// - void ISystem.Init() => OnInit(); + void ISystem.Init() + { + OnInit(); + } /// - /// 抽象初始化方法,由子类实现具体的初始化逻辑 + /// 抽象初始化方法,由子类实现具体的初始化逻辑 /// protected abstract void OnInit(); -} +} \ No newline at end of file diff --git a/GFramework.Core/system/ICanGetSystem.cs b/GFramework.Core/system/ICanGetSystem.cs index ddbd00e..ecbf630 100644 --- a/GFramework.Core/system/ICanGetSystem.cs +++ b/GFramework.Core/system/ICanGetSystem.cs @@ -3,7 +3,7 @@ using GFramework.Core.rule; namespace GFramework.Core.system; /// -/// 定义一个接口,表示可以获取系统的对象。 -/// 该接口继承自IBelongToArchitecture接口,用于标识那些属于系统架构并能够获取系统实例的类型。 +/// 定义一个接口,表示可以获取系统的对象。 +/// 该接口继承自IBelongToArchitecture接口,用于标识那些属于系统架构并能够获取系统实例的类型。 /// -public interface ICanGetSystem : IBelongToArchitecture; +public interface ICanGetSystem : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/system/ISystem.cs b/GFramework.Core/system/ISystem.cs index 45a3a3e..4f5bef4 100644 --- a/GFramework.Core/system/ISystem.cs +++ b/GFramework.Core/system/ISystem.cs @@ -6,15 +6,15 @@ using GFramework.Core.utility; namespace GFramework.Core.system; /// -/// 系统接口,定义了系统的基本行为和功能 -/// 该接口继承了多个框架相关的接口,提供了系统初始化能力 +/// 系统接口,定义了系统的基本行为和功能 +/// 该接口继承了多个框架相关的接口,提供了系统初始化能力 /// public interface ISystem : ICanSetArchitecture, ICanGetModel, ICanGetUtility, ICanRegisterEvent, ICanSendEvent, ICanGetSystem { /// - /// 初始化系统 - /// 在系统被创建后调用,用于执行系统的初始化逻辑 + /// 初始化系统 + /// 在系统被创建后调用,用于执行系统的初始化逻辑 /// void Init(); -} +} \ No newline at end of file diff --git a/GFramework.Core/system/README.md b/GFramework.Core/system/README.md index 9b82309..1cefe54 100644 --- a/GFramework.Core/system/README.md +++ b/GFramework.Core/system/README.md @@ -11,6 +11,7 @@ System 包定义了业务逻辑层(Business Logic Layer)。System 负责处 标记接口,表示该类型可以获取其他 System。 **继承关系:** + ```csharp public interface ICanGetSystem : IBelongToArchitecture ``` @@ -20,11 +21,13 @@ public interface ICanGetSystem : IBelongToArchitecture System 接口,定义了系统的基本行为。 **核心成员:** + ```csharp void Init(); // 系统初始化方法 ``` **继承的能力:** + - `ICanSetArchitecture` - 可设置架构 - `ICanGetModel` - 可获取 Model - `ICanGetUtility` - 可获取 Utility @@ -39,6 +42,7 @@ void Init(); // 系统初始化方法 抽象 System 基类,提供了 System 的基础实现。 **使用方式:** + ```csharp public abstract class AbstractSystem : ISystem { @@ -419,11 +423,13 @@ public class GameStateSystem : AbstractSystem ## System vs Model ### Model(数据层) + - **职责**:存储数据和状态 - **特点**:被动,等待修改 - **示例**:PlayerModel、InventoryModel ### System(逻辑层) + - **职责**:处理业务逻辑,协调 Model - **特点**:主动,响应事件 - **示例**:CombatSystem、QuestSystem diff --git a/GFramework.Core/utility/ICanGetUtility.cs b/GFramework.Core/utility/ICanGetUtility.cs index 2dc1c03..4bb7771 100644 --- a/GFramework.Core/utility/ICanGetUtility.cs +++ b/GFramework.Core/utility/ICanGetUtility.cs @@ -3,7 +3,7 @@ namespace GFramework.Core.utility; /// -/// 定义一个接口,表示可以获取工具类的对象 -/// 该接口继承自IBelongToArchitecture,表明实现此接口的类型属于某个架构组件 +/// 定义一个接口,表示可以获取工具类的对象 +/// 该接口继承自IBelongToArchitecture,表明实现此接口的类型属于某个架构组件 /// -public interface ICanGetUtility : IBelongToArchitecture; +public interface ICanGetUtility : IBelongToArchitecture; \ No newline at end of file diff --git a/GFramework.Core/utility/IUtility.cs b/GFramework.Core/utility/IUtility.cs index 90366eb..f097af3 100644 --- a/GFramework.Core/utility/IUtility.cs +++ b/GFramework.Core/utility/IUtility.cs @@ -1,7 +1,7 @@ namespace GFramework.Core.utility; /// -/// IUtility接口定义了通用工具类的基本契约 -/// 该接口作为所有工具类实现的基础接口,提供统一的工具方法规范 +/// IUtility接口定义了通用工具类的基本契约 +/// 该接口作为所有工具类实现的基础接口,提供统一的工具方法规范 /// -public interface IUtility; +public interface IUtility; \ No newline at end of file diff --git a/GFramework.Core/utility/README.md b/GFramework.Core/utility/README.md index 76ed3e9..5446cc6 100644 --- a/GFramework.Core/utility/README.md +++ b/GFramework.Core/utility/README.md @@ -2,7 +2,8 @@ ## 概述 -Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如数学计算、文件操作、序列化等通用工具方法。与 System 不同,Utility 不依赖架构状态,是纯粹的工具函数集合。 +Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如数学计算、文件操作、序列化等通用工具方法。与 System 不同,Utility +不依赖架构状态,是纯粹的工具函数集合。 ## 核心接口 @@ -11,6 +12,7 @@ Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如 标记接口,表示该类型可以获取 Utility。 **继承关系:** + ```csharp public interface ICanGetUtility : IBelongToArchitecture ``` @@ -20,6 +22,7 @@ public interface ICanGetUtility : IBelongToArchitecture Utility 标记接口,所有工具类都应实现此接口。 **接口定义:** + ```csharp public interface IUtility { @@ -419,12 +422,14 @@ public class LogUtility : IUtility ## Utility vs System ### Utility(工具层) + - **无状态** - 不存储业务数据 - **纯函数** - 相同输入产生相同输出 - **独立性** - 不依赖架构状态 - **可复用** - 可在多个项目中使用 ### System(逻辑层) + - **有状态** - 可能存储临时状态 - **业务逻辑** - 处理特定业务流程 - **架构依赖** - 需要访问 Model diff --git a/GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs b/GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs index f78769c..b62031d 100644 --- a/GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs +++ b/GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs @@ -1,21 +1,20 @@ - -using System; +using System; namespace GFramework.Generator.Attributes { /// - /// 标注在 enum 上,Source Generator 会为该 enum 生成扩展方法。 + /// 标注在 enum 上,Source Generator 会为该 enum 生成扩展方法。 /// [AttributeUsage(AttributeTargets.Enum)] public sealed class GenerateEnumExtensionsAttribute : Attribute { /// - /// 是否为每个枚举项生成单独的 IsXXX 方法(默认 true)。 + /// 是否为每个枚举项生成单独的 IsXXX 方法(默认 true)。 /// public bool GenerateIsMethods { get; set; } = true; /// - /// 是否生成一个 IsIn(params T[]) 方法以简化多值判断(默认 true)。 + /// 是否生成一个 IsIn(params T[]) 方法以简化多值判断(默认 true)。 /// public bool GenerateIsInMethod { get; set; } = true; } diff --git a/GFramework.Generator/GFramework.Generator.csproj b/GFramework.Generator/GFramework.Generator.csproj index 2223095..593796c 100644 --- a/GFramework.Generator/GFramework.Generator.csproj +++ b/GFramework.Generator/GFramework.Generator.csproj @@ -20,7 +20,7 @@ - + @@ -33,12 +33,12 @@ - + - true - + true + diff --git a/GFramework.Generator/README.md b/GFramework.Generator/README.md index 9aa7518..08b71e1 100644 --- a/GFramework.Generator/README.md +++ b/GFramework.Generator/README.md @@ -1,3 +1,4 @@ 项目额外的简单代码生成器 目前已有的功能 + - 为枚举添加两个扩展方法方便判断枚举值 \ No newline at end of file diff --git a/GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs b/GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs index 3bc6df3..fdc7ab5 100644 --- a/GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs +++ b/GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs @@ -5,121 +5,118 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -namespace GFramework.Generator.generator.enums +namespace GFramework.Generator.generator.enums; + +[Generator] +public class EnumExtensionsGenerator : IIncrementalGenerator { - [Generator] - public class EnumExtensionsGenerator : IIncrementalGenerator + private const string AttributeFullName = "GFramework.Generator.Attributes.GenerateEnumExtensionsAttribute"; + + public void Initialize(IncrementalGeneratorInitializationContext context) { - private const string AttributeFullName = "GFramework.Generator.Attributes.GenerateEnumExtensionsAttribute"; + // 1. 找到所有 EnumDeclarationSyntax 节点 + var enumDecls = context.SyntaxProvider + .CreateSyntaxProvider( + (s, _) => s is EnumDeclarationSyntax, + (ctx, _) => + (EnumDecl: (EnumDeclarationSyntax)ctx.Node, ctx.SemanticModel)) + .Where(t => t.EnumDecl != null); - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // 1. 找到所有 EnumDeclarationSyntax 节点 - var enumDecls = context.SyntaxProvider - .CreateSyntaxProvider( - predicate: (s, _) => s is EnumDeclarationSyntax, - transform: (ctx, _) => - (EnumDecl: (EnumDeclarationSyntax)ctx.Node, ctx.SemanticModel)) - .Where(t => t.EnumDecl != null); - - // 2. 解析为 symbol 并过滤带 Attribute 的 enum - var enumSymbols = enumDecls - .Select((t, _) => - { - var model = t.SemanticModel; - var enumDecl = t.EnumDecl; - var symbol = model.GetDeclaredSymbol(enumDecl) as INamedTypeSymbol; - return symbol; - }) - .Where(symbol => symbol != null) - .Select((symbol, _) => - { - // 判断是否包含我们的 Attribute - var hasAttr = symbol.GetAttributes().Any(ad => - ad.AttributeClass?.ToDisplayString() == AttributeFullName || - ad.AttributeClass?.ToDisplayString().EndsWith(".GenerateEnumExtensionsAttribute") == true); - return (Symbol: symbol, HasAttr: hasAttr); - }) - .Where(x => x.HasAttr) - .Collect(); - - // 3. 为每个 enum 生成代码 - context.RegisterSourceOutput(enumSymbols, (spc, list) => + // 2. 解析为 symbol 并过滤带 Attribute 的 enum + var enumSymbols = enumDecls + .Select((t, _) => { - foreach (var enumSymbol in list.Select(item => item.Symbol)) + var model = t.SemanticModel; + var enumDecl = t.EnumDecl; + var symbol = model.GetDeclaredSymbol(enumDecl) as INamedTypeSymbol; + return symbol; + }) + .Where(symbol => symbol != null) + .Select((symbol, _) => + { + // 判断是否包含我们的 Attribute + var hasAttr = symbol.GetAttributes().Any(ad => + ad.AttributeClass?.ToDisplayString() == AttributeFullName || + ad.AttributeClass?.ToDisplayString().EndsWith(".GenerateEnumExtensionsAttribute") == true); + return (Symbol: symbol, HasAttr: hasAttr); + }) + .Where(x => x.HasAttr) + .Collect(); + + // 3. 为每个 enum 生成代码 + context.RegisterSourceOutput(enumSymbols, (spc, list) => + { + foreach (var enumSymbol in list.Select(item => item.Symbol)) + try { - try - { - var src = GenerateForEnum(enumSymbol); - var hintName = $"{enumSymbol.Name}.EnumExtensions.g.cs"; - spc.AddSource(hintName, SourceText.From(src, Encoding.UTF8)); - } - catch (Exception ex) - { - // 发生异常时生成一个注释文件(避免完全静默失败) - var err = $"// EnumExtensionsGenerator failed for {enumSymbol?.Name}: {ex.Message}"; - spc.AddSource($"{enumSymbol?.Name}.EnumExtensions.Error.g.cs", - SourceText.From(err, Encoding.UTF8)); - } + var src = GenerateForEnum(enumSymbol); + var hintName = $"{enumSymbol.Name}.EnumExtensions.g.cs"; + spc.AddSource(hintName, SourceText.From(src, Encoding.UTF8)); } - }); + catch (Exception ex) + { + // 发生异常时生成一个注释文件(避免完全静默失败) + var err = $"// EnumExtensionsGenerator failed for {enumSymbol?.Name}: {ex.Message}"; + spc.AddSource($"{enumSymbol?.Name}.EnumExtensions.Error.g.cs", + SourceText.From(err, Encoding.UTF8)); + } + }); + } + + private static string GenerateForEnum(INamedTypeSymbol enumSymbol) + { + var ns = enumSymbol.ContainingNamespace.IsGlobalNamespace + ? null + : enumSymbol.ContainingNamespace.ToDisplayString(); + var enumName = enumSymbol.Name; + var fullEnumName = enumSymbol.ToDisplayString(); // 包含命名空间 + var members = enumSymbol.GetMembers().OfType().Where(f => f.ConstantValue != null).ToArray(); + + var sb = new StringBuilder(); + sb.AppendLine("// "); + sb.AppendLine("using System;"); + if (!string.IsNullOrEmpty(ns)) + { + sb.AppendLine($"namespace {ns}"); + sb.AppendLine("{"); + } + else + { + sb.AppendLine("namespace EnumExtensionsGenerated"); + sb.AppendLine("{"); } - private static string GenerateForEnum(INamedTypeSymbol enumSymbol) - { - var ns = enumSymbol.ContainingNamespace.IsGlobalNamespace - ? null - : enumSymbol.ContainingNamespace.ToDisplayString(); - var enumName = enumSymbol.Name; - var fullEnumName = enumSymbol.ToDisplayString(); // 包含命名空间 - var members = enumSymbol.GetMembers().OfType().Where(f => f.ConstantValue != null).ToArray(); + sb.AppendLine($" public static partial class {enumName}Extensions"); + sb.AppendLine(" {"); - var sb = new StringBuilder(); - sb.AppendLine("// "); - sb.AppendLine("using System;"); - if (!string.IsNullOrEmpty(ns)) - { - sb.AppendLine($"namespace {ns}"); - sb.AppendLine("{"); - } - else - { - sb.AppendLine("namespace EnumExtensionsGenerated"); - sb.AppendLine("{"); - } - - sb.AppendLine($" public static partial class {enumName}Extensions"); - sb.AppendLine(" {"); - - // 1. 单项 IsX 方法 + // 1. 单项 IsX 方法 // 替换原第93行开始的 foreach 块 - var memberChecks = members.Select(m => - { - var memberName = m.Name; - var safeMethodName = $"Is{memberName}"; - return $@" /// Auto-generated: 是否为 {memberName} + var memberChecks = members.Select(m => + { + var memberName = m.Name; + var safeMethodName = $"Is{memberName}"; + return $@" /// Auto-generated: 是否为 {memberName} public static bool {safeMethodName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName}; "; - }).ToArray(); + }).ToArray(); - sb.Append(string.Join("", memberChecks)); + sb.Append(string.Join("", memberChecks)); - // 2. IsIn(params ...) 方法 - sb.AppendLine($" /// Auto-generated: 判断是否属于指定集合"); - sb.AppendLine( - $" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)"); - sb.AppendLine(" {"); - sb.AppendLine(" if (values == null) return false;"); - sb.AppendLine(" foreach (var v in values) if (value == v) return true;"); - sb.AppendLine(" return false;"); - sb.AppendLine(" }"); + // 2. IsIn(params ...) 方法 + sb.AppendLine(" /// Auto-generated: 判断是否属于指定集合"); + sb.AppendLine( + $" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (values == null) return false;"); + sb.AppendLine(" foreach (var v in values) if (value == v) return true;"); + sb.AppendLine(" return false;"); + sb.AppendLine(" }"); - sb.AppendLine(" }"); - sb.AppendLine("}"); // namespace + sb.AppendLine(" }"); + sb.AppendLine("}"); // namespace - return sb.ToString(); - } + return sb.ToString(); } } \ No newline at end of file diff --git a/GFramework.csproj b/GFramework.csproj index 3c6c5ff..478d4d1 100644 --- a/GFramework.csproj +++ b/GFramework.csproj @@ -25,37 +25,37 @@ - - - - + + + + - - - + + + - - - - - - + + + + + + - GFramework.Godot\godot\extensions\NodeExtensions.cs + GFramework.Godot\godot\extensions\NodeExtensions.cs - GFramework.Godot\godot\extensions\UnRegisterExtension.cs + GFramework.Godot\godot\extensions\UnRegisterExtension.cs - - - + + + + ReferenceOutputAssembly="false"/> diff --git a/README.md b/README.md index 8774a9b..aa10597 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # 项目介绍 + 本项目参考(CV)自[QFramework](https://github.com/liangxiegame/QFramework) # 为什么要有这个项目 + - 原来的项目是单文件框架,我把框架拆成多个文件,方便管理 - 纯粹个人自用,要使用还是请访问[QFramework](https://github.com/liangxiegame/QFramework) - 至于修改名字,是因为我为了方便会发布GuGet包,假设将来QFramework也要发布GuGet包,那么就会冲突了 \ No newline at end of file