commit 5aa11ddc41db0d62975a5726007c634887f84121
Author: GwWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Tue Dec 9 15:32:17 2025 +0800
feat(architecture): 添加架构核心组件与命令模式实现
- 新增 Architecture 基类与 IArchitecture 接口,实现单例模式与组件注册管理
- 集成 IOC 容器支持系统、模型、工具的依赖注入与生命周期管理
- 实现命令模式基础类 AbstractCommand 与接口 ICommand,支持带返回值命令
- 提供事件系统集成,支持事件的发布与订阅机制
- 添加控制器接口 IController,整合命令发送、事件注册与模型获取能力
- 创建详细的 README 文档说明各组件使用方式与设计模式应用
- 支持命令、查询、事件的统一调度与解耦通信机制
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..add57be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+bin/
+obj/
+/packages/
+riderModule.iml
+/_ReSharper.Caches/
\ No newline at end of file
diff --git a/GFramework.csproj b/GFramework.csproj
new file mode 100644
index 0000000..17b910f
--- /dev/null
+++ b/GFramework.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+
+
diff --git a/GFramework.sln b/GFramework.sln
new file mode 100644
index 0000000..1095a82
--- /dev/null
+++ b/GFramework.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework", "GFramework.csproj", "{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..667ef5f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# 项目介绍
+本项目参考(CV)自[QFramework](https://github.com/liangxiegame/QFramework)
+# 为什么要有这个项目
+- 原来的项目是单文件框架,我把框架拆成多个文件,方便管理
\ No newline at end of file
diff --git a/framework/README.md b/framework/README.md
new file mode 100644
index 0000000..91fd2a7
--- /dev/null
+++ b/framework/README.md
@@ -0,0 +1,1108 @@
+# Framework 架构框架
+
+> 一个基于 CQRS、MVC 和事件驱动的轻量级游戏开发架构框架,专为 Godot 引擎设计。
+
+## 📖 目录
+
+- [框架概述](#框架概述)
+- [核心概念](#核心概念)
+- [架构图](#架构图)
+- [快速开始](#快速开始)
+- [包说明](#包说明)
+- [组件联动](#组件联动)
+- [最佳实践](#最佳实践)
+- [示例项目](#示例项目)
+
+## 框架概述
+
+本框架是一个专为 Godot C# 游戏开发设计的轻量级架构,它结合了多种经典设计模式:
+
+- **MVC 架构模式** - 清晰的层次划分
+- **CQRS 模式** - 命令查询职责分离
+- **IoC/DI** - 依赖注入和控制反转
+- **事件驱动** - 松耦合的组件通信
+- **响应式编程** - 可绑定属性和数据流
+
+### 核心特性
+
+✅ **清晰的分层架构** - Model、View、Controller、System、Utility 各司其职
+✅ **类型安全** - 基于泛型的组件获取和事件系统
+✅ **松耦合** - 通过事件和接口实现组件解耦
+✅ **易于测试** - 依赖注入和纯函数设计
+✅ **可扩展** - 基于接口的规则体系
+✅ **生命周期管理** - 自动的注册和注销机制
+
+## 核心概念
+
+### 五层架构
+
+```
+┌─────────────────────────────────────────┐
+│ View (Godot Nodes) │ UI 层:Godot 节点
+├─────────────────────────────────────────┤
+│ Controller │ 控制层:处理用户输入
+├─────────────────────────────────────────┤
+│ System │ 逻辑层:业务逻辑
+├─────────────────────────────────────────┤
+│ Model │ 数据层:游戏状态
+├─────────────────────────────────────────┤
+│ Utility │ 工具层:无状态工具
+└─────────────────────────────────────────┘
+```
+
+### 横切关注点
+
+```
+Command ──┐
+Query ──┼──→ 跨层操作(修改/查询数据)
+Event ──┘
+```
+
+## 架构图
+
+### 整体架构
+
+```
+ ┌──────────────────┐
+ │ Architecture │ ← 单例,管理所有组件
+ └────────┬─────────┘
+ │
+ ┌────────────────────┼────────────────────┐
+ │ │ │
+ ┌───▼────┐ ┌───▼────┐ ┌───▼─────┐
+ │ Model │ │ System │ │ Utility │
+ │ 层 │ │ 层 │ │ 层 │
+ └───┬────┘ └───┬────┘ └────────┘
+ │ │
+ │ ┌─────────────┤
+ │ │ │
+ ┌───▼────▼───┐ ┌───▼──────┐
+ │ Controller │ │ Command/ │
+ │ 层 │ │ Query │
+ └─────┬──────┘ └──────────┘
+ │
+ ┌─────▼─────┐
+ │ View │
+ │ (Godot节点)│
+ └───────────┘
+```
+
+### 数据流向
+
+```
+用户输入 → Controller → Command → System → Model → Event → Controller → View 更新
+
+查询流程:Controller → Query → Model → 返回数据
+```
+
+## 快速开始
+
+本框架采用"约定优于配置"的设计理念,只需 4 步即可搭建完整的游戏架构。
+
+### 为什么需要这个框架?
+
+在传统的 Godot 开发中,我们经常遇到这些问题:
+- 💔 **代码耦合严重**:UI 直接访问游戏逻辑,逻辑直接操作 UI
+- 🔄 **难以维护**:修改一个功能需要改动多个文件
+- 🐛 **难以测试**:业务逻辑和 UI 混在一起无法独立测试
+- 📦 **难以复用**:代码紧密耦合,无法在其他项目中复用
+
+本框架通过清晰的分层解决这些问题。
+
+### 1. 定义架构(Architecture)
+
+**作用**:Architecture 是整个游戏的"中央调度器",负责管理所有组件的生命周期。
+
+```csharp
+using GFramework.framework.architecture;
+
+public class GameArchitecture : Architecture
+{
+ protected override void Init()
+ {
+ // 注册 Model - 游戏数据
+ this.RegisterModel(new PlayerModel());
+
+ // 注册 System - 业务逻辑
+ this.RegisterSystem(new CombatSystem());
+
+ // 注册 Utility - 工具类
+ this.RegisterUtility(new StorageUtility());
+ }
+}
+```
+
+**优势**:
+- ✅ **单例模式**:通过 `GameArchitecture.Interface` 全局访问
+- ✅ **自动初始化**:注册时自动调用组件的 Init 方法
+- ✅ **依赖注入**:组件自动获得架构引用,无需手动传递
+- ✅ **集中管理**:所有组件注册在一处,一目了然
+
+### 2. 定义 Model(数据层)
+
+**作用**:Model 是游戏的"数据库",只负责存储和管理游戏状态。
+
+```csharp
+public class PlayerModel : AbstractModel
+{
+ // 使用 BindableProperty 实现响应式数据
+ public BindableProperty Health { get; } = new(100);
+ public BindableProperty Gold { get; } = new(0);
+
+ protected override void OnInit()
+ {
+ // Model 中可以监听自己的数据变化
+ Health.Register(hp =>
+ {
+ if (hp <= 0) this.SendEvent(new PlayerDiedEvent());
+ });
+ }
+}
+//当然也可以不这样
+public class PlayerModel : AbstractModel
+{
+ public int Health { get;private set; }
+ public int Gold { get;private set; }
+
+ protected override void OnInit()
+ {
+ Health = 100;
+ Gold = 0;
+ }
+}
+```
+
+**优势**:
+- ✅ **数据响应式**:BindableProperty 让数据变化自动通知 UI
+- ✅ **职责单一**:只存储数据,不包含复杂业务逻辑
+- ✅ **易于测试**:可以独立测试数据逻辑
+- ✅ **数据持久化**:可以轻松序列化整个 Model 进行存档
+
+**为什么不在 Model 中写业务逻辑?**
+- 保持 Model 简单纯粹,业务逻辑应该在 System 中处理
+- Model 应该是"被动"的,等待其他组件修改它
+
+### 3. 定义 System(业务逻辑层)
+
+**作用**:System 是游戏的"大脑",处理所有业务逻辑。
+
+```csharp
+public class CombatSystem : AbstractSystem
+{
+ protected override void OnInit()
+ {
+ // System 通过事件驱动,响应游戏中的各种事件
+ this.RegisterEvent(OnEnemyAttack);
+ }
+
+ private void OnEnemyAttack(EnemyAttackEvent e)
+ {
+ var playerModel = this.GetModel();
+
+ // 处理业务逻辑:计算伤害、更新数据
+ playerModel.Health.Value -= e.Damage;
+
+ // 发送事件通知其他组件
+ this.SendEvent(new PlayerTookDamageEvent { Damage = e.Damage });
+ }
+}
+```
+
+**优势**:
+- ✅ **事件驱动**:通过事件解耦,不同 System 之间松耦合
+- ✅ **可组合**:多个 System 协同工作,每个专注自己的领域
+- ✅ **易于扩展**:新增功能只需添加新的 System 和事件监听
+- ✅ **独立测试**:可以模拟事件来测试 System 的逻辑
+
+**System 与 Model 的关系**:
+- System 读取和修改 Model 的数据
+- System 不应该存储重要的游戏状态(状态应在 Model 中)
+- System 可以存储临时的计算结果或缓存
+
+### 4. 定义 Controller(控制层)
+
+**作用**:Controller 是"桥梁",连接 UI(View)和业务逻辑。
+
+```csharp
+public partial class PlayerController : Node, IController
+{
+ [Export] private Label _healthLabel;
+ private IUnRegisterList _unregisterList = new UnRegisterList();
+
+ // 实现接口,连接到架构
+ public IArchitecture GetArchitecture() => GameArchitecture.Interface;
+
+ public override void _Ready()
+ {
+ var playerModel = this.GetModel();
+
+ // 数据绑定:Model 数据变化自动更新 UI
+ playerModel.Health
+ .RegisterWithInitValue(hp => _healthLabel.Text = $"HP: {hp}")
+ .AddToUnregisterList(_unregisterList);
+ }
+
+ public override void _ExitTree()
+ {
+ // 重要:节点销毁时注销所有监听,避免内存泄漏
+ _unregisterList.UnRegisterAll();
+ }
+}
+```
+
+**优势**:
+- ✅ **自动更新 UI**:通过 BindableProperty,数据变化自动反映到界面
+- ✅ **生命周期管理**:自动注销监听,避免内存泄漏
+- ✅ **分离关注点**:UI 逻辑和业务逻辑完全分离
+- ✅ **易于修改 UI**:改变 UI 不影响业务逻辑
+
+**为什么使用 RegisterWithInitValue?**
+- 注册监听时立即获得当前值,避免 UI 显示空白
+- 后续数据变化会自动触发更新
+
+### 完成!现在你有了一个完整的架构
+
+这 4 步完成后,你就拥有了:
+- 📦 **清晰的数据层**(Model)
+- 🧠 **独立的业务逻辑**(System)
+- 🎮 **灵活的控制层**(Controller)
+- 🔧 **统一的生命周期管理**(Architecture)
+
+### 下一步该做什么?
+
+1. **添加 Command**:封装用户操作(如购买物品、使用技能)
+2. **添加 Query**:封装数据查询(如查询背包物品数量)
+3. **添加更多 System**:如任务系统、背包系统、商店系统
+4. **使用 Utility**:添加工具类(如存档工具、数学工具)
+
+## 包说明
+
+| 包名 | 职责 | 文档 |
+|-----|------|------|
+| **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) |
+
+## 组件联动
+
+### 1. 初始化流程
+
+```
+Architecture.Interface
+ └─> Init()
+ ├─> RegisterModel → Model.SetArchitecture() → Model.Init()
+ ├─> RegisterSystem → System.SetArchitecture() → System.Init()
+ └─> RegisterUtility (无需初始化)
+```
+
+### 2. Command 执行流程
+
+```
+Controller.SendCommand(command)
+ └─> command.SetArchitecture(architecture) // 自动注入
+ └─> command.Execute()
+ └─> command.OnExecute() // 子类实现
+ ├─> GetModel() // 获取数据
+ ├─> 修改 Model 数据
+ └─> SendEvent() // 发送事件
+```
+
+### 3. Event 传播流程
+
+```
+组件.SendEvent(event)
+ └─> TypeEventSystem.Send(event)
+ └─> 通知所有订阅者
+ ├─> Controller 响应 → 更新 UI
+ ├─> System 响应 → 执行逻辑
+ └─> Model 响应 → 更新状态
+```
+
+### 4. BindableProperty 数据绑定
+
+```
+Model: BindableProperty Health = new(100);
+Controller: Health.RegisterWithInitValue(hp => UpdateUI(hp))
+修改值: Health.Value = 50 → 触发所有回调 → UI 自动更新
+```
+
+## 最佳实践
+
+掌握这些最佳实践,能让你充分发挥框架的优势,避免常见陷阱。
+
+### 1. 分层职责原则 📋
+
+每一层都有明确的职责边界,遵循这些原则能让代码更清晰、更易维护。
+
+#### ✅ 好的实践 vs ❌ 坏的实践
+
+**Model 层**:
+```csharp
+// ✅ 好:只存储数据
+public class PlayerModel : AbstractModel
+{
+ public BindableProperty Health { get; } = new(100);
+ public BindableProperty MaxHealth { get; } = new(100);
+ protected override void OnInit() { }
+}
+
+// ❌ 坏:包含业务逻辑
+public class PlayerModel : AbstractModel
+{
+ public void TakeDamage(int damage) // ❌ 业务逻辑应在 System
+ {
+ Health.Value -= damage;
+ if (Health.Value <= 0) Die();
+ }
+}
+```
+
+**System 层**:
+```csharp
+// ✅ 好:处理业务逻辑
+public class CombatSystem : AbstractSystem
+{
+ protected override void OnInit()
+ {
+ this.RegisterEvent(OnAttack);
+ }
+
+ private void OnAttack(AttackEvent e)
+ {
+ // 业务逻辑在这里
+ var target = this.GetModel();
+ int finalDamage = CalculateDamage(e.BaseDamage, target);
+ target.Health.Value -= finalDamage;
+ }
+}
+
+// ❌ 坏:直接操作 UI
+public class CombatSystem : AbstractSystem
+{
+ private Label _damageLabel; // ❌ 不应持有 UI 引用
+
+ private void OnAttack(AttackEvent e)
+ {
+ _damageLabel.Text = $"-{e.Damage}"; // ❌ 应通过事件通知
+ }
+}
+```
+
+**Controller 层**:
+```csharp
+// ✅ 好:只处理 UI 和用户输入
+public partial class AttackButton : Button, IController
+{
+ public override void _Ready()
+ {
+ Pressed += () => this.SendCommand(new AttackCommand());
+ }
+}
+
+// ❌ 坏:包含业务逻辑
+public partial class AttackButton : Button, IController
+{
+ public override void _Ready()
+ {
+ Pressed += () =>
+ {
+ var player = this.GetModel();
+ var enemy = this.GetModel();
+
+ // ❌ 这些业务逻辑应该在 System/Command 中
+ int damage = player.AttackPower.Value - enemy.Defense.Value;
+ enemy.Health.Value -= damage;
+ };
+ }
+}
+```
+
+### 2. 通信方式选择指南 💬
+
+不同的通信方式适用于不同场景,选对方式能让代码更优雅。
+
+| 通信方式 | 使用场景 | 示例 | 优势 |
+|---------|---------|------|------|
+| **Command** | 用户操作、修改状态 | 购买物品、攻击敌人 | 可撤销、可记录 |
+| **Query** | 查询数据、检查条件 | 查询金币数量、检查是否可购买 | 明确只读意图 |
+| **Event** | 通知其他组件 | 玩家死亡、物品拾取 | 松耦合、可扩展 |
+| **BindableProperty** | 数据→UI 自动同步 | 生命值变化更新血条 | 自动化、不会遗漏 |
+
+**实战示例:购物系统**
+
+```csharp
+// 1. Controller:用户点击购买按钮
+public void OnBuyButtonPressed()
+{
+ // 先用 Query 检查条件
+ bool canBuy = this.SendQuery(new CanBuyItemQuery
+ {
+ ItemId = "sword",
+ Price = 100
+ });
+
+ if (canBuy)
+ {
+ // 使用 Command 执行操作
+ this.SendCommand(new BuyItemCommand { ItemId = "sword" });
+ }
+ else
+ {
+ ShowMessage("金币不足!");
+ }
+}
+
+// 2. Query:检查是否可以购买
+public class CanBuyItemQuery : AbstractQuery
+{
+ public string ItemId { get; set; }
+ public int Price { get; set; }
+
+ protected override bool OnDo()
+ {
+ var player = this.GetModel();
+ return player.Gold.Value >= Price;
+ }
+}
+
+// 3. Command:执行购买
+public class BuyItemCommand : AbstractCommand
+{
+ public string ItemId { get; set; }
+
+ protected override void OnExecute()
+ {
+ var player = this.GetModel();
+ var shop = this.GetModel();
+
+ int price = shop.GetPrice(ItemId);
+ player.Gold.Value -= price;
+
+ // 发送事件通知
+ this.SendEvent(new ItemPurchasedEvent { ItemId = ItemId });
+ }
+}
+
+// 4. System:响应购买事件
+public class InventorySystem : AbstractSystem
+{
+ protected override void OnInit()
+ {
+ this.RegisterEvent(OnItemPurchased);
+ }
+
+ private void OnItemPurchased(ItemPurchasedEvent e)
+ {
+ var inventory = this.GetModel();
+ inventory.AddItem(e.ItemId);
+ }
+}
+
+// 5. Controller:通过 BindableProperty 自动更新 UI
+public partial class GoldDisplay : Label, IController
+{
+ public override void _Ready()
+ {
+ var player = this.GetModel();
+
+ // 金币变化自动更新 UI
+ player.Gold
+ .RegisterWithInitValue(gold => Text = $"金币: {gold}")
+ .UnRegisterWhenNodeExitTree(this);
+ }
+}
+```
+
+### 3. 生命周期管理 ♻️
+
+**为什么需要注销?**
+
+忘记注销监听器会导致:
+- 💥 **内存泄漏**:对象无法被 GC 回收
+- 🐛 **逻辑错误**:已销毁的对象仍在响应事件
+- 📉 **性能下降**:无用的监听器消耗资源
+
+#### 方式一:自动注销(推荐用于 Godot 节点)
+
+```csharp
+public override void _Ready()
+{
+ // 节点退出场景树时自动注销
+ this.RegisterEvent(OnEvent)
+ .UnRegisterWhenNodeExitTree(this);
+
+ playerModel.Health
+ .RegisterWithInitValue(UpdateHealthBar)
+ .UnRegisterWhenNodeExitTree(this);
+}
+```
+
+**优势**:一行代码搞定,不会忘记
+
+#### 方式二:统一管理(推荐用于多个监听器)
+
+```csharp
+private IUnRegisterList _unregisterList = new UnRegisterList();
+
+public override void _Ready()
+{
+ // 所有监听器统一管理
+ this.RegisterEvent(OnEvent1)
+ .AddToUnregisterList(_unregisterList);
+
+ this.RegisterEvent(OnEvent2)
+ .AddToUnregisterList(_unregisterList);
+
+ playerModel.Health
+ .RegisterWithInitValue(UpdateUI)
+ .AddToUnregisterList(_unregisterList);
+}
+
+public override void _ExitTree()
+{
+ // 一次性注销所有
+ _unregisterList.UnRegisterAll();
+}
+```
+
+**优势**:集中管理,清晰明了
+
+#### 方式三:手动注销(需要精确控制时机)
+
+```csharp
+private IUnRegister _healthUnregister;
+
+public override void _Ready()
+{
+ _healthUnregister = playerModel.Health.Register(UpdateUI);
+}
+
+public void StopListening()
+{
+ // 在特定时机手动注销
+ _healthUnregister?.UnRegister();
+ _healthUnregister = null;
+}
+```
+
+**优势**:可以在任何时候注销
+
+### 4. 性能优化技巧 ⚡
+
+#### 技巧 1:缓存组件引用
+
+```csharp
+// ❌ 低效:每帧都查询
+public override void _Process(double delta)
+{
+ var model = this.GetModel(); // 每帧查询 IoC 容器
+ UpdateUI(model.Health.Value);
+}
+
+// ✅ 高效:只查询一次
+private PlayerModel _playerModel;
+
+public override void _Ready()
+{
+ _playerModel = this.GetModel(); // 只查询一次
+}
+
+public override void _Process(double delta)
+{
+ UpdateUI(_playerModel.Health.Value);
+}
+```
+
+**原因**:GetModel 需要查询 IoC 容器,虽然很快但不必要重复查询
+
+#### 技巧 2:批量操作避免频繁触发
+
+```csharp
+// ❌ 低效:每次修改都触发事件
+foreach (var item in 100items)
+{
+ inventory.AddItem(item); // 触发100次 UI 更新!
+}
+
+// ✅ 高效:批量操作后统一通知
+foreach (var item in items)
+{
+ inventory.Items.SetValueWithoutEvent(
+ inventory.Items.Value + item
+ );
+}
+// 最后统一触发一次
+this.SendEvent(new InventoryChangedEvent());
+```
+
+**原因**:每次数据变化都会触发所有监听器,批量操作会导致性能问题
+
+#### 技巧 3:使用对象池
+
+```csharp
+// 在 Utility 中实现对象池
+public class PoolUtility : IUtility
+{
+ private Dictionary> _pools = new();
+
+ public T Get() where T : new()
+ {
+ var type = typeof(T);
+ if (_pools.ContainsKey(type) && _pools[type].Count > 0)
+ return (T)_pools[type].Dequeue();
+ return new T();
+ }
+
+ public void Return(T obj)
+ {
+ var type = typeof(T);
+ if (!_pools.ContainsKey(type))
+ _pools[type] = new Queue