feat(architecture): 添加架构核心组件与命令模式实现

- 新增 Architecture 基类与 IArchitecture 接口,实现单例模式与组件注册管理
- 集成 IOC 容器支持系统、模型、工具的依赖注入与生命周期管理
- 实现命令模式基础类 AbstractCommand 与接口 ICommand,支持带返回值命令
- 提供事件系统集成,支持事件的发布与订阅机制
- 添加控制器接口 IController,整合命令发送、事件注册与模型获取能力
- 创建详细的 README 文档说明各组件使用方式与设计模式应用
- 支持命令、查询、事件的统一调度与解耦通信机制
This commit is contained in:
GwWuYou 2025-12-09 15:32:17 +08:00
commit 5aa11ddc41
59 changed files with 7418 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

9
GFramework.csproj Normal file
View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

16
GFramework.sln Normal file
View File

@ -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

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# 项目介绍
本项目参考(CV)自[QFramework](https://github.com/liangxiegame/QFramework)
# 为什么要有这个项目
- 原来的项目是单文件框架,我把框架拆成多个文件,方便管理

1108
framework/README.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,268 @@
using GFramework.framework.command;
using GFramework.framework.events;
using GFramework.framework.ioc;
using GFramework.framework.model;
using GFramework.framework.query;
using GFramework.framework.system;
using GFramework.framework.utility;
namespace GFramework.framework.architecture;
/// <summary>
/// 架构基类,提供系统、模型、工具等组件的注册与管理功能。
/// 使用单例模式确保全局唯一实例,并支持命令、查询和事件机制。
/// </summary>
/// <typeparam name="T">派生类类型,用于实现单例</typeparam>
public abstract class Architecture<T> : IArchitecture where T : Architecture<T>, new()
{
/// <summary>
/// 标记架构是否已初始化完成
/// </summary>
private bool _mInited;
/// <summary>
/// 存储尚未初始化的系统集合在初始化阶段统一调用Init方法
/// </summary>
private readonly HashSet<ISystem> _mSystems = [];
/// <summary>
/// 存储尚未初始化的模型集合在初始化阶段统一调用Init方法
/// </summary>
private readonly HashSet<IModel> _mModels = [];
/// <summary>
/// 注册补丁委托,允许在架构创建后执行额外逻辑
/// </summary>
public static Action<T> OnRegisterPatch { get; set; } = _ => { };
/// <summary>
/// 静态只读字段,用于延迟初始化架构实例
/// 使用Lazy确保线程安全的单例模式实现
/// </summary>
/// <remarks>
/// 初始化过程包括:
/// 1. 创建T类型的实例
/// 2. 调用用户自定义的Init方法
/// 3. 执行注册的补丁逻辑
/// 4. 初始化所有已注册的模型和系统
/// 5. 清理临时集合并标记初始化完成
/// </remarks>
/// <returns>T类型的架构实例</returns>
private static readonly Lazy<T> MArchitectureLazy = new(() =>
{
var arch = new T();
// 调用用户实现的初始化
arch.Init();
// 执行注册的补丁逻辑
OnRegisterPatch?.Invoke(arch);
// 初始化所有已注册但尚未初始化的模型
foreach (var model in arch._mModels)
{
model.Init();
}
arch._mModels.Clear();
// 初始化所有已注册但尚未初始化的系统
foreach (var system in arch._mSystems)
{
system.Init();
}
arch._mSystems.Clear();
arch._mInited = true;
return arch;
}, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
/// <summary>
/// 获取架构实例的受保护静态属性
/// 通过Lazy初始化确保只创建一个实例
/// </summary>
/// <returns>T类型的架构实例</returns>
protected static T MArchitecture => MArchitectureLazy.Value;
/// <summary>
/// 获取架构实例的公共静态属性,以接口形式暴露
/// 提供对外访问架构功能的标准接口
/// </summary>
/// <returns>IArchitecture接口类型的架构实例</returns>
public static IArchitecture Interface => MArchitectureLazy.Value;
/// <summary>
/// 抽象初始化方法,由子类重写以进行自定义初始化操作
/// </summary>
protected abstract void Init();
/// <summary>
/// 控制反转容器,用于存储和获取各种服务(如系统、模型、工具)
/// </summary>
private readonly IocContainer _mContainer = new();
/// <summary>
/// 注册一个系统到架构中。
/// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该系统。
/// </summary>
/// <typeparam name="TSystem">要注册的系统类型</typeparam>
/// <param name="system">要注册的系统实例</param>
public void RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
{
system.SetArchitecture(this);
_mContainer.Register(system);
if (!_mInited)
{
_mSystems.Add(system);
}
else
{
system.Init();
}
}
/// <summary>
/// 注册一个模型到架构中。
/// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该模型。
/// </summary>
/// <typeparam name="TModel">要注册的模型类型</typeparam>
/// <param name="model">要注册的模型实例</param>
public void RegisterModel<TModel>(TModel model) where TModel : IModel
{
model.SetArchitecture(this);
_mContainer.Register(model);
if (!_mInited)
{
_mModels.Add(model);
}
else
{
model.Init();
}
}
/// <summary>
/// 注册一个工具到架构中。
/// 工具不会被延迟初始化直接放入IOC容器供后续使用。
/// </summary>
/// <typeparam name="TUtility">要注册的工具类型</typeparam>
/// <param name="utility">要注册的工具实例</param>
public void RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility =>
_mContainer.Register(utility);
/// <summary>
/// 从IOC容器中获取指定类型的系统实例
/// </summary>
/// <typeparam name="TSystem">目标系统类型</typeparam>
/// <returns>对应的系统实例</returns>
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem => _mContainer.Get<TSystem>();
/// <summary>
/// 从IOC容器中获取指定类型的模型实例
/// </summary>
/// <typeparam name="TModel">目标模型类型</typeparam>
/// <returns>对应的模型实例</returns>
public TModel GetModel<TModel>() where TModel : class, IModel => _mContainer.Get<TModel>();
/// <summary>
/// 从IOC容器中获取指定类型的工具实例
/// </summary>
/// <typeparam name="TUtility">目标工具类型</typeparam>
/// <returns>对应的工具实例</returns>
public TUtility GetUtility<TUtility>() where TUtility : class, IUtility => _mContainer.Get<TUtility>();
/// <summary>
/// 发送一个带返回结果的命令请求
/// </summary>
/// <typeparam name="TResult">命令执行后的返回值类型</typeparam>
/// <param name="command">要发送的命令对象</param>
/// <returns>命令执行的结果</returns>
public TResult SendCommand<TResult>(ICommand<TResult> command) => ExecuteCommand(command);
/// <summary>
/// 发送一个无返回结果的命令请求
/// </summary>
/// <typeparam name="TCommand">命令的具体类型</typeparam>
/// <param name="command">要发送的命令对象</param>
public void SendCommand<TCommand>(TCommand command) where TCommand : ICommand => ExecuteCommand(command);
/// <summary>
/// 执行一个带返回结果的命令
/// </summary>
/// <typeparam name="TResult">命令执行后的返回值类型</typeparam>
/// <param name="command">要执行的命令对象</param>
/// <returns>命令执行的结果</returns>
protected virtual TResult ExecuteCommand<TResult>(ICommand<TResult> command)
{
command.SetArchitecture(this);
return command.Execute();
}
/// <summary>
/// 执行一个无返回结果的命令
/// </summary>
/// <param name="command">要执行的命令对象</param>
protected virtual void ExecuteCommand(ICommand command)
{
command.SetArchitecture(this);
command.Execute();
}
/// <summary>
/// 发起一次查询请求并获得其结果
/// </summary>
/// <typeparam name="TResult">查询结果的数据类型</typeparam>
/// <param name="query">要发起的查询对象</param>
/// <returns>查询得到的结果数据</returns>
public TResult SendQuery<TResult>(IQuery<TResult> query) => DoQuery(query);
/// <summary>
/// 实际执行查询逻辑的方法
/// </summary>
/// <typeparam name="TResult">查询结果的数据类型</typeparam>
/// <param name="query">要处理的查询对象</param>
/// <returns>查询结果</returns>
protected virtual TResult DoQuery<TResult>(IQuery<TResult> query)
{
query.SetArchitecture(this);
return query.Do();
}
/// <summary>
/// 类型化事件系统,负责事件的发布与订阅管理
/// </summary>
private readonly TypeEventSystem _mTypeEventSystem = new();
/// <summary>
/// 发布一个默认构造的新事件对象
/// </summary>
/// <typeparam name="TEvent">事件类型</typeparam>
public void SendEvent<TEvent>() where TEvent : new() => _mTypeEventSystem.Send<TEvent>();
/// <summary>
/// 发布一个具体的事件对象
/// </summary>
/// <typeparam name="TEvent">事件类型</typeparam>
/// <param name="e">要发布的事件实例</param>
public void SendEvent<TEvent>(TEvent e) => _mTypeEventSystem.Send(e);
/// <summary>
/// 订阅某个特定类型的事件
/// </summary>
/// <typeparam name="TEvent">事件类型</typeparam>
/// <param name="onEvent">当事件发生时触发的动作</param>
/// <returns>可用于取消订阅的对象</returns>
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> onEvent) => _mTypeEventSystem.Register(onEvent);
/// <summary>
/// 取消对某类型事件的监听
/// </summary>
/// <typeparam name="TEvent">事件类型</typeparam>
/// <param name="onEvent">之前绑定的事件处理器</param>
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent) => _mTypeEventSystem.UnRegister(onEvent);
}

View File

@ -0,0 +1,110 @@

using GFramework.framework.command;
using GFramework.framework.events;
using GFramework.framework.model;
using GFramework.framework.query;
using GFramework.framework.system;
using GFramework.framework.utility;
namespace GFramework.framework.architecture;
/// <summary>
/// 架构接口,定义了应用程序架构的核心功能,包括系统、模型、工具的注册和获取,
/// 以及命令、查询、事件的发送和处理机制
/// </summary>
public interface IArchitecture
{
/// <summary>
/// 注册系统实例到架构中
/// </summary>
/// <typeparam name="T">系统类型必须实现ISystem接口</typeparam>
/// <param name="system">要注册的系统实例</param>
void RegisterSystem<T>(T system) where T : ISystem;
/// <summary>
/// 注册模型实例到架构中
/// </summary>
/// <typeparam name="T">模型类型必须实现IModel接口</typeparam>
/// <param name="model">要注册的模型实例</param>
void RegisterModel<T>(T model) where T : IModel;
/// <summary>
/// 注册工具实例到架构中
/// </summary>
/// <typeparam name="T">工具类型必须实现IUtility接口</typeparam>
/// <param name="utility">要注册的工具实例</param>
void RegisterUtility<T>(T utility) where T : IUtility;
/// <summary>
/// 从架构中获取指定类型的系统实例
/// </summary>
/// <typeparam name="T">系统类型必须是class且实现ISystem接口</typeparam>
/// <returns>指定类型的系统实例</returns>
T GetSystem<T>() where T : class, ISystem;
/// <summary>
/// 从架构中获取指定类型的模型实例
/// </summary>
/// <typeparam name="T">模型类型必须是class且实现IModel接口</typeparam>
/// <returns>指定类型的模型实例</returns>
T GetModel<T>() where T : class, IModel;
/// <summary>
/// 从架构中获取指定类型的工具实例
/// </summary>
/// <typeparam name="T">工具类型必须是class且实现IUtility接口</typeparam>
/// <returns>指定类型的工具实例</returns>
T GetUtility<T>() where T : class, IUtility;
/// <summary>
/// 发送并执行指定的命令
/// </summary>
/// <typeparam name="T">命令类型必须实现ICommand接口</typeparam>
/// <param name="command">要执行的命令实例</param>
void SendCommand<T>(T command) where T : ICommand;
/// <summary>
/// 发送并执行带有返回值的命令
/// </summary>
/// <typeparam name="TResult">命令执行结果的类型</typeparam>
/// <param name="command">要执行的命令实例</param>
/// <returns>命令执行的结果</returns>
TResult SendCommand<TResult>(ICommand<TResult> command);
/// <summary>
/// 发送并执行查询操作
/// </summary>
/// <typeparam name="TResult">查询结果的类型</typeparam>
/// <param name="query">要执行的查询实例</param>
/// <returns>查询的结果</returns>
TResult SendQuery<TResult>(IQuery<TResult> query);
/// <summary>
/// 发送无参事件
/// </summary>
/// <typeparam name="T">事件类型,必须具有无参构造函数</typeparam>
void SendEvent<T>() where T : new();
/// <summary>
/// 发送指定的事件实例
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="e">要发送的事件实例</param>
void SendEvent<T>(T e);
/// <summary>
/// 注册事件监听器
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="onEvent">事件触发时的回调方法</param>
/// <returns>用于取消注册的句柄</returns>
IUnRegister RegisterEvent<T>(Action<T> onEvent);
/// <summary>
/// 取消注册事件监听器
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="onEvent">要取消注册的事件回调方法</param>
void UnRegisterEvent<T>(Action<T> onEvent);
}

View File

@ -0,0 +1,169 @@
# Architecture 包使用说明
## 概述
Architecture 包是整个框架的核心,提供了基于 MVC 架构模式的应用程序架构基础。它实现了依赖注入IoC容器、组件生命周期管理以及命令、查询、事件的统一调度机制。
## 核心类
### 1. [`IArchitecture`](IArchitecture.cs)
架构接口,定义了框架的核心功能契约。
**主要职责:**
- 组件注册:注册 System、Model、Utility
- 组件获取:从容器中获取已注册的组件
- 命令处理:发送并执行命令
- 查询处理:发送并执行查询
- 事件管理:发送、注册、注销事件
**核心方法:**
```csharp
// 注册组件
void RegisterSystem<T>(T system) where T : ISystem;
void RegisterModel<T>(T model) where T : IModel;
void RegisterUtility<T>(T utility) where T : IUtility;
// 获取组件
T GetSystem<T>() where T : class, ISystem;
T GetModel<T>() where T : class, IModel;
T GetUtility<T>() where T : class, IUtility;
// 命令处理
void SendCommand<T>(T command) where T : ICommand;
TResult SendCommand<TResult>(ICommand<TResult> command);
// 查询处理
TResult SendQuery<TResult>(IQuery<TResult> query);
// 事件管理
void SendEvent<T>() where T : new();
void SendEvent<T>(T e);
IUnRegister RegisterEvent<T>(Action<T> onEvent);
void UnRegisterEvent<T>(Action<T> onEvent);
```
### 2. [`Architecture<T>`](Architecture.cs)
架构基类,实现了 [`IArchitecture`](IArchitecture.cs) 接口,提供完整的架构功能实现。
**特性:**
- **单例模式**:使用泛型和 `Lazy<T>` 确保全局唯一实例
- **线程安全**:采用 `LazyThreadSafetyMode.ExecutionAndPublication` 保证多线程安全
- **生命周期管理**:自动管理 System 和 Model 的初始化顺序
- **IoC 容器**:内置依赖注入容器,管理所有组件实例
- **事件系统**:集成类型化事件系统,支持类型安全的事件通信
**初始化流程:**
1. 创建架构实例
2. 调用用户自定义的 `Init()` 方法
3. 执行注册的补丁逻辑(`OnRegisterPatch`
4. 初始化所有已注册的 Model
5. 初始化所有已注册的 System
6. 标记初始化完成
**使用示例:**
```csharp
// 1. 定义你的架构
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 注册 Model
RegisterModel(new PlayerModel());
RegisterModel(new InventoryModel());
// 注册 System
RegisterSystem(new GameplaySystem());
RegisterSystem(new SaveSystem());
// 注册 Utility
RegisterUtility(new StorageUtility());
RegisterUtility(new TimeUtility());
}
}
// 2. 在其他地方使用架构
public class GameController : IController
{
private IArchitecture _architecture;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void Start()
{
// 获取 Model
var playerModel = this.GetModel<PlayerModel>();
// 发送命令
this.SendCommand<StartGameCommand>();
// 发送查询
var score = this.SendQuery(new GetScoreQuery());
// 注册事件
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
}
private void OnPlayerDied(PlayerDiedEvent e)
{
// 处理玩家死亡事件
}
}
```
**高级特性:**
```csharp
// 动态扩展架构(补丁系统)
Architecture<GameArchitecture>.OnRegisterPatch += arch =>
{
// 在架构初始化完成前注入额外逻辑
arch.RegisterSystem(new DebugSystem());
};
// 在运行时动态注册组件(初始化后)
var newSystem = new DynamicSystem();
GameArchitecture.Interface.RegisterSystem(newSystem);
// newSystem.Init() 会被立即调用
```
## 设计模式
### 单例模式
通过泛型约束和 `Lazy<T>` 实现类型安全的单例。
### 依赖注入IoC
使用内置 IoC 容器管理组件生命周期和依赖关系。
### 命令模式
通过 [`ICommand`](../command/ICommand.cs) 封装所有用户操作。
### 查询模式CQRS
通过 [`IQuery<T>`](../query/IQuery.cs) 分离查询和命令操作。
### 观察者模式
通过事件系统实现组件间的松耦合通信。
## 最佳实践
1. **保持架构类简洁**:只在 `Init()` 中注册组件,不要包含业务逻辑
2. **合理划分职责**
- Model数据和状态
- System业务逻辑和规则
- Utility无状态的工具方法
3. **使用接口访问**:通过 `Interface` 属性访问架构,便于测试
4. **事件命名规范**:使用过去式命名事件类,如 `PlayerDiedEvent`
5. **避免循环依赖**System 不应直接引用 System应通过事件通信
## 相关包
- [`command`](../command/README.md) - 命令模式实现
- [`query`](../query/README.md) - 查询模式实现
- [`events`](../events/README.md) - 事件系统
- [`ioc`](../ioc/README.md) - IoC 容器
- [`model`](../model/README.md) - 数据模型
- [`system`](../system/README.md) - 业务系统
- [`utility`](../utility/README.md) - 工具类

View File

@ -0,0 +1,68 @@
using GFramework.framework.architecture;
using GFramework.framework.rule;
namespace GFramework.framework.command;
/// <summary>
/// 抽象命令类,实现 ICommand 接口,为具体命令提供基础架构支持
/// </summary>
public abstract class AbstractCommand : ICommand
{
private IArchitecture _mArchitecture;
/// <summary>
/// 获取命令所属的架构实例
/// </summary>
/// <returns>IArchitecture 架构接口实例</returns>
IArchitecture IBelongToArchitecture.GetArchitecture() => _mArchitecture;
/// <summary>
/// 设置命令所属的架构实例
/// </summary>
/// <param name="architecture">要设置的架构实例</param>
void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture;
/// <summary>
/// 执行命令,调用抽象方法 OnExecute 来实现具体逻辑
/// </summary>
void ICommand.Execute() => OnExecute();
/// <summary>
/// 抽象方法,由子类实现具体的命令执行逻辑
/// </summary>
protected abstract void OnExecute();
}
/// <summary>
/// 带返回值的抽象命令类,实现 ICommand{TResult} 接口,为需要返回结果的命令提供基础架构支持
/// </summary>
/// <typeparam name="TResult">命令执行后返回的结果类型</typeparam>
public abstract class AbstractCommand<TResult> : ICommand<TResult>
{
private IArchitecture _mArchitecture;
/// <summary>
/// 获取命令所属的架构实例
/// </summary>
/// <returns>IArchitecture 架构接口实例</returns>
IArchitecture IBelongToArchitecture.GetArchitecture() => _mArchitecture;
/// <summary>
/// 设置命令所属的架构实例
/// </summary>
/// <param name="architecture">要设置的架构实例</param>
void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture;
/// <summary>
/// 执行命令,调用抽象方法 OnExecute 来实现具体逻辑并返回结果
/// </summary>
/// <returns>TResult 类型的执行结果</returns>
TResult ICommand<TResult>.Execute() => OnExecute();
/// <summary>
/// 抽象方法,由子类实现具体的命令执行逻辑
/// </summary>
/// <returns>TResult 类型的执行结果</returns>
protected abstract TResult OnExecute();
}

View File

@ -0,0 +1,9 @@
using GFramework.framework.rule;
namespace GFramework.framework.command;
/// <summary>
/// 定义一个可以发送命令的接口继承自IBelongToArchitecture接口。
/// 该接口用于标识那些具备发送命令能力的架构组件。
/// </summary>
public interface ICanSendCommand : IBelongToArchitecture;

View File

@ -0,0 +1,39 @@
using GFramework.framework.events;
using GFramework.framework.model;
using GFramework.framework.query;
using GFramework.framework.rule;
using GFramework.framework.system;
using GFramework.framework.utility;
namespace GFramework.framework.command;
/// <summary>
/// 命令接口,定义了无返回值命令的基本契约
/// 该接口继承了多个框架能力接口,使命令可以访问架构、系统、模型、工具,并能够发送事件、命令和查询
/// </summary>
public interface ICommand : ICanSetArchitecture, ICanGetSystem, ICanGetModel, ICanGetUtility,
ICanSendEvent, ICanSendCommand, ICanSendQuery
{
/// <summary>
/// 执行命令的核心方法
/// 该方法不接受参数且无返回值,具体实现由派生类完成
/// </summary>
void Execute();
}
/// <summary>
/// 带返回值的命令接口,定义了有返回值命令的基本契约
/// 该接口继承了多个框架能力接口,使命令可以访问架构、系统、模型、工具,并能够发送事件、命令和查询
/// </summary>
/// <typeparam name="TResult">命令执行后返回的结果类型</typeparam>
public interface ICommand<out TResult> : ICanSetArchitecture, ICanGetSystem, ICanGetModel,
ICanGetUtility,
ICanSendEvent, ICanSendCommand, ICanSendQuery
{
/// <summary>
/// 执行命令的核心方法
/// 该方法不接受参数,但会返回指定类型的结果
/// </summary>
/// <returns>命令执行后的结果,类型为 TResult</returns>
TResult Execute();
}

293
framework/command/README.md Normal file
View File

@ -0,0 +1,293 @@
# Command 包使用说明
## 概述
Command 包实现了命令模式Command Pattern用于封装用户操作和业务逻辑。通过命令模式可以将请求封装为对象实现操作的参数化、队列化、日志记录、撤销等功能。
## 核心接口
### 1. [`ICommand`](ICommand.cs)
无返回值命令接口,定义了命令的基本契约。
**继承的能力接口:**
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构
- [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统
- [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
- [`ICanSendCommand`](ICanSendCommand.cs) - 可发送命令
- [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询
**核心方法:**
```csharp
void Execute(); // 执行命令
```
### 2. [`ICommand<TResult>`](ICommand.cs)
带返回值的命令接口,用于需要返回执行结果的命令。
**核心方法:**
```csharp
TResult Execute(); // 执行命令并返回结果
```
### 3. [`ICanSendCommand`](ICanSendCommand.cs)
标记接口,表示实现者可以发送命令。继承自 [`IBelongToArchitecture`](../rule/IBelongToArchitecture.cs)。
## 核心类
### 1. [`AbstractCommand`](AbstractCommand.cs)
无返回值命令的抽象基类,提供了命令的基础实现。
**使用示例:**
```csharp
// 定义一个开始游戏的命令
public class StartGameCommand : AbstractCommand
{
protected override void OnExecute()
{
// 获取需要的模型
var playerModel = this.GetModel<PlayerModel>();
var gameModel = this.GetModel<GameModel>();
// 执行业务逻辑
playerModel.Health.Value = 100;
gameModel.GameState.Value = GameState.Playing;
// 发送事件通知其他模块
this.SendEvent(new GameStartedEvent());
}
}
// 使用命令
public class GameController : IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void OnStartButtonClicked()
{
// 方式1发送命令实例
this.SendCommand(new StartGameCommand());
// 方式2通过泛型发送需要无参构造函数
this.SendCommand<StartGameCommand>();
}
}
```
### 2. [`AbstractCommand<TResult>`](AbstractCommand.cs)
带返回值命令的抽象基类。
**使用示例:**
```csharp
// 定义一个计算伤害的命令
public class CalculateDamageCommand : AbstractCommand<int>
{
private readonly int _attackPower;
private readonly int _defense;
public CalculateDamageCommand(int attackPower, int defense)
{
_attackPower = attackPower;
_defense = defense;
}
protected override int OnExecute()
{
// 获取游戏配置
var config = this.GetModel<GameConfigModel>();
// 计算最终伤害
var baseDamage = _attackPower - _defense;
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
return (int)finalDamage;
}
}
// 使用带返回值的命令
public class CombatSystem : AbstractSystem
{
protected override void OnInit() { }
public void Attack(Character attacker, Character defender)
{
// 发送命令并获取返回值
var damage = this.SendCommand(
new CalculateDamageCommand(attacker.AttackPower, defender.Defense)
);
// 应用伤害
defender.Health -= damage;
// 发送伤害事件
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
}
}
```
## 命令的生命周期
1. **创建命令**:实例化命令对象,传入必要的参数
2. **设置架构**:框架自动调用 `SetArchitecture()` 设置架构引用
3. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
4. **返回结果**:对于带返回值的命令,返回执行结果
5. **命令销毁**:命令执行完毕后可以被垃圾回收
## 使用场景
### 1. 用户交互操作
```csharp
// UI 按钮点击
public class SaveGameCommand : AbstractCommand
{
protected override void OnExecute()
{
var saveSystem = this.GetSystem<SaveSystem>();
var playerModel = this.GetModel<PlayerModel>();
saveSystem.SavePlayerData(playerModel);
this.SendEvent(new GameSavedEvent());
}
}
```
### 2. 业务流程控制
```csharp
// 关卡切换
public class LoadLevelCommand : AbstractCommand
{
private readonly int _levelId;
public LoadLevelCommand(int levelId)
{
_levelId = levelId;
}
protected override void OnExecute()
{
var levelSystem = this.GetSystem<LevelSystem>();
var uiSystem = this.GetSystem<UISystem>();
// 显示加载界面
uiSystem.ShowLoadingScreen();
// 加载关卡
levelSystem.LoadLevel(_levelId);
// 发送事件
this.SendEvent(new LevelLoadedEvent(_levelId));
}
}
```
### 3. 网络请求封装
```csharp
// 登录命令
public class LoginCommand : AbstractCommand<bool>
{
private readonly string _username;
private readonly string _password;
public LoginCommand(string username, string password)
{
_username = username;
_password = password;
}
protected override bool OnExecute()
{
var networkSystem = this.GetSystem<NetworkSystem>();
var playerModel = this.GetModel<PlayerModel>();
// 发送登录请求
var result = networkSystem.Login(_username, _password);
if (result.Success)
{
playerModel.UserId = result.UserId;
playerModel.Username = _username;
this.SendEvent(new LoginSuccessEvent());
return true;
}
else
{
this.SendEvent(new LoginFailedEvent(result.ErrorMessage));
return false;
}
}
}
```
## 命令 vs 系统方法
**何时使用命令:**
- 需要参数化操作
- 需要记录操作历史(用于撤销/重做)
- 操作需要跨多个系统协调
- 用户触发的离散操作
**何时使用系统方法:**
- 持续运行的逻辑(如每帧更新)
- 系统内部的私有逻辑
- 不需要外部调用的功能
## 最佳实践
1. **保持命令原子性**:一个命令应该完成一个完整的业务操作
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
5. **合理使用返回值**:只在确实需要返回结果时使用 `ICommand<TResult>`
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand`
## 扩展功能
### 命令撤销/重做(可扩展)
```csharp
// 可撤销命令接口
public interface IUndoableCommand : ICommand
{
void Undo();
}
// 实现可撤销命令
public class MoveCommand : AbstractCommand, IUndoableCommand
{
private Vector3 _oldPosition;
private Vector3 _newPosition;
protected override void OnExecute()
{
var player = this.GetModel<PlayerModel>();
_oldPosition = player.Position;
player.Position = _newPosition;
}
public void Undo()
{
var player = this.GetModel<PlayerModel>();
player.Position = _oldPosition;
}
}
```
## 相关包
- [`architecture`](../architecture/README.md) - 架构核心,负责命令的分发和执行
- [`extensions`](../extensions/README.md) - 提供 `SendCommand()` 扩展方法
- [`query`](../query/README.md) - 查询模式,用于数据查询
- [`events`](../events/README.md) - 事件系统,命令执行后的通知机制
- [`system`](../system/README.md) - 业务系统,命令的主要执行者
- [`model`](../model/README.md) - 数据模型,命令操作的数据

View File

@ -0,0 +1,16 @@
using GFramework.framework.command;
using GFramework.framework.events;
using GFramework.framework.model;
using GFramework.framework.query;
using GFramework.framework.system;
using GFramework.framework.utility;
namespace GFramework.framework.controller;
/// <summary>
/// 控制器接口,定义了控制器需要实现的所有功能契约
/// 该接口继承了多个框架核心接口,用于支持控制器的各种能力
/// 包括架构归属、命令发送、系统获取、模型获取、事件注册、查询发送和工具获取等功能
/// </summary>
public interface IController : ICanSendCommand, ICanGetSystem, ICanGetModel,
ICanRegisterEvent, ICanSendQuery, ICanGetUtility;

View File

@ -0,0 +1,409 @@
# Controller 包使用说明
## 概述
Controller 包定义了控制器Controller的接口规范。控制器是 MVC 架构中的 C 层负责处理用户交互、协调视图和模型是连接表现层和业务层的桥梁。在本框架中Controller 通常对应 Godot 的节点脚本。
## 核心接口
### [`IController`](IController.cs)
控制器接口,定义了控制器需要实现的所有功能契约。
**继承的能力接口:**
- [`ICanSendCommand`](../command/ICanSendCommand.cs) - 可发送命令
- [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统
- [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型
- [`ICanRegisterEvent`](../events/ICanRegisterEvent.cs) - 可注册事件
- [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具
**能力说明:**
控制器拥有框架中最全面的能力集合,可以:
1. 发送命令执行业务逻辑
2. 获取系统调用服务
3. 获取模型读写数据
4. 注册事件监听变化
5. 发送查询获取信息
6. 获取工具使用辅助功能
## 使用示例
### 基础控制器实现
```csharp
using Godot;
using GFramework.framework.controller;
using GFramework.framework.architecture;
// Godot 节点控制器
public partial class PlayerController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
// 实现架构获取
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 获取模型
var playerModel = this.GetModel<PlayerModel>();
// 监听模型变化
playerModel.Health.RegisterWithInitValue(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
// 注册事件
this.RegisterEvent<PlayerLevelUpEvent>(OnPlayerLevelUp)
.AddToUnregisterList(_unregisterList);
}
// 处理用户输入
public override void _Process(double delta)
{
if (Input.IsActionJustPressed("attack"))
{
// 发送命令
this.SendCommand(new AttackCommand());
}
if (Input.IsActionJustPressed("use_item"))
{
// 发送查询
var inventory = this.SendQuery(new GetInventoryQuery());
if (inventory.HasItem("potion"))
{
this.SendCommand(new UseItemCommand("potion"));
}
}
}
private void OnHealthChanged(int newHealth)
{
// 更新 UI 显示
UpdateHealthBar(newHealth);
}
private void OnPlayerLevelUp(PlayerLevelUpEvent e)
{
// 显示升级特效
ShowLevelUpEffect();
}
public override void _ExitTree()
{
// 清理事件注册
_unregisterList.UnRegisterAll();
}
private void UpdateHealthBar(int health) { /* UI 更新逻辑 */ }
private void ShowLevelUpEffect() { /* 特效逻辑 */ }
}
```
### UI 控制器示例
```csharp
// UI 面板控制器
public partial class MainMenuController : Control, IController
{
[Export] private Button _startButton;
[Export] private Button _settingsButton;
[Export] private Button _quitButton;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 绑定按钮事件
_startButton.Pressed += OnStartButtonPressed;
_settingsButton.Pressed += OnSettingsButtonPressed;
_quitButton.Pressed += OnQuitButtonPressed;
// 获取模型更新 UI
var gameModel = this.GetModel<GameModel>();
UpdateUI(gameModel);
}
private void OnStartButtonPressed()
{
// 通过命令启动游戏
this.SendCommand<StartGameCommand>();
}
private void OnSettingsButtonPressed()
{
// 查询当前设置
var settings = this.SendQuery(new GetSettingsQuery());
// 打开设置面板
var uiSystem = this.GetSystem<UISystem>();
uiSystem.OpenSettingsPanel(settings);
}
private void OnQuitButtonPressed()
{
// 发送退出命令
this.SendCommand<QuitGameCommand>();
}
private void UpdateUI(GameModel model) { /* UI 更新逻辑 */ }
}
```
### 复杂交互控制器
```csharp
// 战斗控制器
public partial class CombatController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
private PlayerModel _playerModel;
private CombatSystem _combatSystem;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 缓存常用引用
_playerModel = this.GetModel<PlayerModel>();
_combatSystem = this.GetSystem<CombatSystem>();
// 注册多个事件
this.RegisterEvent<EnemySpawnedEvent>(OnEnemySpawned)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<CombatEndedEvent>(OnCombatEnded)
.AddToUnregisterList(_unregisterList);
// 监听模型状态
_playerModel.CombatState.Register(OnCombatStateChanged)
.AddToUnregisterList(_unregisterList);
}
private void OnEnemySpawned(EnemySpawnedEvent e)
{
// 进入战斗状态
this.SendCommand(new EnterCombatCommand(e.Enemy));
}
private void OnCombatEnded(CombatEndedEvent e)
{
if (e.Victory)
{
// 查询奖励
var rewards = this.SendQuery(new CalculateRewardsQuery(e.Enemy));
// 发放奖励
this.SendCommand(new GiveRewardsCommand(rewards));
}
else
{
// 处理失败
this.SendCommand<GameOverCommand>();
}
}
private void OnCombatStateChanged(CombatState state)
{
// 根据战斗状态更新 UI
var uiSystem = this.GetSystem<UISystem>();
uiSystem.UpdateCombatUI(state);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
}
```
## 控制器职责
### ✅ 应该做的事
1. **处理用户输入**
- 键盘、鼠标、触摸输入
- UI 按钮点击
- 手势识别
2. **协调视图和模型**
- 监听模型变化更新视图
- 将用户操作转换为命令
3. **管理界面逻辑**
- UI 元素的显示/隐藏
- 动画播放控制
- 视觉反馈
4. **事件监听**
- 注册关心的事件
- 响应事件更新界面
### ❌ 不应该做的事
1. **不包含业务逻辑**
- 业务逻辑应该在 System 中
- 控制器只负责调用和协调
2. **不直接修改模型**
- 应该通过 Command 修改模型
- 避免直接访问 Model 的 setter
3. **不处理复杂计算**
- 复杂计算应该在 Query 或 System 中
- 控制器保持简洁
4. **不保存核心状态**
- 核心状态应该在 Model 中
- 控制器可以保存临时 UI 状态
## 生命周期管理
### 事件注销
```csharp
public partial class MyController : Node, IController
{
// 使用 UnRegisterList 统一管理
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
{
// 所有事件注册都添加到列表
this.RegisterEvent<GameEvent>(OnGameEvent)
.AddToUnregisterList(_unregisterList);
this.GetModel<PlayerModel>().Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
// 节点销毁时统一注销所有事件
_unregisterList.UnRegisterAll();
}
}
```
### Godot 特定的生命周期
```csharp
public partial class GameController : Node, IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
// 节点进入场景树
public override void _Ready()
{
// 初始化控制器
InitializeController();
}
// 每帧更新
public override void _Process(double delta)
{
// 处理实时输入
HandleInput();
}
// 物理帧更新
public override void _PhysicsProcess(double delta)
{
// 处理物理相关输入
}
// 节点即将退出场景树
public override void _ExitTree()
{
// 清理资源
CleanupController();
}
}
```
## 最佳实践
1. **一个控制器对应一个视图**
- 每个 Godot 场景/节点有对应的控制器
- 避免一个控制器管理多个不相关的视图
2. **使用依赖注入获取依赖**
- 通过 `GetModel()``GetSystem()` 获取依赖
- 不要在构造函数中获取,应在 `_Ready()`
3. **保持控制器轻量**
- 复杂逻辑放在 Command、Query、System 中
- 控制器只做协调和转发
4. **合理使用缓存**
- 频繁使用的 Model、System 可以缓存引用
- 平衡性能和内存占用
5. **统一管理事件注销**
- 使用 `IUnRegisterList` 统一管理
- 在 `_ExitTree()` 中统一注销
6. **命名规范**
- 控制器类名:`XxxController`
- 继承 Godot 节点:`Node``Control``Node2D`
## 常见模式
### 数据绑定模式
```csharp
public partial class ScoreController : Label, IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 绑定模型数据到 UI
this.GetModel<GameModel>()
.Score
.RegisterWithInitValue(score => Text = $"Score: {score}")
.UnRegisterWhenNodeExitTree(this);
}
}
```
### 状态机模式
```csharp
public partial class PlayerStateController : Node, IController
{
private Dictionary<PlayerState, Action> _stateHandlers;
public override void _Ready()
{
_stateHandlers = new Dictionary<PlayerState, Action>
{
{ PlayerState.Idle, HandleIdleState },
{ PlayerState.Moving, HandleMovingState },
{ PlayerState.Attacking, HandleAttackingState }
};
this.GetModel<PlayerModel>()
.State
.Register(OnStateChanged)
.UnRegisterWhenNodeExitTree(this);
}
private void OnStateChanged(PlayerState state)
{
_stateHandlers[state]?.Invoke();
}
}
```
## 相关包
- [`architecture`](../architecture/README.md) - 提供架构访问能力
- [`command`](../command/README.md) - 控制器发送命令执行业务逻辑
- [`query`](../query/README.md) - 控制器发送查询获取数据
- [`events`](../events/README.md) - 控制器注册事件监听变化
- [`model`](../model/README.md) - 控制器读取模型数据
- [`system`](../system/README.md) - 控制器调用系统服务
- [`extensions`](../extensions/README.md) - 提供便捷的扩展方法

View File

@ -0,0 +1,21 @@
namespace GFramework.framework.events;
/// <summary>
/// 默认注销器类,用于执行注销操作
/// </summary>
/// <param name="onUnRegister">注销时要执行的回调函数</param>
public class DefaultUnRegister(Action onUnRegister): IUnRegister
{
private Action _mOnUnRegister = onUnRegister;
/// <summary>
/// 执行注销操作,调用注册的回调函数并清理引用
/// </summary>
public void UnRegister()
{
// 调用注销回调函数并清理引用
_mOnUnRegister.Invoke();
_mOnUnRegister = null;
}
}

View File

@ -0,0 +1,31 @@
namespace GFramework.framework.events;
/// <summary>
/// 简单事件类,用于注册、注销和触发无参事件回调
/// </summary>
public class EasyEvent
{
private Action _mOnEvent = () => { };
/// <summary>
/// 注册事件回调函数
/// </summary>
/// <param name="onEvent">要注册的事件回调函数</param>
/// <returns>用于注销事件的 unregister 对象</returns>
public IUnRegister Register(Action onEvent)
{
_mOnEvent += onEvent;
return new DefaultUnRegister(() => { UnRegister(onEvent); });
}
/// <summary>
/// 注销已注册的事件回调函数
/// </summary>
/// <param name="onEvent">要注销的事件回调函数</param>
public void UnRegister(Action onEvent) => _mOnEvent -= onEvent;
/// <summary>
/// 触发所有已注册的事件回调函数
/// </summary>
public void Trigger() => _mOnEvent?.Invoke();
}

View File

@ -0,0 +1,154 @@
namespace GFramework.framework.events;
/// <summary>
/// 泛型事件类,支持一个泛型参数 T 的事件注册、注销与触发。
/// 实现了 IEasyEvent 接口以提供统一的事件操作接口。
/// </summary>
/// <typeparam name="T">事件回调函数的第一个参数类型。</typeparam>
public class EasyEvent<T> : IEasyEvent
{
/// <summary>
/// 存储已注册的事件处理委托。
/// 默认为空操作no-op委托避免 null 检查。
/// </summary>
private Action<T> _mOnEvent = e => { };
/// <summary>
/// 注册一个事件监听器,并返回可用于取消注册的对象。
/// </summary>
/// <param name="onEvent">要注册的事件处理方法。</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器。</returns>
public IUnRegister Register(Action<T> onEvent)
{
_mOnEvent += onEvent;
return new DefaultUnRegister(() => { UnRegister(onEvent); });
}
/// <summary>
/// 取消指定的事件监听器。
/// </summary>
/// <param name="onEvent">需要被注销的事件处理方法。</param>
public void UnRegister(Action<T> onEvent) => _mOnEvent -= onEvent;
/// <summary>
/// 触发所有已注册的事件处理程序,并传递参数 t。
/// </summary>
/// <param name="t">传递给事件处理程序的参数。</param>
public void Trigger(T t) => _mOnEvent?.Invoke(t);
/// <summary>
/// 显式实现 IEasyEvent 接口中的 Register 方法。
/// 允许使用无参 Action 来订阅当前带参事件。
/// </summary>
/// <param name="onEvent">无参事件处理方法。</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器。</returns>
IUnRegister IEasyEvent.Register(Action onEvent)
{
return Register(Action);
void Action(T _) => onEvent();
}
}
/// <summary>
/// 支持两个泛型参数 T 和 TK 的事件类。
/// 提供事件注册、注销和触发功能。
/// </summary>
/// <typeparam name="T">第一个参数类型。</typeparam>
/// <typeparam name="TK">第二个参数类型。</typeparam>
public class EasyEvent<T, TK> : IEasyEvent
{
/// <summary>
/// 存储已注册的双参数事件处理委托。
/// 默认为空操作no-op委托。
/// </summary>
private Action<T, TK> _mOnEvent = (_, _) => { };
/// <summary>
/// 注册一个接受两个参数的事件监听器,并返回可用于取消注册的对象。
/// </summary>
/// <param name="onEvent">要注册的事件处理方法。</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器。</returns>
public IUnRegister Register(Action<T, TK> onEvent)
{
_mOnEvent += onEvent;
return new DefaultUnRegister(() => { UnRegister(onEvent); });
}
/// <summary>
/// 取消指定的双参数事件监听器。
/// </summary>
/// <param name="onEvent">需要被注销的事件处理方法。</param>
public void UnRegister(Action<T, TK> onEvent) => _mOnEvent -= onEvent;
/// <summary>
/// 触发所有已注册的事件处理程序,并传递参数 t 和 k。
/// </summary>
/// <param name="t">第一个参数。</param>
/// <param name="k">第二个参数。</param>
public void Trigger(T t, TK k) => _mOnEvent?.Invoke(t, k);
/// <summary>
/// 显式实现 IEasyEvent 接口中的 Register 方法。
/// 允许使用无参 Action 来订阅当前带参事件。
/// </summary>
/// <param name="onEvent">无参事件处理方法。</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器。</returns>
IUnRegister IEasyEvent.Register(Action onEvent)
{
return Register(Action);
void Action(T _, TK __) => onEvent();
}
}
/// <summary>
/// 支持三个泛型参数 T、TK 和 TS 的事件类。
/// 提供事件注册、注销和触发功能。
/// </summary>
/// <typeparam name="T">第一个参数类型。</typeparam>
/// <typeparam name="TK">第二个参数类型。</typeparam>
/// <typeparam name="TS">第三个参数类型。</typeparam>
public class EasyEvent<T, TK, TS> : IEasyEvent
{
/// <summary>
/// 存储已注册的三参数事件处理委托。
/// 默认为空操作no-op委托。
/// </summary>
private Action<T, TK, TS> _mOnEvent = (_, _, _) => { };
/// <summary>
/// 注册一个接受三个参数的事件监听器,并返回可用于取消注册的对象。
/// </summary>
/// <param name="onEvent">要注册的事件处理方法。</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器。</returns>
public IUnRegister Register(Action<T, TK, TS> onEvent)
{
_mOnEvent += onEvent;
return new DefaultUnRegister(() => { UnRegister(onEvent); });
}
/// <summary>
/// 取消指定的三参数事件监听器。
/// </summary>
/// <param name="onEvent">需要被注销的事件处理方法。</param>
public void UnRegister(Action<T, TK, TS> onEvent) => _mOnEvent -= onEvent;
/// <summary>
/// 触发所有已注册的事件处理程序,并传递参数 t、k 和 s。
/// </summary>
/// <param name="t">第一个参数。</param>
/// <param name="k">第二个参数。</param>
/// <param name="s">第三个参数。</param>
public void Trigger(T t, TK k, TS s) => _mOnEvent?.Invoke(t, k, s);
/// <summary>
/// 显式实现 IEasyEvent 接口中的 Register 方法。
/// 允许使用无参 Action 来订阅当前带参事件。
/// </summary>
/// <param name="onEvent">无参事件处理方法。</param>
/// <returns>IUnRegister 对象,用于稍后注销该事件监听器。</returns>
IUnRegister IEasyEvent.Register(Action onEvent)
{
return Register(Action);
void Action(T _, TK __, TS ___) => onEvent();
}
}

View File

@ -0,0 +1,67 @@
namespace GFramework.framework.events;
/// <summary>
/// EasyEvents事件管理器类用于全局事件的注册、获取和管理
/// 提供了类型安全的事件系统,支持泛型事件的自动创建和检索
/// </summary>
public class EasyEvents
{
/// <summary>
/// 全局单例事件管理器实例
/// </summary>
private static readonly EasyEvents MGlobalEvents = new();
/// <summary>
/// 获取指定类型的全局事件实例
/// </summary>
/// <typeparam name="T">事件类型必须实现IEasyEvent接口</typeparam>
/// <returns>指定类型的事件实例,如果未注册则返回默认值</returns>
public static T Get<T>() where T : IEasyEvent => MGlobalEvents.GetEvent<T>();
/// <summary>
/// 注册指定类型的全局事件
/// </summary>
/// <typeparam name="T">事件类型必须实现IEasyEvent接口且具有无参构造函数</typeparam>
public static void Register<T>() where T : IEasyEvent, new() => MGlobalEvents.AddEvent<T>();
/// <summary>
/// 存储事件类型与事件实例映射关系的字典
/// </summary>
private readonly Dictionary<Type, IEasyEvent> _mTypeEvents = new();
/// <summary>
/// 添加指定类型的事件到事件字典中
/// </summary>
/// <typeparam name="T">事件类型必须实现IEasyEvent接口且具有无参构造函数</typeparam>
public void AddEvent<T>() where T : IEasyEvent, new() => _mTypeEvents.Add(typeof(T), new T());
/// <summary>
/// 获取指定类型的事件实例
/// </summary>
/// <typeparam name="T">事件类型必须实现IEasyEvent接口</typeparam>
/// <returns>指定类型的事件实例,如果不存在则返回默认值</returns>
public T GetEvent<T>() where T : IEasyEvent
{
return _mTypeEvents.TryGetValue(typeof(T), out var e) ? (T)e : default;
}
/// <summary>
/// 获取指定类型的事件实例,如果不存在则创建并添加到事件字典中
/// </summary>
/// <typeparam name="T">事件类型必须实现IEasyEvent接口且具有无参构造函数</typeparam>
/// <returns>指定类型的事件实例</returns>
public T GetOrAddEvent<T>() where T : IEasyEvent, new()
{
var eType = typeof(T);
// 尝试从字典中获取事件实例
if (_mTypeEvents.TryGetValue(eType, out var e))
{
return (T)e;
}
// 如果不存在则创建新实例并添加到字典中
var t = new T();
_mTypeEvents.Add(eType, t);
return t;
}
}

View File

@ -0,0 +1,10 @@
using GFramework.framework.rule;
namespace GFramework.framework.events;
/// <summary>
/// 定义一个可以注册事件的接口继承自IBelongToArchitecture接口。
/// 该接口用于标识那些能够注册事件的对象,通常在框架的事件系统中使用。
/// 实现此接口的类型表明它属于某个架构组件,并具备事件注册的能力。
/// </summary>
public interface ICanRegisterEvent:IBelongToArchitecture;

View File

@ -0,0 +1,9 @@
using GFramework.framework.rule;
namespace GFramework.framework.events;
/// <summary>
/// 定义一个可以发送事件的接口继承自IBelongToArchitecture接口。
/// 该接口用于标识那些具备发送事件能力的类型,通常作为架构中事件发送功能的基础接口。
/// </summary>
public interface ICanSendEvent : IBelongToArchitecture;

View File

@ -0,0 +1,14 @@
namespace GFramework.framework.events;
/// <summary>
/// 事件接口,定义了事件注册的基本功能
/// </summary>
public interface IEasyEvent
{
/// <summary>
/// 注册事件处理函数
/// </summary>
/// <param name="onEvent">事件触发时要执行的回调函数</param>
/// <returns>用于取消注册的句柄对象</returns>
IUnRegister Register(Action onEvent);
}

View File

@ -0,0 +1,12 @@
namespace GFramework.framework.events;
/// <summary>
/// 提供注销功能的接口
/// </summary>
public interface IUnRegister
{
/// <summary>
/// 执行注销操作
/// </summary>
void UnRegister();
}

View File

@ -0,0 +1,12 @@
namespace GFramework.framework.events;
/// <summary>
/// 提供统一注销功能的接口,用于管理需要注销的对象列表
/// </summary>
public interface IUnRegisterList
{
/// <summary>
/// 获取需要注销的对象列表
/// </summary>
List<IUnRegister> UnregisterList { get; }
}

View File

@ -0,0 +1,53 @@
using GFramework.framework.extensions;
namespace GFramework.framework.events;
/// <summary>
/// OrEvent类用于实现事件的或逻辑组合当任意一个注册的事件触发时都会触发OrEvent本身
/// </summary>
public class OrEvent : IUnRegisterList
{
/// <summary>
/// 将指定的事件与当前OrEvent进行或逻辑组合
/// </summary>
/// <param name="easyEvent">要组合的事件对象</param>
/// <returns>返回当前OrEvent实例支持链式调用</returns>
public OrEvent Or(IEasyEvent easyEvent)
{
easyEvent.Register(Trigger).AddToUnregisterList(this);
return this;
}
private Action _mOnEvent = () => { };
/// <summary>
/// 注册事件处理函数
/// </summary>
/// <param name="onEvent">要注册的事件处理函数</param>
/// <returns>返回一个可取消注册的对象</returns>
public IUnRegister Register(Action onEvent)
{
_mOnEvent += onEvent;
return new DefaultUnRegister(() => { UnRegister(onEvent); });
}
/// <summary>
/// 取消注册指定的事件处理函数
/// </summary>
/// <param name="onEvent">要取消注册的事件处理函数</param>
public void UnRegister(Action onEvent)
{
_mOnEvent -= onEvent;
this.UnRegisterAll();
}
/// <summary>
/// 触发所有已注册的事件处理函数
/// </summary>
private void Trigger() => _mOnEvent?.Invoke();
/// <summary>
/// 获取取消注册列表
/// </summary>
public List<IUnRegister> UnregisterList { get; } = new List<IUnRegister>();
}

503
framework/events/README.md Normal file
View File

@ -0,0 +1,503 @@
# Events 包使用说明
## 概述
Events 包提供了一套完整的事件系统实现了观察者模式Observer Pattern。通过事件系统可以实现组件间的松耦合通信支持无参和带参事件、事件注册/注销、以及灵活的事件组合。
## 核心接口
### 1. [`IEasyEvent`](IEasyEvent.cs)
基础事件接口,定义了事件注册的基本功能。
**核心方法:**
```csharp
IUnRegister Register(Action onEvent); // 注册事件处理函数
```
### 2. [`IUnRegister`](IUnRegister.cs)
注销接口,用于取消事件注册。
**核心方法:**
```csharp
void UnRegister(); // 执行注销操作
```
### 3. [`IUnRegisterList`](IUnRegisterList.cs)
注销列表接口,用于批量管理注销对象。
**属性:**
```csharp
List<IUnRegister> UnregisterList { get; } // 获取注销列表
```
### 4. 能力接口
- [`ICanRegisterEvent`](ICanRegisterEvent.cs) - 标记可以注册事件的组件
- [`ICanSendEvent`](ICanSendEvent.cs) - 标记可以发送事件的组件
## 核心类
### 1. [`EasyEvent`](EasyEvent.cs)
无参事件类,支持注册、注销和触发无参事件。
**使用示例:**
```csharp
// 创建事件
var onClicked = new EasyEvent();
// 注册监听
var unregister = onClicked.Register(() =>
{
GD.Print("Button clicked!");
});
// 触发事件
onClicked.Trigger();
// 取消注册
unregister.UnRegister();
```
### 2. [`EasyEvent<T>`](EasyEventGeneric.cs)
单参数泛型事件类,支持一个参数的事件。
**使用示例:**
```csharp
// 创建带参数的事件
var onScoreChanged = new EasyEvent<int>();
// 注册监听
onScoreChanged.Register(newScore =>
{
GD.Print($"Score changed to: {newScore}");
});
// 触发事件并传递参数
onScoreChanged.Trigger(100);
```
### 3. [`EasyEvent<T, TK>`](EasyEventGeneric.cs)
双参数泛型事件类。
**使用示例:**
```csharp
// 伤害事件:攻击者、伤害值
var onDamageDealt = new EasyEvent<string, int>();
onDamageDealt.Register((attacker, damage) =>
{
GD.Print($"{attacker} dealt {damage} damage!");
});
onDamageDealt.Trigger("Player", 50);
```
### 4. [`EasyEvent<T, TK, TS>`](EasyEventGeneric.cs)
三参数泛型事件类。
**使用示例:**
```csharp
// 位置变化事件:对象、旧位置、新位置
var onPositionChanged = new EasyEvent<string, Vector3, Vector3>();
onPositionChanged.Register((obj, oldPos, newPos) =>
{
GD.Print($"{obj} moved from {oldPos} to {newPos}");
});
onPositionChanged.Trigger("Player", Vector3.Zero, new Vector3(10, 0, 0));
```
### 5. [`EasyEvents`](EasyEvents.cs)
全局事件管理器,提供类型安全的事件注册和获取。
**使用示例:**
```csharp
// 注册全局事件类型
EasyEvents.Register<GameStartEvent>();
// 获取事件实例
var gameStartEvent = EasyEvents.Get<GameStartEvent>();
// 注册监听
gameStartEvent.Register(() =>
{
GD.Print("Game started!");
});
// 触发事件
gameStartEvent.Trigger();
```
### 6. [`TypeEventSystem`](TypeEventSystem.cs)
类型化事件系统,支持基于类型的事件发送和注册。
**使用示例:**
```csharp
// 使用全局事件系统
var eventSystem = TypeEventSystem.Global;
// 注册类型化事件
eventSystem.Register<PlayerDiedEvent>(e =>
{
GD.Print($"Player died at position: {e.Position}");
});
// 发送事件(自动创建实例)
eventSystem.Send<PlayerDiedEvent>();
// 发送事件(传递实例)
eventSystem.Send(new PlayerDiedEvent
{
Position = new Vector3(10, 0, 5)
});
```
### 7. [`DefaultUnRegister`](DefaultUnRegister.cs)
默认注销器实现,封装注销回调。
**使用示例:**
```csharp
Action onUnregister = () => GD.Print("Unregistered");
var unregister = new DefaultUnRegister(onUnregister);
// 执行注销
unregister.UnRegister();
```
### 8. [`OrEvent`](OrEvent.cs)
事件或运算组合器,当任意一个事件触发时触发。
**使用示例:**
```csharp
var onAnyInput = new OrEvent()
.Or(onKeyPressed)
.Or(onMouseClicked)
.Or(onTouchDetected);
// 当上述任意事件触发时,执行回调
onAnyInput.Register(() =>
{
GD.Print("Input detected!");
});
```
## 在架构中使用事件
### 定义事件类
```csharp
// 简单事件
public struct GameStartedEvent { }
// 带数据的事件
public struct PlayerDiedEvent
{
public Vector3 Position;
public string Cause;
}
// 复杂事件
public struct LevelCompletedEvent
{
public int LevelId;
public float CompletionTime;
public int Score;
public List<string> Achievements;
}
```
### Model 中发送事件
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
protected override void OnInit()
{
// 监听生命值变化
Health.Register(newHealth =>
{
if (newHealth <= 0)
{
// 发送玩家死亡事件
this.SendEvent(new PlayerDiedEvent
{
Position = Position,
Cause = "Health depleted"
});
}
});
}
}
```
### System 中发送事件
```csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit() { }
public void DealDamage(Character attacker, Character target, int damage)
{
target.Health -= damage;
// 发送伤害事件
this.SendEvent(new DamageDealtEvent
{
Attacker = attacker.Name,
Target = target.Name,
Damage = damage
});
}
}
```
### Controller 中注册事件
```csharp
public partial class GameController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 注册多个事件
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted)
.AddToUnregisterList(_unregisterList);
}
private void OnGameStarted(GameStartedEvent e)
{
GD.Print("Game started!");
}
private void OnPlayerDied(PlayerDiedEvent e)
{
GD.Print($"Player died at {e.Position}: {e.Cause}");
ShowGameOverScreen();
}
private void OnLevelCompleted(LevelCompletedEvent e)
{
GD.Print($"Level {e.LevelId} completed! Score: {e.Score}");
ShowVictoryScreen(e);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
}
```
## 高级用法
### 1. 事件链式组合
```csharp
// 使用 Or 组合多个事件
var onAnyDamage = new OrEvent()
.Or(onPhysicalDamage)
.Or(onMagicDamage)
.Or(onPoisonDamage);
onAnyDamage.Register(() =>
{
PlayDamageSound();
});
```
### 2. 事件过滤
```csharp
// 只处理高伤害事件
this.RegisterEvent<DamageDealtEvent>(e =>
{
if (e.Damage >= 50)
{
ShowCriticalHitEffect();
}
});
```
### 3. 事件转发
```csharp
public class EventBridge : AbstractSystem
{
protected override void OnInit()
{
// 将内部事件转发为公共事件
this.RegisterEvent<InternalPlayerDiedEvent>(e =>
{
this.SendEvent(new PublicPlayerDiedEvent
{
PlayerId = e.Id,
Timestamp = DateTime.Now
});
});
}
}
```
### 4. 临时事件监听
```csharp
public class TutorialController : Node, IController
{
public override void _Ready()
{
// 只监听一次
IUnRegister unregister = null;
unregister = this.RegisterEvent<FirstEnemyKilledEvent>(e =>
{
ShowTutorialComplete();
unregister?.UnRegister(); // 立即注销
});
}
}
```
### 5. 条件事件
```csharp
public class AchievementSystem : AbstractSystem
{
private int _killCount = 0;
protected override void OnInit()
{
this.RegisterEvent<EnemyKilledEvent>(e =>
{
_killCount++;
// 条件满足时发送成就事件
if (_killCount >= 100)
{
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "kill_100_enemies"
});
}
});
}
}
```
## 生命周期管理
### 使用 UnRegisterList
```csharp
public class MyController : Node, IController
{
// 统一管理所有注销对象
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
{
// 所有注册都添加到列表
this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<Event2>(OnEvent2)
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
// 一次性注销所有
_unregisterList.UnRegisterAll();
}
}
```
### 使用 Godot 节点生命周期
```csharp
public override void _Ready()
{
// 当节点退出场景树时自动注销
this.RegisterEvent<GameEvent>(OnGameEvent)
.UnRegisterWhenNodeExitTree(this);
}
```
## 最佳实践
1. **事件命名规范**
- 使用过去式:`PlayerDiedEvent``LevelCompletedEvent`
- 使用 `Event` 后缀:便于识别
- 使用结构体:减少内存分配
2. **事件数据设计**
- 只包含必要信息
- 使用值类型struct提高性能
- 避免传递可变引用
3. **避免事件循环**
- 事件处理器中谨慎发送新事件
- 使用命令打破循环依赖
4. **合理使用事件**
- 用于通知状态变化
- 用于跨模块通信
- 不用于返回数据(使用 Query
5. **注销管理**
- 始终注销事件监听
- 使用 `IUnRegisterList` 批量管理
- 利用 Godot 节点生命周期
6. **性能考虑**
- 避免频繁触发的事件(如每帧)
- 事件处理器保持轻量
- 使用结构体事件减少 GC
## 事件 vs 其他通信方式
| 方式 | 适用场景 | 优点 | 缺点 |
|------|---------|------|------|
| **Event** | 状态变化通知、跨模块通信 | 松耦合、一对多 | 难以追踪调用链 |
| **Command** | 执行操作、修改状态 | 封装逻辑、可撤销 | 单向通信 |
| **Query** | 查询数据 | 职责清晰、有返回值 | 同步调用 |
| **BindableProperty** | UI 数据绑定 | 自动更新、响应式 | 仅限单一属性 |
## 相关包
- [`architecture`](../architecture/README.md) - 提供全局事件系统
- [`extensions`](../extensions/README.md) - 提供事件扩展方法
- [`property`](../property/README.md) - 可绑定属性基于事件实现
- [`controller`](../controller/README.md) - 控制器监听事件
- [`model`](../model/README.md) - 模型发送事件
- [`system`](../system/README.md) - 系统发送和监听事件

View File

@ -0,0 +1,23 @@
namespace GFramework.framework.events;
/// <summary>
/// TypeEventSystem
/// </summary>
public class TypeEventSystem
{
private readonly EasyEvents _mEvents = new();
public static readonly TypeEventSystem Global = new();
public void Send<T>() where T : new() => _mEvents.GetEvent<EasyEvent<T>>()?.Trigger(new T());
public void Send<T>(T e) => _mEvents.GetEvent<EasyEvent<T>>()?.Trigger(e);
public IUnRegister Register<T>(Action<T> onEvent) => _mEvents.GetOrAddEvent<EasyEvent<T>>().Register(onEvent);
public void UnRegister<T>(Action<T> onEvent)
{
var e = _mEvents.GetEvent<EasyEvent<T>>();
e?.UnRegister(onEvent);
}
}

View File

@ -0,0 +1,39 @@
namespace GFramework.framework.events;
/// <summary>
/// 取消注册列表类,用于管理多个需要取消注册的对象
/// </summary>
public class UnRegisterList : IUnRegisterList
{
private readonly List<IUnRegister> _unRegisterList = [];
/// <summary>
/// 向取消注册列表中添加一个新的可取消注册对象
/// </summary>
/// <param name="unRegister">需要添加到列表中的可取消注册对象</param>
public void Add(IUnRegister unRegister)
{
_unRegisterList.Add(unRegister);
}
/// <summary>
/// 对列表中的所有对象执行取消注册操作,并清空列表
/// </summary>
public void UnRegisterAll()
{
// 遍历所有注册项并执行取消注册
foreach (var t in _unRegisterList)
{
t.UnRegister();
}
// 清空列表
_unRegisterList.Clear();
}
/// <summary>
/// 获取取消注册列表的只读属性
/// </summary>
public List<IUnRegister> UnregisterList { get; }
}

View File

@ -0,0 +1,50 @@
using GFramework.framework.model;
using GFramework.framework.system;
using GFramework.framework.utility;
namespace GFramework.framework.extensions;
/// <summary>
/// 提供获取模型对象扩展方法的静态类
/// </summary>
public static class CanGetModelExtension
{
/// <summary>
/// 从架构中获取指定类型的模型实例
/// </summary>
/// <typeparam name="T">要获取的模型类型必须实现IModel接口</typeparam>
/// <param name="self">实现ICanGetModel接口的对象实例</param>
/// <returns>指定类型的模型实例</returns>
public static T GetModel<T>(this ICanGetModel self) where T : class, IModel =>
self.GetArchitecture().GetModel<T>();
}
/// <summary>
/// 提供获取系统对象扩展方法的静态类
/// </summary>
public static class CanGetSystemExtension
{
/// <summary>
/// 从架构中获取指定类型的系统实例
/// </summary>
/// <typeparam name="T">要获取的系统类型必须实现ISystem接口</typeparam>
/// <param name="self">实现ICanGetSystem接口的对象实例</param>
/// <returns>指定类型的系统实例</returns>
public static T GetSystem<T>(this ICanGetSystem self) where T : class, ISystem =>
self.GetArchitecture().GetSystem<T>();
}
/// <summary>
/// 提供获取工具对象扩展方法的静态类
/// </summary>
public static class CanGetUtilityExtension
{
/// <summary>
/// 从架构中获取指定类型的工具实例
/// </summary>
/// <typeparam name="T">要获取的工具类型必须实现IUtility接口</typeparam>
/// <param name="self">实现ICanGetUtility接口的对象实例</param>
/// <returns>指定类型的工具实例</returns>
public static T GetUtility<T>(this ICanGetUtility self) where T : class, IUtility =>
self.GetArchitecture().GetUtility<T>();
}

View File

@ -0,0 +1,28 @@
using GFramework.framework.events;
namespace GFramework.framework.extensions;
/// <summary>
/// 事件注册扩展类提供ICanRegisterEvent接口的扩展方法用于注册和注销事件
/// </summary>
public static class CanRegisterEventExtensions
{
/// <summary>
/// 注册指定类型的事件处理函数
/// </summary>
/// <typeparam name="T">事件数据类型</typeparam>
/// <param name="self">实现ICanRegisterEvent接口的对象实例</param>
/// <param name="onEvent">事件处理回调函数</param>
/// <returns>返回事件注销器接口,可用于后续注销事件</returns>
public static IUnRegister RegisterEvent<T>(this ICanRegisterEvent self, Action<T> onEvent) =>
self.GetArchitecture().RegisterEvent(onEvent);
/// <summary>
/// 注销指定类型的事件处理函数
/// </summary>
/// <typeparam name="T">事件数据类型</typeparam>
/// <param name="self">实现ICanRegisterEvent接口的对象实例</param>
/// <param name="onEvent">要注销的事件处理回调函数</param>
public static void UnRegisterEvent<T>(this ICanRegisterEvent self, Action<T> onEvent) =>
self.GetArchitecture().UnRegisterEvent(onEvent);
}

View File

@ -0,0 +1,76 @@
using GFramework.framework.command;
using GFramework.framework.events;
using GFramework.framework.query;
namespace GFramework.framework.extensions;
/// <summary>
/// 提供发送命令功能的扩展类
/// </summary>
public static class CanSendCommandExtension
{
/// <summary>
/// 发送指定类型的命令该命令类型必须实现ICommand接口且具有无参构造函数
/// </summary>
/// <typeparam name="T">命令类型必须实现ICommand接口且具有无参构造函数</typeparam>
/// <param name="self">实现ICanSendCommand接口的对象实例</param>
public static void SendCommand<T>(this ICanSendCommand self) where T : ICommand, new() =>
self.GetArchitecture().SendCommand(new T());
/// <summary>
/// 发送指定的命令实例
/// </summary>
/// <typeparam name="T">命令类型必须实现ICommand接口</typeparam>
/// <param name="self">实现ICanSendCommand接口的对象实例</param>
/// <param name="command">要发送的命令实例</param>
public static void SendCommand<T>(this ICanSendCommand self, T command) where T : ICommand =>
self.GetArchitecture().SendCommand(command);
/// <summary>
/// 发送带有返回值的命令并获取执行结果
/// </summary>
/// <typeparam name="TResult">命令执行结果的类型</typeparam>
/// <param name="self">实现ICanSendCommand接口的对象实例</param>
/// <param name="command">要发送的命令实例必须实现ICommand&lt;TResult&gt;接口</param>
/// <returns>命令执行后的返回结果</returns>
public static TResult SendCommand<TResult>(this ICanSendCommand self, ICommand<TResult> command) =>
self.GetArchitecture().SendCommand(command);
}
/// <summary>
/// 提供发送事件功能的扩展类
/// </summary>
public static class CanSendEventExtension
{
/// <summary>
/// 发送指定类型的事件,该事件类型必须具有无参构造函数
/// </summary>
/// <typeparam name="T">事件类型,必须具有无参构造函数</typeparam>
/// <param name="self">实现ICanSendEvent接口的对象实例</param>
public static void SendEvent<T>(this ICanSendEvent self) where T : new() =>
self.GetArchitecture().SendEvent<T>();
/// <summary>
/// 发送指定的事件实例
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="self">实现ICanSendEvent接口的对象实例</param>
/// <param name="e">要发送的事件实例</param>
public static void SendEvent<T>(this ICanSendEvent self, T e) => self.GetArchitecture().SendEvent(e);
}
/// <summary>
/// 提供发送查询功能的扩展类
/// </summary>
public static class CanSendQueryExtension
{
/// <summary>
/// 发送查询请求并获取查询结果
/// </summary>
/// <typeparam name="TResult">查询结果的类型</typeparam>
/// <param name="self">实现ICanSendQuery接口的对象实例</param>
/// <param name="query">要发送的查询实例必须实现IQuery&lt;TResult&gt;接口</param>
/// <returns>查询操作的返回结果</returns>
public static TResult SendQuery<TResult>(this ICanSendQuery self, IQuery<TResult> query) =>
self.GetArchitecture().SendQuery(query);
}

View File

@ -0,0 +1,17 @@
using GFramework.framework.events;
namespace GFramework.framework.extensions;
/// <summary>
/// 提供Or事件扩展方法的静态类
/// </summary>
public static class OrEventExtensions
{
/// <summary>
/// 创建一个OrEvent实例将当前事件与指定事件进行逻辑或运算组合
/// </summary>
/// <param name="self">当前的IEasyEvent事件实例</param>
/// <param name="e">要与当前事件进行或运算的另一个IEasyEvent事件实例</param>
/// <returns>返回一个新的OrEvent实例表示两个事件的或运算结果</returns>
public static OrEvent Or(this IEasyEvent self, IEasyEvent e) => new OrEvent().Or(self).Or(e);
}

View File

@ -0,0 +1,503 @@
# Extensions 包使用说明
## 概述
Extensions 包提供了一系列扩展方法,简化了框架各个接口的使用。通过扩展方法,可以用更简洁的语法访问框架功能,提高代码可读性和开发效率。
## 扩展方法类别
### 1. 获取组件扩展 ([`CanGetExtensions.cs`](CanGetExtensions.cs))
为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。
#### CanGetModelExtension
```csharp
public static T GetModel<T>(this ICanGetModel self) where T : class, IModel
```
**使用示例:**
```csharp
// 在 Controller、Command、Query 中使用
public class PlayerController : IController
{
public void UpdateUI()
{
// 直接通过 this 调用
var playerModel = this.GetModel<PlayerModel>();
var inventoryModel = this.GetModel<InventoryModel>();
}
}
```
#### CanGetSystemExtension
```csharp
public static T GetSystem<T>(this ICanGetSystem self) where T : class, ISystem
```
**使用示例:**
```csharp
public class SaveCommand : AbstractCommand
{
protected override void OnExecute()
{
// 获取系统
var saveSystem = this.GetSystem<SaveSystem>();
var networkSystem = this.GetSystem<NetworkSystem>();
saveSystem.SaveGame();
}
}
```
#### CanGetUtilityExtension
```csharp
public static T GetUtility<T>(this ICanGetUtility self) where T : class, IUtility
```
**使用示例:**
```csharp
public class GameModel : AbstractModel
{
protected override void OnInit()
{
// 获取工具
var timeUtility = this.GetUtility<TimeUtility>();
var storageUtility = this.GetUtility<StorageUtility>();
}
}
```
### 2. 发送命令扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
为 [`ICanSendCommand`](../command/ICanSendCommand.cs) 提供扩展方法。
#### CanSendCommandExtension
```csharp
// 发送无参命令(通过类型)
public static void SendCommand<T>(this ICanSendCommand self)
where T : ICommand, new()
// 发送命令实例
public static void SendCommand<T>(this ICanSendCommand self, T command)
where T : ICommand
// 发送带返回值的命令
public static TResult SendCommand<TResult>(this ICanSendCommand self,
ICommand<TResult> command)
```
**使用示例:**
```csharp
public class GameController : IController
{
public void OnStartButtonClicked()
{
// 方式1通过类型发送需要无参构造函数
this.SendCommand<StartGameCommand>();
// 方式2发送命令实例
this.SendCommand(new LoadLevelCommand(levelId: 1));
// 方式3发送带返回值的命令
var score = this.SendCommand(new CalculateScoreCommand());
}
}
```
### 3. 发送事件扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
为 [`ICanSendEvent`](../events/ICanSendEvent.cs) 提供扩展方法。
#### CanSendEventExtension
```csharp
// 发送无参事件
public static void SendEvent<T>(this ICanSendEvent self) where T : new()
// 发送事件实例
public static void SendEvent<T>(this ICanSendEvent self, T e)
```
**使用示例:**
```csharp
public class PlayerModel : AbstractModel
{
public void TakeDamage(int damage)
{
Health -= damage;
if (Health <= 0)
{
// 方式1发送无参事件
this.SendEvent<PlayerDiedEvent>();
// 方式2发送带数据的事件
this.SendEvent(new PlayerDiedEvent
{
Position = Position,
Cause = "Damage"
});
}
}
}
```
### 4. 发送查询扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
为 [`ICanSendQuery`](../query/ICanSendQuery.cs) 提供扩展方法。
#### CanSendQueryExtension
```csharp
public static TResult SendQuery<TResult>(this ICanSendQuery self,
IQuery<TResult> query)
```
**使用示例:**
```csharp
public class InventoryController : IController
{
public void ShowInventory()
{
// 发送查询获取数据
var items = this.SendQuery(new GetInventoryItemsQuery());
var gold = this.SendQuery(new GetPlayerGoldQuery());
UpdateInventoryUI(items, gold);
}
}
```
### 5. 注册事件扩展 ([`CanRegisterEventExtensions.cs`](CanRegisterEventExtensions.cs))
为 [`ICanRegisterEvent`](../events/ICanRegisterEvent.cs) 提供扩展方法。
#### CanRegisterEventExtensions
```csharp
// 注册事件
public static IUnRegister RegisterEvent<T>(this ICanRegisterEvent self,
Action<T> onEvent)
// 注销事件
public static void UnRegisterEvent<T>(this ICanRegisterEvent self,
Action<T> onEvent)
```
**使用示例:**
```csharp
public class GameController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
{
// 注册事件监听
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<PlayerLevelUpEvent>(OnPlayerLevelUp)
.AddToUnregisterList(_unregisterList);
}
private void OnGameStarted(GameStartedEvent e) { }
private void OnPlayerLevelUp(PlayerLevelUpEvent e) { }
}
```
### 6. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs))
为 [`IEasyEvent`](../events/IEasyEvent.cs) 提供事件组合功能。
#### OrEventExtensions
```csharp
public static OrEvent Or(this IEasyEvent self, IEasyEvent e)
```
**使用示例:**
```csharp
// 组合多个事件:当任意一个触发时执行
var onAnyInput = onKeyPressed.Or(onMouseClicked).Or(onTouchDetected);
onAnyInput.Register(() =>
{
GD.Print("Any input detected!");
});
// 链式组合
var onAnyDamage = onPhysicalDamage
.Or(onMagicDamage)
.Or(onPoisonDamage);
```
### 7. UnRegister 扩展 ([`UnRegisterExtension.cs`](UnRegisterExtension.cs))
为 [`IUnRegister`](../events/IUnRegister.cs) 提供 Godot 生命周期绑定。
#### UnRegisterExtension
```csharp
public static IUnRegister UnRegisterWhenNodeExitTree(this IUnRegister unRegister,
Node node)
```
**使用示例:**
```csharp
#if GODOT
public class PlayerController : Node, IController
{
public override void _Ready()
{
// 当节点退出场景树时自动注销
this.RegisterEvent<GameEvent>(OnGameEvent)
.UnRegisterWhenNodeExitTree(this);
this.GetModel<PlayerModel>()
.Health
.Register(OnHealthChanged)
.UnRegisterWhenNodeExitTree(this);
}
// 不需要手动在 _ExitTree 中注销
}
#endif
```
### 8. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs))
为 [`IUnRegister`](../events/IUnRegister.cs) 和 [`IUnRegisterList`](../events/IUnRegisterList.cs) 提供批量管理功能。
#### UnRegisterListExtension
```csharp
// 添加到注销列表
public static void AddToUnregisterList(this IUnRegister self,
IUnRegisterList unRegisterList)
// 批量注销
public static void UnRegisterAll(this IUnRegisterList self)
```
**使用示例:**
```csharp
public class ComplexController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
{
// 所有注册都添加到列表中
this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<Event2>(OnEvent2)
.AddToUnregisterList(_unregisterList);
this.GetModel<Model1>().Property1.Register(OnProperty1Changed)
.AddToUnregisterList(_unregisterList);
this.GetModel<Model2>().Property2.Register(OnProperty2Changed)
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
// 一次性注销所有
_unregisterList.UnRegisterAll();
}
}
```
## 完整使用示例
### Controller 示例
```csharp
public partial class GameplayController : Node, IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 使用扩展方法获取 Model
var playerModel = this.GetModel<PlayerModel>();
var gameModel = this.GetModel<GameModel>();
// 使用扩展方法注册事件
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
// 监听可绑定属性
playerModel.Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
// 或者使用 Godot 特定的自动注销
gameModel.Score.Register(OnScoreChanged)
.UnRegisterWhenNodeExitTree(this);
}
public override void _Process(double delta)
{
if (Input.IsActionJustPressed("attack"))
{
// 使用扩展方法发送命令
this.SendCommand(new AttackCommand(targetId: 1));
}
if (Input.IsActionJustPressed("use_item"))
{
// 使用扩展方法发送查询
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
if (hasPotion)
{
this.SendCommand<UseHealthPotionCommand>();
}
}
}
private void OnGameStarted(GameStartedEvent e)
{
GD.Print("Game started!");
}
private void OnHealthChanged(int health)
{
UpdateHealthBar(health);
}
private void OnScoreChanged(int score)
{
UpdateScoreDisplay(score);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
}
```
### Command 示例
```csharp
public class ComplexGameCommand : AbstractCommand
{
protected override void OnExecute()
{
// 获取多个组件
var playerModel = this.GetModel<PlayerModel>();
var gameSystem = this.GetSystem<GameSystem>();
var timeUtility = this.GetUtility<TimeUtility>();
// 执行业务逻辑
var currentTime = timeUtility.GetCurrentTime();
gameSystem.ProcessGameLogic(playerModel, currentTime);
// 发送事件通知
this.SendEvent(new GameStateChangedEvent());
// 可以发送其他命令(谨慎使用)
this.SendCommand<SaveGameCommand>();
}
}
```
### System 示例
```csharp
public class AchievementSystem : AbstractSystem
{
protected override void OnInit()
{
// 注册事件监听
this.RegisterEvent<EnemyKilledEvent>(OnEnemyKilled);
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted);
}
private void OnEnemyKilled(EnemyKilledEvent e)
{
// 获取模型
var playerModel = this.GetModel<PlayerModel>();
playerModel.EnemyKillCount++;
// 检查成就
if (playerModel.EnemyKillCount >= 100)
{
// 发送成就解锁事件
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "kill_100_enemies"
});
}
}
private void OnLevelCompleted(LevelCompletedEvent e)
{
// 发送查询
var completionTime = this.SendQuery(new GetLevelTimeQuery(e.LevelId));
if (completionTime < 60)
{
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "speed_runner"
});
}
}
}
```
## 扩展方法的优势
1. **简洁的语法**:不需要显式调用 `GetArchitecture()`
2. **类型安全**:编译时检查类型
3. **可读性高**:代码意图更清晰
4. **智能提示**IDE 可以提供完整的自动补全
5. **链式调用**:支持流式编程风格
## 注意事项
1. **确保引用命名空间**
```csharp
using GFramework.framework.extensions;
```
2. **理解扩展方法本质**
- 扩展方法是静态方法的语法糖
- 不会改变原始类型的结构
- 仅在编译时解析
3. **Godot 特定功能**
- `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用
- 使用 `#if GODOT` 编译指令控制
4. **性能考虑**
- 扩展方法本身无性能开销
- 实际调用的是底层方法
## 相关包
- [`architecture`](../architecture/README.md) - 扩展方法最终调用架构方法
- [`command`](../command/README.md) - 命令发送扩展
- [`query`](../query/README.md) - 查询发送扩展
- [`events`](../events/README.md) - 事件注册和 Or 组合扩展
- [`model`](../model/README.md) - 模型获取扩展
- [`system`](../system/README.md) - 系统获取扩展
- [`utility`](../utility/README.md) - 工具获取扩展

View File

@ -0,0 +1,26 @@
#if GODOT
using Godot;
using GFramework.framework.events;
namespace GFramework.framework.extensions;
/// <summary>
/// 提供取消注册扩展方法的静态类
/// </summary>
public static class UnRegisterExtension
{
/// <summary>
/// 当节点退出场景树时自动取消注册监听器
/// </summary>
/// <param name="unRegister">需要在节点退出时被取消注册的监听器接口实例</param>
/// <param name="node">Godot节点对象当该节点退出场景树时触发取消注册操作</param>
/// <returns>返回传入的原始IUnRegister实例支持链式调用</returns>
public static IUnRegister UnRegisterWhenNodeExitTree(this IUnRegister unRegister, Node node)
{
// 监听节点的TreeExiting事件在节点即将退出场景树时执行取消注册操作
node.TreeExiting += unRegister.UnRegister;
return unRegister;
}
}
#endif

View File

@ -0,0 +1,34 @@
using GFramework.framework.events;
namespace GFramework.framework.extensions;
/// <summary>
/// 扩展方法类为IUnRegister和IUnRegisterList接口提供便捷的注册和注销功能
/// </summary>
public static class UnRegisterListExtension
{
/// <summary>
/// 将指定的可注销对象添加到注销列表中
/// </summary>
/// <param name="self">要添加的可注销对象</param>
/// <param name="unRegisterList">目标注销列表</param>
public static void AddToUnregisterList(this IUnRegister self, IUnRegisterList unRegisterList) =>
unRegisterList.UnregisterList.Add(self);
/// <summary>
/// 注销列表中的所有对象并清空列表
/// </summary>
/// <param name="self">包含注销列表的对象</param>
public static void UnRegisterAll(this IUnRegisterList self)
{
// 遍历注销列表中的所有对象并执行注销操作
foreach (var unRegister in self.UnregisterList)
{
unRegister.UnRegister();
}
// 清空注销列表
self.UnregisterList.Clear();
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
namespace GFramework.framework.ioc;
/// <summary>
/// IOC容器类用于管理对象的注册和获取
/// </summary>
public class IocContainer
{
private readonly Dictionary<Type, object> _mInstances = new();
/// <summary>
/// 注册一个实例到IOC容器中
/// </summary>
/// <typeparam name="T">实例的类型</typeparam>
/// <param name="instance">要注册的实例对象</param>
public void Register<T>(T instance)
{
var key = typeof(T);
_mInstances[key] = instance;
}
/// <summary>
/// 从IOC容器中获取指定类型的实例
/// </summary>
/// <typeparam name="T">要获取的实例类型</typeparam>
/// <returns>返回指定类型的实例如果未找到则返回null</returns>
public T Get<T>() where T : class
{
var key = typeof(T);
// 尝试从字典中获取实例
if (_mInstances.TryGetValue(key, out var retInstance))
{
return retInstance as T;
}
return null;
}
}

446
framework/ioc/README.md Normal file
View File

@ -0,0 +1,446 @@
# IoC 包使用说明
## 概述
IoCInversion of Control控制反转包提供了一个轻量级的依赖注入容器用于管理框架中各种组件的注册和获取。通过 IoC 容器,可以实现组件间的解耦,便于测试和维护。
## 核心类
### [`IocContainer`](IocContainer.cs)
IoC 容器类,负责管理对象的注册和获取。
**主要功能:**
- 注册实例到容器
- 从容器中获取实例
- 类型安全的依赖管理
## 核心方法
### 1. Register<T>
注册一个实例到容器中。
```csharp
public void Register<T>(T instance)
```
**参数:**
- `instance`: 要注册的实例对象
**使用示例:**
```csharp
var container = new IocContainer();
// 注册各种类型的实例
container.Register<IPlayerModel>(new PlayerModel());
container.Register<IGameSystem>(new GameSystem());
container.Register<IStorageUtility>(new StorageUtility());
```
### 2. Get<T>
从容器中获取指定类型的实例。
```csharp
public T Get<T>() where T : class
```
**返回值:**
- 返回指定类型的实例,如果未找到则返回 `null`
**使用示例:**
```csharp
// 获取已注册的实例
var playerModel = container.Get<IPlayerModel>();
var gameSystem = container.Get<IGameSystem>();
// 如果类型未注册,返回 null
var unknownService = container.Get<IUnknownService>(); // null
```
## 在框架中的使用
### Architecture 中的应用
IoC 容器是 [`Architecture`](../architecture/Architecture.cs) 类的核心组件,用于管理所有的 System、Model 和 Utility。
```csharp
public abstract class Architecture<T> : IArchitecture where T : Architecture<T>, new()
{
// 内置 IoC 容器
private readonly IocContainer _mContainer = new();
// 注册系统
public void RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
{
system.SetArchitecture(this);
_mContainer.Register(system); // 注册到容器
// ...
}
// 获取系统
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem
=> _mContainer.Get<TSystem>(); // 从容器获取
// Model 和 Utility 同理
}
```
### 注册组件到容器
```csharp
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 这些方法内部都使用 IoC 容器
// 注册 Model存储游戏数据
RegisterModel<IPlayerModel>(new PlayerModel());
RegisterModel<IInventoryModel>(new InventoryModel());
// 注册 System业务逻辑
RegisterSystem<IGameplaySystem>(new GameplaySystem());
RegisterSystem<ISaveSystem>(new SaveSystem());
// 注册 Utility工具类
RegisterUtility<ITimeUtility>(new TimeUtility());
RegisterUtility<IStorageUtility>(new StorageUtility());
}
}
```
### 从容器获取组件
```csharp
// 通过扩展方法间接使用 IoC 容器
public class PlayerController : IController
{
public void Start()
{
// GetModel 内部调用 Architecture.GetModel
// Architecture.GetModel 内部调用 IocContainer.Get
var playerModel = this.GetModel<IPlayerModel>();
var gameplaySystem = this.GetSystem<IGameplaySystem>();
var timeUtility = this.GetUtility<ITimeUtility>();
}
}
```
## 工作原理
### 内部实现
```csharp
public class IocContainer
{
// 使用字典存储类型到实例的映射
private readonly Dictionary<Type, object> _mInstances = new();
public void Register<T>(T instance)
{
var key = typeof(T);
_mInstances[key] = instance; // 注册或覆盖
}
public T Get<T>() where T : class
{
var key = typeof(T);
if (_mInstances.TryGetValue(key, out var retInstance))
{
return retInstance as T; // 类型转换
}
return null;
}
}
```
### 注册流程
```
用户代码
RegisterSystem<T>(system)
IocContainer.Register<T>(system)
Dictionary[typeof(T)] = system
```
### 获取流程
```
用户代码
this.GetSystem<T>()
Architecture.GetSystem<T>()
IocContainer.Get<T>()
Dictionary.TryGetValue(typeof(T))
返回实例或 null
```
## 使用示例
### 基础使用
```csharp
// 1. 创建容器
var container = new IocContainer();
// 2. 注册服务
var playerService = new PlayerService();
container.Register<IPlayerService>(playerService);
// 3. 获取服务
var service = container.Get<IPlayerService>();
service.DoSomething();
```
### 接口和实现分离
```csharp
// 定义接口
public interface IDataService
{
void SaveData(string data);
string LoadData();
}
// 实现类
public class LocalDataService : IDataService
{
public void SaveData(string data) { /* 本地存储 */ }
public string LoadData() { /* 本地加载 */ return ""; }
}
public class CloudDataService : IDataService
{
public void SaveData(string data) { /* 云端存储 */ }
public string LoadData() { /* 云端加载 */ return ""; }
}
// 注册(可以根据配置选择不同实现)
var container = new IocContainer();
#if CLOUD_SAVE
container.Register<IDataService>(new CloudDataService());
#else
container.Register<IDataService>(new LocalDataService());
#endif
// 使用(不需要关心具体实现)
var dataService = container.Get<IDataService>();
dataService.SaveData("game data");
```
### 覆盖注册
```csharp
var container = new IocContainer();
// 首次注册
container.Register<IConfig>(new DefaultConfig());
// 后续注册会覆盖
container.Register<IConfig>(new CustomConfig());
// 获取到的是最后注册的实例
var config = container.Get<IConfig>(); // CustomConfig
```
## 设计特点
### 1. 简单轻量
- 只有两个核心方法:`Register``Get`
- 基于字典实现,性能高效
- 无复杂的依赖解析逻辑
### 2. 手动注册
- 需要显式注册每个组件
- 不支持自动依赖注入
- 完全可控的组件生命周期
### 3. 单例模式
- 每个类型只能注册一个实例
- 适合管理全局单例服务
- 后续注册会覆盖前面的实例
### 4. 类型安全
- 基于泛型,编译时类型检查
- 避免字符串键导致的错误
- IDE 友好,支持自动补全
## 与其他 IoC 容器的区别
### 本框架的 IocContainer
```csharp
// 简单直接
var container = new IocContainer();
container.Register(new MyService());
var service = container.Get<MyService>();
```
**特点:**
- ✅ 简单易用
- ✅ 性能高
- ❌ 不支持构造函数注入
- ❌ 不支持自动解析依赖
- ❌ 不支持生命周期管理Transient/Scoped/Singleton
### 完整的 IoC 框架(如 Autofac、Zenject
```csharp
// 复杂但功能强大
var builder = new ContainerBuilder();
builder.RegisterType<MyService>().As<IMyService>().SingleInstance();
builder.RegisterType<MyController>().WithParameter("config", config);
var container = builder.Build();
// 自动解析依赖
var controller = container.Resolve<MyController>();
```
**特点:**
- ✅ 自动依赖注入
- ✅ 生命周期管理
- ✅ 复杂场景支持
- ❌ 学习成本高
- ❌ 性能开销大
## 最佳实践
### 1. 在架构初始化时注册
```csharp
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 按顺序注册组件
// 1. 工具类(无依赖)
RegisterUtility(new TimeUtility());
RegisterUtility(new StorageUtility());
// 2. 模型(可能依赖工具)
RegisterModel(new PlayerModel());
RegisterModel(new GameModel());
// 3. 系统(可能依赖模型和工具)
RegisterSystem(new GameplaySystem());
RegisterSystem(new SaveSystem());
}
}
```
### 2. 使用接口类型注册
```csharp
// ❌ 不推荐:直接使用实现类
RegisterSystem(new ConcreteSystem());
var system = GetSystem<ConcreteSystem>();
// ✅ 推荐:使用接口
RegisterSystem<IGameSystem>(new ConcreteSystem());
var system = GetSystem<IGameSystem>();
```
### 3. 避免运行时频繁注册
```csharp
// ❌ 不好:游戏运行时频繁注册
void Update()
{
RegisterService(new TempService()); // 每帧创建
}
// ✅ 好:在初始化时一次性注册
protected override void Init()
{
RegisterService(new PersistentService());
}
```
### 4. 检查 null 返回值
```csharp
// 获取可能不存在的服务
var service = container.Get<IOptionalService>();
if (service != null)
{
service.DoSomething();
}
else
{
GD.Print("Service not registered!");
}
```
## 注意事项
1. **类型唯一性**
- 每个类型只能注册一个实例
- 重复注册会覆盖之前的实例
2. **手动管理依赖顺序**
- 组件的依赖关系需要手动保证
- 先注册被依赖的组件
3. **无生命周期管理**
- 实例一旦注册就一直存在
- 需要手动管理实例的生命周期
4. **线程安全**
- 当前实现非线程安全
- 避免多线程同时访问
## 扩展可能性
如果需要更复杂的功能,可以扩展 `IocContainer`
```csharp
// 支持工厂模式
public class AdvancedIocContainer : IocContainer
{
private Dictionary<Type, Func<object>> _factories = new();
public void RegisterFactory<T>(Func<T> factory) where T : class
{
_factories[typeof(T)] = () => factory();
}
public new T Get<T>() where T : class
{
// 先尝试获取实例
var instance = base.Get<T>();
if (instance != null) return instance;
// 如果没有实例,尝试使用工厂创建
if (_factories.TryGetValue(typeof(T), out var factory))
{
return factory() as T;
}
return null;
}
}
```
## 相关包
- [`architecture`](../architecture/README.md) - 使用 IoC 容器管理所有组件
- [`model`](../model/README.md) - Model 通过 IoC 容器注册和获取
- [`system`](../system/README.md) - System 通过 IoC 容器注册和获取
- [`utility`](../utility/README.md) - Utility 通过 IoC 容器注册和获取

View File

@ -0,0 +1,37 @@
using GFramework.framework.architecture;
using GFramework.framework.model;
namespace GFramework.framework.model;
/// <summary>
/// 抽象模型基类实现IModel接口提供模型的基本架构支持
/// </summary>
public abstract class AbstractModel : IModel
{
/// <summary>
/// 模型所属的架构实例
/// </summary>
protected IArchitecture Architecture;
/// <summary>
/// 获取模型所属的架构实例
/// </summary>
/// <returns>返回当前模型关联的架构对象</returns>
public IArchitecture GetArchitecture() => Architecture;
/// <summary>
/// 设置模型所属的架构实例
/// </summary>
/// <param name="architecture">要关联到此模型的架构实例</param>
public void SetArchitecture(IArchitecture architecture) => Architecture = architecture;
/// <summary>
/// 初始化模型调用抽象方法OnInit执行具体初始化逻辑
/// </summary>
void IModel.Init() => OnInit();
/// <summary>
/// 抽象初始化方法,由子类实现具体的初始化逻辑
/// </summary>
protected abstract void OnInit();
}

View File

@ -0,0 +1,9 @@
using GFramework.framework.rule;
namespace GFramework.framework.model;
/// <summary>
/// 定义一个接口,表示可以获取模型的架构组件。
/// 该接口继承自IBelongToArchitecture表明实现此接口的类型属于特定架构。
/// </summary>
public interface ICanGetModel : IBelongToArchitecture;

16
framework/model/IModel.cs Normal file
View File

@ -0,0 +1,16 @@
using GFramework.framework.events;
using GFramework.framework.rule;
using GFramework.framework.utility;
namespace GFramework.framework.model;
/// <summary>
/// 模型接口,定义了模型的基本行为和功能
/// </summary>
public interface IModel : ICanSetArchitecture, ICanGetUtility, ICanSendEvent
{
/// <summary>
/// 初始化模型
/// </summary>
void Init();
}

140
framework/model/README.md Normal file
View File

@ -0,0 +1,140 @@
# Model 包使用说明
## 概述
Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的 M 层负责管理应用程序的数据和状态。Model 层应该只包含数据和简单的数据逻辑,不包含复杂的业务逻辑。
## 核心接口
### [`IModel`](IModel.cs)
模型接口,定义了模型的基本行为和功能。
**继承的能力接口:**
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构引用
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具类
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
**核心方法:**
```csharp
void Init(); // 初始化模型
```
### [`ICanGetModel`](ICanGetModel.cs)
标记接口,表示实现者可以获取模型。继承自 [`IBelongToArchitecture`](../rule/IBelongToArchitecture.cs)。
## 核心类
### [`AbstractModel`](AbstractModel.cs)
抽象模型基类,提供模型的基础实现。
**使用示例:**
```csharp
public class PlayerModel : AbstractModel
{
// 使用 BindableProperty 定义可监听的属性
public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
protected override void OnInit()
{
// 监听生命值变化
Health.Register(newHealth =>
{
if (newHealth <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
});
}
}
```
## Model 的职责
### ✅ 应该做的事
1. **存储数据和状态**
2. **提供数据访问接口**
3. **监听自身属性变化并做出响应**
4. **发送数据变化事件**
### ❌ 不应该做的事
1. **不包含复杂业务逻辑** - 业务逻辑应该在 System 中
2. **不直接依赖其他 Model** - 通过 Command 协调
3. **不包含 UI 逻辑** - UI 逻辑应该在 Controller 中
## 常见 Model 示例
### 玩家数据模型
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<string> PlayerId { get; } = new("");
public BindableProperty<string> PlayerName { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<int> Gold { get; } = new(0);
public Vector3 Position { get; set; }
protected override void OnInit()
{
Health.Register(hp =>
{
if (hp <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
});
}
}
```
### 游戏状态模型
```csharp
public class GameModel : AbstractModel
{
public BindableProperty<GameState> State { get; } = new(GameState.Menu);
public BindableProperty<int> Score { get; } = new(0);
public BindableProperty<int> HighScore { get; } = new(0);
public BindableProperty<int> CurrentLevel { get; } = new(1);
protected override void OnInit()
{
Score.Register(newScore =>
{
if (newScore > HighScore.Value)
{
HighScore.Value = newScore;
this.SendEvent(new NewHighScoreEvent { Score = newScore });
}
});
}
}
```
## 最佳实践
1. **使用 BindableProperty 存储需要监听的数据**
2. **Model 之间不要直接调用,通过 Command/System 协调**
3. **复杂计算和业务逻辑放在 System 中**
4. **使用事件通知数据的重要变化**
5. **保持 Model 简单纯粹,只做数据管理**
## 相关包
- [`architecture`](../architecture/README.md) - 提供 Model 的注册和获取
- [`property`](../property/README.md) - BindableProperty 用于定义可监听属性
- [`events`](../events/README.md) - Model 发送事件通知变化
- [`utility`](../utility/README.md) - Model 可以使用工具类
- [`extensions`](../extensions/README.md) - 提供 GetModel 等扩展方法

View File

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using GFramework.framework.events;
namespace GFramework.framework.property;
/// <summary>
/// 可绑定属性类,用于实现数据绑定功能
/// </summary>
/// <typeparam name="T">属性值的类型</typeparam>
/// <param name="defaultValue">属性的默认值</param>
public class BindableProperty<T>(T defaultValue = default) : IBindableProperty<T>
{
protected T MValue = defaultValue;
/// <summary>
/// 获取或设置属性值比较器默认使用Equals方法进行比较
/// </summary>
public static Func<T, T, bool> Comparer { get; set; } = (a, b) => a.Equals(b);
/// <summary>
/// 设置自定义比较器
/// </summary>
/// <param name="comparer">用于比较两个值是否相等的函数</param>
/// <returns>当前可绑定属性实例</returns>
public BindableProperty<T> WithComparer(Func<T, T, bool> comparer)
{
Comparer = comparer;
return this;
}
/// <summary>
/// 获取或设置属性值,当值发生变化时会触发注册的回调事件
/// </summary>
public T Value
{
get => GetValue();
set
{
// 使用 default(T) 替代 null 比较,避免 SonarQube 警告
if (EqualityComparer<T>.Default.Equals(value, default) &&
EqualityComparer<T>.Default.Equals(MValue, default))
return;
// 若新值与旧值相等则不执行后续操作
if (!EqualityComparer<T>.Default.Equals(value, default) && Comparer(value, MValue))
return;
SetValue(value);
_mOnValueChanged?.Invoke(value);
}
}
/// <summary>
/// 设置属性值的虚方法,可在子类中重写
/// </summary>
/// <param name="newValue">新的属性值</param>
protected virtual void SetValue(T newValue) => MValue = newValue;
/// <summary>
/// 获取属性值的虚方法,可在子类中重写
/// </summary>
/// <returns>当前属性值</returns>
protected virtual T GetValue() => MValue;
/// <summary>
/// 直接设置属性值而不触发事件
/// </summary>
/// <param name="newValue">新的属性值</param>
public void SetValueWithoutEvent(T newValue) => MValue = newValue;
private Action<T> _mOnValueChanged = (_) => { };
/// <summary>
/// 注册属性值变化事件回调
/// </summary>
/// <param name="onValueChanged">属性值变化时的回调函数</param>
/// <returns>可用于取消注册的接口</returns>
public IUnRegister Register(Action<T> onValueChanged)
{
_mOnValueChanged += onValueChanged;
return new BindablePropertyUnRegister<T>(this, onValueChanged);
}
/// <summary>
/// 注册属性值变化事件回调,并立即调用回调函数传递当前值
/// </summary>
/// <param name="action">属性值变化时的回调函数</param>
/// <returns>可用于取消注册的接口</returns>
public IUnRegister RegisterWithInitValue(Action<T> action)
{
action(MValue);
return Register(action);
}
/// <summary>
/// 取消注册属性值变化事件回调
/// </summary>
/// <param name="onValueChanged">要取消注册的回调函数</param>
public void UnRegister(Action<T> onValueChanged) => _mOnValueChanged -= onValueChanged;
/// <summary>
/// 实现IEasyEvent接口的注册方法将无参事件转换为有参事件处理
/// </summary>
/// <param name="onEvent">无参事件回调</param>
/// <returns>可用于取消注册的接口</returns>
IUnRegister IEasyEvent.Register(Action onEvent)
{
return Register(Action);
void Action(T _) => onEvent();
}
/// <summary>
/// 返回属性值的字符串表示形式
/// </summary>
/// <returns>属性值的字符串表示</returns>
public override string ToString() => Value.ToString();
}

View File

@ -0,0 +1,38 @@
using System;
using GFramework.framework.events;
namespace GFramework.framework.property;
/// <summary>
/// 可绑定属性注销器类,用于取消注册可绑定属性的值变化监听
/// </summary>
/// <typeparam name="T">可绑定属性的值类型</typeparam>
/// <param name="bindableProperty">需要注销的可绑定属性实例</param>
/// <param name="onValueChanged">需要注销的值变化回调函数</param>
public class BindablePropertyUnRegister<T>(BindableProperty<T> bindableProperty, Action<T> onValueChanged)
: IUnRegister
{
/// <summary>
/// 获取或设置可绑定属性实例
/// </summary>
public BindableProperty<T> BindableProperty { get; set; } = bindableProperty;
/// <summary>
/// 获取或设置值变化时的回调函数
/// </summary>
public Action<T> OnValueChanged { get; set; } = onValueChanged;
/// <summary>
/// 执行注销操作,取消注册值变化监听并清理引用
/// </summary>
public void UnRegister()
{
// 调用可绑定属性的注销方法,传入值变化回调函数
BindableProperty.UnRegister(OnValueChanged);
// 清理属性引用
BindableProperty = null;
// 清理回调函数引用
OnValueChanged = null;
}
}

View File

@ -0,0 +1,19 @@
namespace GFramework.framework.property;
/// <summary>
/// 可绑定属性接口,继承自只读可绑定属性接口,提供可读写的属性绑定功能
/// </summary>
/// <typeparam name="T">属性值的类型</typeparam>
public interface IBindableProperty<T> : IReadonlyBindableProperty<T>
{
/// <summary>
/// 获取或设置属性的值
/// </summary>
new T Value { get; set; }
/// <summary>
/// 设置属性值但不触发事件通知
/// </summary>
/// <param name="newValue">要设置的新值</param>
void SetValueWithoutEvent(T newValue);
}

View File

@ -0,0 +1,37 @@
using System;
using GFramework.framework.events;
namespace GFramework.framework.property;
/// <summary>
/// 只读可绑定属性接口,提供属性值的读取和变更监听功能
/// </summary>
/// <typeparam name="T">属性值的类型</typeparam>
public interface IReadonlyBindableProperty<out T> : IEasyEvent
{
/// <summary>
/// 获取属性的当前值
/// </summary>
T Value { get; }
/// <summary>
/// 注册属性值变更回调,并立即执行一次初始值的回调
/// </summary>
/// <param name="action">属性值变更时执行的回调函数,参数为新的属性值</param>
/// <returns>用于取消注册的句柄对象</returns>
IUnRegister RegisterWithInitValue(Action<T> action);
/// <summary>
/// 取消注册属性值变更回调
/// </summary>
/// <param name="onValueChanged">要取消注册的回调函数</param>
void UnRegister(Action<T> onValueChanged);
/// <summary>
/// 注册属性值变更回调
/// </summary>
/// <param name="onValueChanged">属性值变更时执行的回调函数,参数为新的属性值</param>
/// <returns>用于取消注册的句柄对象</returns>
IUnRegister Register(Action<T> onValueChanged);
}

View File

@ -0,0 +1,297 @@
# Property 包使用说明
## 概述
Property 包提供了可绑定属性BindableProperty的实现支持属性值的监听和响应式编程。这是实现 MVVM 模式和数据绑定的核心组件。
## 核心接口
### [`IReadonlyBindableProperty<T>`](IReadonlyBindableProperty.cs)
只读可绑定属性接口,提供属性值的读取和变更监听功能。
**核心成员:**
```csharp
T Value { get; } // 获取属性值
IUnRegister Register(Action<T> onValueChanged); // 注册监听
IUnRegister RegisterWithInitValue(Action<T> action); // 注册并立即回调当前值
void UnRegister(Action<T> onValueChanged); // 取消监听
```
### [`IBindableProperty<T>`](IBindableProperty.cs)
可绑定属性接口,继承自只读接口,增加了修改能力。
**核心成员:**
```csharp
new T Value { get; set; } // 可读写的属性值
void SetValueWithoutEvent(T newValue); // 设置值但不触发事件
```
## 核心类
### [`BindableProperty<T>`](BindableProperty.cs)
可绑定属性的完整实现。
**使用示例:**
```csharp
// 创建可绑定属性
var health = new BindableProperty<int>(100);
// 监听值变化
var unregister = health.Register(newValue =>
{
GD.Print($"Health changed to: {newValue}");
});
// 修改值(会触发监听器)
health.Value = 50; // 输出: Health changed to: 50
// 取消监听
unregister.UnRegister();
// 设置值但不触发事件
health.SetValueWithoutEvent(75);
```
**高级功能:**
```csharp
// 1. 注册并立即获得当前值
health.RegisterWithInitValue(value =>
{
GD.Print($"Current health: {value}"); // 立即输出当前值
// 后续值变化时也会调用
});
// 2. 自定义比较器
var position = new BindableProperty<Vector3>(Vector3.Zero)
.WithComparer((a, b) => a.DistanceTo(b) < 0.01f); // 距离小于0.01认为相等
// 3. 链式调用
health.Value = 100;
```
### [`BindablePropertyUnRegister<T>`](BindablePropertyUnRegister.cs)
可绑定属性的注销器,负责清理监听。
## 在 Model 中使用
### 定义可绑定属性
```csharp
public class PlayerModel : AbstractModel
{
// 可读写属性
public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Level { get; } = new(1);
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
// 只读属性(外部只能读取和监听)
public IReadonlyBindableProperty<int> ReadonlyHealth => Health;
protected override void OnInit()
{
// 内部监听属性变化
Health.Register(hp =>
{
if (hp <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
});
}
}
```
## 在 Controller 中监听
### UI 数据绑定
```csharp
public partial class PlayerUI : Control, IController
{
[Export] private Label _healthLabel;
[Export] private Label _nameLabel;
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
var playerModel = this.GetModel<PlayerModel>();
// 绑定生命值到UI立即显示当前值
playerModel.Health
.RegisterWithInitValue(health =>
{
_healthLabel.Text = $"HP: {health}/{playerModel.MaxHealth.Value}";
})
.AddToUnregisterList(_unregisterList);
// 绑定名称
playerModel.Name
.RegisterWithInitValue(name =>
{
_nameLabel.Text = name;
})
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
{
_unregisterList.UnRegisterAll();
}
}
```
## 常见使用模式
### 1. 双向绑定
```csharp
// Model
public class SettingsModel : AbstractModel
{
public BindableProperty<float> MasterVolume { get; } = new(1.0f);
protected override void OnInit() { }
}
// UI Controller
public partial class VolumeSlider : HSlider, IController
{
private BindableProperty<float> _volumeProperty;
public override void _Ready()
{
_volumeProperty = this.GetModel<SettingsModel>().MasterVolume;
// Model -> UI
_volumeProperty.RegisterWithInitValue(vol => Value = vol)
.UnRegisterWhenNodeExitTree(this);
// UI -> Model
ValueChanged += newValue => _volumeProperty.Value = (float)newValue;
}
}
```
### 2. 计算属性
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> MaxHealth { get; } = new(100);
public BindableProperty<float> HealthPercent { get; } = new(1.0f);
protected override void OnInit()
{
// 自动计算百分比
Action updatePercent = () =>
{
HealthPercent.Value = (float)Health.Value / MaxHealth.Value;
};
Health.Register(_ => updatePercent());
MaxHealth.Register(_ => updatePercent());
updatePercent(); // 初始计算
}
}
```
### 3. 属性验证
```csharp
public class PlayerModel : AbstractModel
{
private BindableProperty<int> _health = new(100);
public BindableProperty<int> Health
{
get => _health;
set
{
// 限制范围
var clampedValue = Math.Clamp(value.Value, 0, MaxHealth.Value);
_health.Value = clampedValue;
}
}
public BindableProperty<int> MaxHealth { get; } = new(100);
protected override void OnInit() { }
}
```
### 4. 条件监听
```csharp
public class CombatController : Node, IController
{
public override void _Ready()
{
var playerModel = this.GetModel<PlayerModel>();
// 只在生命值低于30%时显示警告
playerModel.Health.Register(hp =>
{
if (hp < playerModel.MaxHealth.Value * 0.3f)
{
ShowLowHealthWarning();
}
else
{
HideLowHealthWarning();
}
}).UnRegisterWhenNodeExitTree(this);
}
}
```
## 性能优化
### 1. 避免频繁触发
```csharp
// 使用 SetValueWithoutEvent 批量修改
public void LoadPlayerData(SaveData data)
{
// 临时关闭事件
Health.SetValueWithoutEvent(data.Health);
Mana.SetValueWithoutEvent(data.Mana);
Gold.SetValueWithoutEvent(data.Gold);
// 最后统一触发一次更新事件
this.SendEvent(new PlayerDataLoadedEvent());
}
```
### 2. 自定义比较器
```csharp
// 避免浮点数精度问题导致的频繁触发
var position = new BindableProperty<Vector3>()
.WithComparer((a, b) => a.DistanceTo(b) < 0.001f);
```
## 最佳实践
1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层
2. **使用只读接口暴露** - 防止外部随意修改
3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
## 相关包
- [`model`](../model/README.md) - Model 中大量使用 BindableProperty
- [`events`](../events/README.md) - BindableProperty 基于事件系统实现
- [`controller`](../controller/README.md) - Controller 监听属性变化更新 UI
- [`extensions`](../extensions/README.md) - 提供便捷的注销扩展方法

View File

@ -0,0 +1,36 @@
using GFramework.framework.architecture;
namespace GFramework.framework.query;
/// <summary>
/// 抽象查询类,提供查询操作的基础实现
/// </summary>
/// <typeparam name="T">查询结果的类型</typeparam>
public abstract class AbstractQuery<T> : IQuery<T>
{
/// <summary>
/// 执行查询操作
/// </summary>
/// <returns>查询结果</returns>
public T Do() => OnDo();
/// <summary>
/// 抽象方法,由子类实现具体的查询逻辑
/// </summary>
/// <returns>查询结果</returns>
protected abstract T OnDo();
private IArchitecture _mArchitecture;
/// <summary>
/// 获取架构实例
/// </summary>
/// <returns>架构实例</returns>
public IArchitecture GetArchitecture() => _mArchitecture;
/// <summary>
/// 设置架构实例
/// </summary>
/// <param name="architecture">要设置的架构实例</param>
public void SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture;
}

View File

@ -0,0 +1,9 @@
using GFramework.framework.rule;
namespace GFramework.framework.query;
/// <summary>
/// 定义一个可以发送查询的接口契约
/// 该接口继承自IBelongToArchitecture表示实现此接口的类型属于某个架构组件
/// </summary>
public interface ICanSendQuery : IBelongToArchitecture;

20
framework/query/IQuery.cs Normal file
View File

@ -0,0 +1,20 @@
using GFramework.framework.model;
using GFramework.framework.rule;
using GFramework.framework.system;
namespace GFramework.framework.query;
/// <summary>
/// 查询接口,定义了执行查询操作的契约
/// </summary>
/// <typeparam name="TResult">查询结果的类型</typeparam>
public interface IQuery<out TResult> : ICanSetArchitecture, ICanGetModel, ICanGetSystem,
ICanSendQuery
{
/// <summary>
/// 执行查询操作并返回结果
/// </summary>
/// <returns>查询的结果,类型为 TResult</returns>
TResult Do();
}

395
framework/query/README.md Normal file
View File

@ -0,0 +1,395 @@
# Query 包使用说明
## 概述
Query 包实现了 CQRS命令查询职责分离模式中的查询部分。Query 用于封装数据查询逻辑,与 Command 不同的是Query 有返回值且不应该修改系统状态。
## 核心接口
### [`ICanSendQuery`](ICanSendQuery.cs)
标记接口,表示该类型可以发送查询。
**继承关系:**
```csharp
public interface ICanSendQuery : IBelongToArchitecture
```
### [`IQuery<TResult>`](IQuery.cs)
查询接口,定义了查询的基本契约。
**核心成员:**
```csharp
TResult Do(); // 执行查询并返回结果
```
**继承的能力:**
- `ICanSetArchitecture` - 可设置架构
- `ICanGetModel` - 可获取 Model
- `ICanGetSystem` - 可获取 System
- `ICanSendQuery` - 可发送其他 Query
## 核心类
### [`AbstractQuery<T>`](AbstractQuery.cs)
抽象查询基类,提供了查询的基础实现。
**使用方式:**
```csharp
public abstract class AbstractQuery<T> : IQuery<T>
{
public T Do() => OnDo();
protected abstract T OnDo(); // 子类实现查询逻辑
}
```
## 基本使用
### 1. 定义查询
```csharp
// 查询玩家金币数量
public class GetPlayerGoldQuery : AbstractQuery<int>
{
protected override int OnDo()
{
return this.GetModel<PlayerModel>().Gold.Value;
}
}
// 查询玩家是否死亡
public class IsPlayerDeadQuery : AbstractQuery<bool>
{
protected override bool OnDo()
{
return this.GetModel<PlayerModel>().Health.Value <= 0;
}
}
// 查询背包中指定物品的数量
public class GetItemCountQuery : AbstractQuery<int>
{
public string ItemId { get; set; }
protected override int OnDo()
{
var inventory = this.GetModel<InventoryModel>();
return inventory.GetItemCount(ItemId);
}
}
```
### 2. 发送查询(在 Controller 中)
```csharp
public partial class ShopUI : Control, IController
{
[Export] private Button _buyButton;
[Export] private int _itemPrice = 100;
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
_buyButton.Pressed += OnBuyButtonPressed;
}
private void OnBuyButtonPressed()
{
// 查询玩家金币
int playerGold = this.SendQuery(new GetPlayerGoldQuery());
if (playerGold >= _itemPrice)
{
// 发送购买命令
this.SendCommand(new BuyItemCommand { ItemId = "sword_01" });
}
else
{
GD.Print("金币不足!");
}
}
}
```
### 3. 在 System 中使用
```csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
// 注册事件监听
this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
}
private void OnEnemyAttack(EnemyAttackEvent e)
{
// 查询玩家是否已经死亡
bool isDead = this.SendQuery(new IsPlayerDeadQuery());
if (!isDead)
{
// 执行伤害逻辑
this.SendCommand(new TakeDamageCommand { Damage = e.Damage });
}
}
}
```
## 高级用法
### 1. 带参数的复杂查询
```csharp
// 查询指定范围内的敌人列表
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
{
public Vector3 Center { get; set; }
public float Radius { get; set; }
protected override List<Enemy> OnDo()
{
var enemySystem = this.GetSystem<EnemySpawnSystem>();
return enemySystem.GetEnemiesInRange(Center, Radius);
}
}
// 使用
var enemies = this.SendQuery(new GetEnemiesInRangeQuery
{
Center = playerPosition,
Radius = 10.0f
});
```
### 2. 组合查询
```csharp
// 查询玩家是否可以使用技能
public class CanUseSkillQuery : AbstractQuery<bool>
{
public string SkillId { get; set; }
protected override bool OnDo()
{
var playerModel = this.GetModel<PlayerModel>();
var skillModel = this.GetModel<SkillModel>();
// 查询技能消耗
var skillCost = this.SendQuery(new GetSkillCostQuery { SkillId = SkillId });
// 检查是否满足条件
return playerModel.Mana.Value >= skillCost.ManaCost
&& !this.SendQuery(new IsSkillOnCooldownQuery { SkillId = SkillId });
}
}
public class GetSkillCostQuery : AbstractQuery<SkillCost>
{
public string SkillId { get; set; }
protected override SkillCost OnDo()
{
return this.GetModel<SkillModel>().GetSkillCost(SkillId);
}
}
public class IsSkillOnCooldownQuery : AbstractQuery<bool>
{
public string SkillId { get; set; }
protected override bool OnDo()
{
return this.GetModel<SkillModel>().IsOnCooldown(SkillId);
}
}
```
### 3. 聚合数据查询
```csharp
// 查询玩家战斗力
public class GetPlayerPowerQuery : AbstractQuery<int>
{
protected override int OnDo()
{
var playerModel = this.GetModel<PlayerModel>();
var equipmentModel = this.GetModel<EquipmentModel>();
int basePower = playerModel.Level.Value * 10;
int equipmentPower = equipmentModel.GetTotalPower();
int buffPower = this.SendQuery(new GetBuffPowerQuery());
return basePower + equipmentPower + buffPower;
}
}
// 查询玩家详细信息用于UI显示
public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
{
protected override PlayerInfo OnDo()
{
var playerModel = this.GetModel<PlayerModel>();
return new PlayerInfo
{
Name = playerModel.Name.Value,
Level = playerModel.Level.Value,
Health = playerModel.Health.Value,
MaxHealth = playerModel.MaxHealth.Value,
Gold = this.SendQuery(new GetPlayerGoldQuery()),
Power = this.SendQuery(new GetPlayerPowerQuery())
};
}
}
```
### 4. 跨 System 查询
```csharp
// 在 AI System 中查询玩家状态
public class EnemyAISystem : AbstractSystem
{
protected override void OnInit() { }
public void UpdateEnemyBehavior(Enemy enemy)
{
// 查询玩家位置
var playerPos = this.SendQuery(new GetPlayerPositionQuery());
// 查询玩家是否在攻击范围内
bool inRange = this.SendQuery(new IsPlayerInRangeQuery
{
Position = enemy.Position,
Range = enemy.AttackRange
});
if (inRange)
{
// 查询是否可以攻击
bool canAttack = this.SendQuery(new CanEnemyAttackQuery
{
EnemyId = enemy.Id
});
if (canAttack)
{
this.SendCommand(new EnemyAttackCommand { EnemyId = enemy.Id });
}
}
}
}
```
## Command vs Query
### Command命令
- **用途**:修改系统状态
- **返回值**无返回值void
- **示例**:购买物品、造成伤害、升级角色
### Query查询
- **用途**:读取数据,不修改状态
- **返回值**:有返回值
- **示例**:获取金币数量、检查技能冷却、查询玩家位置
```csharp
// ❌ 错误:在 Query 中修改状态
public class BadQuery : AbstractQuery<int>
{
protected override int OnDo()
{
var model = this.GetModel<PlayerModel>();
model.Gold.Value += 100; // 不应该在 Query 中修改数据!
return model.Gold.Value;
}
}
// ✅ 正确Query 只读取数据
public class GoodQuery : AbstractQuery<int>
{
protected override int OnDo()
{
return this.GetModel<PlayerModel>().Gold.Value;
}
}
// ✅ 修改数据应该使用 Command
public class AddGoldCommand : AbstractCommand
{
public int Amount { get; set; }
protected override void OnExecute()
{
var model = this.GetModel<PlayerModel>();
model.Gold.Value += Amount;
}
}
```
## 最佳实践
1. **查询只读取,不修改** - 保持 Query 的纯粹性
2. **小而专注** - 每个 Query 只负责一个具体的查询任务
3. **可组合** - 复杂查询可以通过组合简单查询实现
4. **避免过度查询** - 如果需要频繁查询,考虑使用 BindableProperty
5. **命名清晰** - Query 名称应该清楚表达查询意图Get、Is、Can、Has等前缀
## 性能优化
### 1. 缓存查询结果
```csharp
// 在 Model 中缓存复杂计算
public class PlayerModel : AbstractModel
{
private int? _cachedPower;
public int GetPower()
{
if (_cachedPower == null)
{
_cachedPower = CalculatePower();
}
return _cachedPower.Value;
}
private int CalculatePower()
{
// 复杂计算...
return 100;
}
public void InvalidatePowerCache()
{
_cachedPower = null;
}
}
```
### 2. 批量查询
```csharp
// 一次查询多个数据,而不是多次单独查询
public class GetMultipleItemCountsQuery : AbstractQuery<Dictionary<string, int>>
{
public List<string> ItemIds { get; set; }
protected override Dictionary<string, int> OnDo()
{
var inventory = this.GetModel<InventoryModel>();
return ItemIds.ToDictionary(id => id, id => inventory.GetItemCount(id));
}
}
```
## 相关包
- [`command`](../command/README.md) - CQRS 的命令部分
- [`model`](../model/README.md) - Query 主要从 Model 获取数据
- [`system`](../system/README.md) - System 中可以发送 Query
- [`controller`](../controller/README.md) - Controller 中可以发送 Query
- [`extensions`](../extensions/README.md) - 提供 SendQuery 扩展方法

View File

@ -0,0 +1,17 @@
using GFramework.framework.architecture;
namespace GFramework.framework.rule;
/// <summary>
/// 定义一个接口,用于标识某个对象属于特定的架构体系。
/// 实现此接口的对象可以通过GetArchitecture方法获取其所属的架构实例。
/// </summary>
public interface IBelongToArchitecture
{
/// <summary>
/// 获取当前对象所属的架构实例。
/// </summary>
/// <returns>返回实现IArchitecture接口的架构实例</returns>
IArchitecture GetArchitecture();
}

View File

@ -0,0 +1,15 @@
using GFramework.framework.architecture;
namespace GFramework.framework.rule;
/// <summary>
/// 定义一个接口,用于设置架构实例
/// </summary>
public interface ICanSetArchitecture
{
/// <summary>
/// 设置架构实例
/// </summary>
/// <param name="architecture">要设置的架构实例</param>
void SetArchitecture(IArchitecture architecture);
}

329
framework/rule/README.md Normal file
View File

@ -0,0 +1,329 @@
# Rule 包使用说明
## 概述
Rule 包定义了框架的核心规则接口,这些接口规定了框架各个组件之间的关系和约束。所有框架组件都需要遵循这些规则接口。
## 核心接口
### [`IBelongToArchitecture`](IBelongToArchitecture.cs)
标记接口,表示某个对象属于特定的架构体系。
**接口定义:**
```csharp
public interface IBelongToArchitecture
{
IArchitecture GetArchitecture();
}
```
**实现此接口的类型:**
- Controller
- System
- Model
- Command
- Query
- Event 处理器
**作用:**
所有实现此接口的类型都能够获取其所属的架构实例,从而访问架构提供的各种能力。
### [`ICanSetArchitecture`](ICanSetArchitecture.cs)
定义可以设置架构实例的能力。
**接口定义:**
```csharp
public interface ICanSetArchitecture
{
void SetArchitecture(IArchitecture architecture);
}
```
**实现此接口的类型:**
- Command
- Query
**作用:**
在 Command 和 Query 执行前,框架会自动调用 `SetArchitecture` 方法注入架构实例,使其能够访问 Model、System 等组件。
## 接口关系图
```
IBelongToArchitecture (属于架构)
↓ 被继承
├── ICanGetModel (可获取 Model)
├── ICanGetSystem (可获取 System)
├── ICanGetUtility (可获取 Utility)
├── ICanSendCommand (可发送 Command)
├── ICanSendEvent (可发送 Event)
├── ICanSendQuery (可发送 Query)
└── ICanRegisterEvent (可注册 Event)
ICanSetArchitecture (可设置架构)
↓ 被继承
├── ICommand (命令接口)
└── IQuery<T> (查询接口)
```
## 使用场景
### 1. Controller 实现 IBelongToArchitecture
```csharp
// Controller 通过实现 IBelongToArchitecture 获得架构访问能力
public partial class PlayerController : Node, IController
{
// 实现 IBelongToArchitecture 接口
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 因为实现了 IBelongToArchitecture所以可以使用扩展方法
var playerModel = this.GetModel<PlayerModel>();
this.SendCommand(new InitPlayerCommand());
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
}
private void OnPlayerDied(PlayerDiedEvent e)
{
GD.Print("Player died!");
}
}
```
### 2. Command 实现 ICanSetArchitecture
```csharp
// Command 实现 ICanSetArchitecture框架会自动注入架构
public class BuyItemCommand : AbstractCommand
{
public string ItemId { get; set; }
protected override void OnExecute()
{
// 框架已经通过 SetArchitecture 注入了架构实例
// 所以这里可以直接使用 this.GetModel
var playerModel = this.GetModel<PlayerModel>();
var shopModel = this.GetModel<ShopModel>();
int price = shopModel.GetItemPrice(ItemId);
if (playerModel.Gold.Value >= price)
{
playerModel.Gold.Value -= price;
this.SendCommand(new AddItemCommand { ItemId = ItemId });
}
}
}
```
### 3. 自定义组件遵循规则
```csharp
// 自定义管理器遵循框架规则
public class SaveManager : IBelongToArchitecture
{
private IArchitecture _architecture;
public SaveManager(IArchitecture architecture)
{
_architecture = architecture;
}
public IArchitecture GetArchitecture() => _architecture;
public void SaveGame()
{
// 因为实现了 IBelongToArchitecture可以使用扩展方法
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
PlayerName = playerModel.Name.Value,
Level = playerModel.Level.Value,
Gold = playerModel.Gold.Value
};
// 保存逻辑...
}
}
```
## 规则约束
### 1. 组件注册规则
```csharp
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// ✅ 正确Model/System/Utility 自动获得架构引用
this.RegisterModel<PlayerModel>(new PlayerModel());
this.RegisterSystem<CombatSystem>(new CombatSystem());
this.RegisterUtility<StorageUtility>(new StorageUtility());
}
}
// Model 继承 AbstractModelAbstractModel 实现了 IBelongToArchitecture
public class PlayerModel : AbstractModel
{
// 无需手动实现 GetArchitecture基类已实现
protected override void OnInit()
{
// 可以直接使用架构能力
this.SendEvent(new PlayerModelInitializedEvent());
}
}
```
### 2. Command/Query 自动注入规则
```csharp
// Controller 中发送 Command
public class ShopUI : Control, IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
private void OnBuyButtonPressed()
{
var command = new BuyItemCommand { ItemId = "sword_01" };
// SendCommand 内部会自动调用 command.SetArchitecture(this.GetArchitecture())
this.SendCommand(command);
}
}
// Command 执行时已经有了架构引用
public class BuyItemCommand : AbstractCommand
{
public string ItemId { get; set; }
protected override void OnExecute()
{
// 此时 GetArchitecture() 已经可用
var model = this.GetModel<ShopModel>();
}
}
```
## 设计模式
### 1. 依赖注入模式
Rule 接口体现了依赖注入DI的思想
```csharp
// 接口定义了"需要什么"
public interface IBelongToArchitecture
{
IArchitecture GetArchitecture();
}
// 框架负责"提供什么"
public static class CanSendExtensions
{
public static void SendCommand<T>(this ICanSendCommand self, T command)
where T : ICommand
{
// 自动注入架构依赖
command.SetArchitecture(self.GetArchitecture());
command.Execute();
}
}
```
### 2. 接口隔离原则ISP
Rule 接口遵循接口隔离原则,每个接口职责单一:
```csharp
// ❌ 不好的设计:一个大接口包含所有能力
public interface IBigInterface
{
IArchitecture GetArchitecture();
void SetArchitecture(IArchitecture architecture);
T GetModel<T>() where T : class, IModel;
T GetSystem<T>() where T : class, ISystem;
void SendCommand<T>(T command) where T : ICommand;
// ... 更多方法
}
// ✅ 好的设计:小接口组合
public interface IBelongToArchitecture { ... } // 只负责获取架构
public interface ICanSetArchitecture { ... } // 只负责设置架构
public interface ICanGetModel { ... } // 只负责获取 Model
public interface ICanSendCommand { ... } // 只负责发送 Command
```
### 3. 组合优于继承
通过接口组合实现不同能力:
```csharp
// Controller 需要获取 Model 和发送 Command
public interface IController : ICanGetModel, ICanGetSystem, ICanSendCommand,
ICanSendQuery, ICanRegisterEvent
{
}
// Command 需要设置架构和获取 Model/System
public interface ICommand : ICanSetArchitecture, ICanGetModel, ICanGetSystem,
ICanSendEvent, ICanSendQuery
{
}
// System 只需要获取其他组件
public interface ISystem : ICanGetModel, ICanGetUtility, ICanGetSystem,
ICanRegisterEvent, ICanSendEvent, ICanSendQuery
{
}
```
## 扩展规则
### 自定义规则接口
```csharp
// 定义新的规则接口
public interface ICanAccessDatabase : IBelongToArchitecture
{
}
// 提供扩展方法
public static class CanAccessDatabaseExtensions
{
public static DatabaseUtility GetDatabase(this ICanAccessDatabase self)
{
return self.GetArchitecture().GetUtility<DatabaseUtility>();
}
}
// 让特定组件实现这个接口
public class DatabaseCommand : AbstractCommand, ICanAccessDatabase
{
protected override void OnExecute()
{
var db = this.GetDatabase();
// 使用数据库...
}
}
```
## 最佳实践
1. **遵循既有规则** - 优先使用框架提供的规则接口
2. **不要绕过规则** - 不要直接存储架构引用,使用接口获取
3. **接口组合** - 通过实现多个小接口获得所需能力
4. **扩展规则** - 需要新能力时,定义新的规则接口
5. **保持一致** - 自定义组件也应遵循相同的规则体系
## 相关包
- [`architecture`](../architecture/README.md) - 定义 IArchitecture 接口
- [`command`](../command/README.md) - Command 实现 ICanSetArchitecture
- [`query`](../query/README.md) - Query 实现 ICanSetArchitecture
- [`controller`](../controller/README.md) - Controller 实现 IBelongToArchitecture
- [`system`](../system/README.md) - System 实现 IBelongToArchitecture
- [`model`](../model/README.md) - Model 实现 IBelongToArchitecture
- [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法

View File

@ -0,0 +1,35 @@
using GFramework.framework.architecture;
using GFramework.framework.rule;
namespace GFramework.framework.system;
/// <summary>
/// 抽象系统基类,实现系统接口的基本功能
/// 提供架构关联和初始化机制
/// </summary>
public abstract class AbstractSystem : ISystem
{
private IArchitecture _mArchitecture;
/// <summary>
/// 获取当前系统所属的架构实例
/// </summary>
/// <returns>返回系统关联的架构对象</returns>
IArchitecture IBelongToArchitecture.GetArchitecture() => _mArchitecture;
/// <summary>
/// 设置系统所属的架构实例
/// </summary>
/// <param name="architecture">要关联的架构对象</param>
void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => _mArchitecture = architecture;
/// <summary>
/// 系统初始化方法,调用抽象初始化方法
/// </summary>
void ISystem.Init() => OnInit();
/// <summary>
/// 抽象初始化方法,由子类实现具体的初始化逻辑
/// </summary>
protected abstract void OnInit();
}

View File

@ -0,0 +1,9 @@
using GFramework.framework.rule;
namespace GFramework.framework.system;
/// <summary>
/// 定义一个接口,表示可以获取系统的对象。
/// 该接口继承自IBelongToArchitecture接口用于标识那些属于系统架构并能够获取系统实例的类型。
/// </summary>
public interface ICanGetSystem : IBelongToArchitecture;

View File

@ -0,0 +1,20 @@
using GFramework.framework.events;
using GFramework.framework.model;
using GFramework.framework.rule;
using GFramework.framework.utility;
namespace GFramework.framework.system;
/// <summary>
/// 系统接口,定义了系统的基本行为和功能
/// 该接口继承了多个框架相关的接口,提供了系统初始化能力
/// </summary>
public interface ISystem : ICanSetArchitecture, ICanGetModel, ICanGetUtility,
ICanRegisterEvent, ICanSendEvent, ICanGetSystem
{
/// <summary>
/// 初始化系统
/// 在系统被创建后调用,用于执行系统的初始化逻辑
/// </summary>
void Init();
}

527
framework/system/README.md Normal file
View File

@ -0,0 +1,527 @@
# System 包使用说明
## 概述
System 包定义了业务逻辑层Business Logic Layer。System 负责处理游戏的核心业务逻辑,协调 Model 之间的交互,响应事件并执行复杂的业务流程。
## 核心接口
### [`ICanGetSystem`](ICanGetSystem.cs)
标记接口,表示该类型可以获取其他 System。
**继承关系:**
```csharp
public interface ICanGetSystem : IBelongToArchitecture
```
### [`ISystem`](ISystem.cs)
System 接口,定义了系统的基本行为。
**核心成员:**
```csharp
void Init(); // 系统初始化方法
```
**继承的能力:**
- `ICanSetArchitecture` - 可设置架构
- `ICanGetModel` - 可获取 Model
- `ICanGetUtility` - 可获取 Utility
- `ICanGetSystem` - 可获取其他 System
- `ICanRegisterEvent` - 可注册事件
- `ICanSendEvent` - 可发送事件
## 核心类
### [`AbstractSystem`](AbstractSystem.cs)
抽象 System 基类,提供了 System 的基础实现。
**使用方式:**
```csharp
public abstract class AbstractSystem : ISystem
{
void ISystem.Init() => OnInit();
protected abstract void OnInit(); // 子类实现初始化逻辑
}
```
## 基本使用
### 1. 定义 System
```csharp
// 战斗系统
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
// 注册事件监听
this.RegisterEvent<EnemyAttackEvent>(OnEnemyAttack);
this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack);
}
private void OnEnemyAttack(EnemyAttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// 计算伤害
int damage = CalculateDamage(e.AttackPower, playerModel.Defense.Value);
// 应用伤害
playerModel.Health.Value -= damage;
// 发送伤害事件
this.SendEvent(new PlayerTookDamageEvent { Damage = damage });
}
private void OnPlayerAttack(PlayerAttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
var enemyModel = this.GetModel<EnemyModel>();
int damage = CalculateDamage(playerModel.AttackPower.Value, e.Enemy.Defense);
e.Enemy.Health -= damage;
this.SendEvent(new EnemyTookDamageEvent
{
EnemyId = e.Enemy.Id,
Damage = damage
});
}
private int CalculateDamage(int attackPower, int defense)
{
return Math.Max(1, attackPower - defense / 2);
}
}
```
### 2. 注册 System
```csharp
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 注册 Model
this.RegisterModel<PlayerModel>(new PlayerModel());
this.RegisterModel<EnemyModel>(new EnemyModel());
// 注册 System系统注册后会自动调用 Init
this.RegisterSystem<CombatSystem>(new CombatSystem());
this.RegisterSystem<InventorySystem>(new InventorySystem());
this.RegisterSystem<QuestSystem>(new QuestSystem());
}
}
```
### 3. 在其他组件中获取 System
```csharp
// 在 Controller 中
public partial class GameController : Node, IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
{
// 获取 System
var combatSystem = this.GetSystem<CombatSystem>();
var questSystem = this.GetSystem<QuestSystem>();
}
}
// 在 Command 中
public class StartBattleCommand : AbstractCommand
{
protected override void OnExecute()
{
var combatSystem = this.GetSystem<CombatSystem>();
// 使用 System...
}
}
```
## 常见使用模式
### 1. 事件驱动的 System
```csharp
public class InventorySystem : AbstractSystem
{
protected override void OnInit()
{
// 监听物品相关事件
this.RegisterEvent<ItemAddedEvent>(OnItemAdded);
this.RegisterEvent<ItemRemovedEvent>(OnItemRemoved);
this.RegisterEvent<ItemUsedEvent>(OnItemUsed);
}
private void OnItemAdded(ItemAddedEvent e)
{
var inventoryModel = this.GetModel<InventoryModel>();
// 添加物品
inventoryModel.AddItem(e.ItemId, e.Count);
// 检查成就
CheckAchievements(e.ItemId);
// 发送通知
this.SendEvent(new ShowNotificationEvent
{
Message = $"获得物品: {e.ItemId} x{e.Count}"
});
}
private void OnItemUsed(ItemUsedEvent e)
{
var inventoryModel = this.GetModel<InventoryModel>();
var playerModel = this.GetModel<PlayerModel>();
if (inventoryModel.HasItem(e.ItemId))
{
// 应用物品效果
ApplyItemEffect(e.ItemId, playerModel);
// 移除物品
inventoryModel.RemoveItem(e.ItemId, 1);
this.SendEvent(new ItemEffectAppliedEvent { ItemId = e.ItemId });
}
}
private void ApplyItemEffect(string itemId, PlayerModel player)
{
// 物品效果逻辑...
if (itemId == "health_potion")
{
player.Health.Value = Math.Min(
player.Health.Value + 50,
player.MaxHealth.Value
);
}
}
private void CheckAchievements(string itemId)
{
// 成就检查逻辑...
}
}
```
### 2. 定时更新的 System
```csharp
public class BuffSystem : AbstractSystem
{
private List<BuffData> _activeBuffs = new();
protected override void OnInit()
{
this.RegisterEvent<BuffAppliedEvent>(OnBuffApplied);
this.RegisterEvent<GameUpdateEvent>(OnUpdate);
}
private void OnBuffApplied(BuffAppliedEvent e)
{
_activeBuffs.Add(new BuffData
{
BuffId = e.BuffId,
Duration = e.Duration,
RemainingTime = e.Duration
});
ApplyBuffEffect(e.BuffId, true);
}
private void OnUpdate(GameUpdateEvent e)
{
// 更新所有 Buff
for (int i = _activeBuffs.Count - 1; i >= 0; i--)
{
var buff = _activeBuffs[i];
buff.RemainingTime -= e.DeltaTime;
if (buff.RemainingTime <= 0)
{
// Buff 过期
ApplyBuffEffect(buff.BuffId, false);
_activeBuffs.RemoveAt(i);
this.SendEvent(new BuffExpiredEvent { BuffId = buff.BuffId });
}
}
}
private void ApplyBuffEffect(string buffId, bool apply)
{
var playerModel = this.GetModel<PlayerModel>();
// 应用或移除 Buff 效果...
}
}
```
### 3. 跨 System 协作
```csharp
public class QuestSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<EnemyKilledEvent>(OnEnemyKilled);
this.RegisterEvent<ItemCollectedEvent>(OnItemCollected);
}
private void OnEnemyKilled(EnemyKilledEvent e)
{
var questModel = this.GetModel<QuestModel>();
var activeQuests = questModel.GetActiveQuests();
foreach (var quest in activeQuests)
{
if (quest.Type == QuestType.KillEnemy && quest.TargetId == e.EnemyType)
{
quest.Progress++;
if (quest.Progress >= quest.RequiredAmount)
{
// 任务完成
CompleteQuest(quest.Id);
}
}
}
}
private void CompleteQuest(string questId)
{
var questModel = this.GetModel<QuestModel>();
var quest = questModel.GetQuest(questId);
// 标记任务完成
questModel.CompleteQuest(questId);
// 发放奖励(通过其他 System
this.SendEvent(new QuestCompletedEvent
{
QuestId = questId,
Rewards = quest.Rewards
});
}
}
public class RewardSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<QuestCompletedEvent>(OnQuestCompleted);
}
private void OnQuestCompleted(QuestCompletedEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// 发放奖励
foreach (var reward in e.Rewards)
{
switch (reward.Type)
{
case RewardType.Gold:
playerModel.Gold.Value += reward.Amount;
break;
case RewardType.Experience:
playerModel.Experience.Value += reward.Amount;
break;
case RewardType.Item:
this.SendEvent(new ItemAddedEvent
{
ItemId = reward.ItemId,
Count = reward.Amount
});
break;
}
}
this.SendEvent(new RewardsGrantedEvent { Rewards = e.Rewards });
}
}
```
### 4. 管理复杂状态机
```csharp
public class GameStateSystem : AbstractSystem
{
private GameState _currentState = GameState.MainMenu;
protected override void OnInit()
{
this.RegisterEvent<GameStateChangeRequestEvent>(OnStateChangeRequest);
}
private void OnStateChangeRequest(GameStateChangeRequestEvent e)
{
if (CanTransition(_currentState, e.TargetState))
{
ExitState(_currentState);
_currentState = e.TargetState;
EnterState(_currentState);
this.SendEvent(new GameStateChangedEvent
{
PreviousState = _currentState,
NewState = e.TargetState
});
}
}
private bool CanTransition(GameState from, GameState to)
{
// 状态转换规则
return (from, to) switch
{
(GameState.MainMenu, GameState.Playing) => true,
(GameState.Playing, GameState.Paused) => true,
(GameState.Paused, GameState.Playing) => true,
(GameState.Playing, GameState.GameOver) => true,
_ => false
};
}
private void EnterState(GameState state)
{
switch (state)
{
case GameState.Playing:
// 开始游戏
this.SendCommand(new StartGameCommand());
break;
case GameState.Paused:
// 暂停游戏
this.SendEvent(new GamePausedEvent());
break;
case GameState.GameOver:
// 游戏结束
this.SendCommand(new GameOverCommand());
break;
}
}
private void ExitState(GameState state)
{
// 清理当前状态
}
}
```
## System vs Model
### Model数据层
- **职责**:存储数据和状态
- **特点**:被动,等待修改
- **示例**PlayerModel、InventoryModel
### System逻辑层
- **职责**:处理业务逻辑,协调 Model
- **特点**:主动,响应事件
- **示例**CombatSystem、QuestSystem
```csharp
// ✅ 正确的职责划分
// Model: 存储数据
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Mana { get; } = new(50);
protected override void OnInit() { }
}
// System: 处理逻辑
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// System 负责计算和决策
int damage = CalculateDamage(e);
playerModel.Health.Value -= damage;
if (playerModel.Health.Value <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
}
}
```
## 最佳实践
1. **单一职责** - 每个 System 专注于一个业务领域
2. **事件驱动** - 通过事件与其他组件通信
3. **无状态或少状态** - 优先将状态存储在 Model 中
4. **可组合** - System 之间通过事件松耦合协作
5. **初始化注册** - 在 `OnInit` 中注册所有事件监听
## 性能优化
### 1. 避免频繁的 GetModel/GetSystem
```csharp
// ❌ 不好:每次都获取
private void OnUpdate(GameUpdateEvent e)
{
var model = this.GetModel<PlayerModel>(); // 频繁调用
// ...
}
// ✅ 好:缓存引用
private PlayerModel _playerModel;
protected override void OnInit()
{
_playerModel = this.GetModel<PlayerModel>(); // 只获取一次
}
private void OnUpdate(GameUpdateEvent e)
{
// 直接使用缓存的引用
_playerModel.Health.Value += 1;
}
```
### 2. 批量处理
```csharp
public class ParticleSystem : AbstractSystem
{
private List<Particle> _particles = new();
private void OnUpdate(GameUpdateEvent e)
{
// 批量更新,而不是每个粒子发一个事件
for (int i = _particles.Count - 1; i >= 0; i--)
{
UpdateParticle(_particles[i], e.DeltaTime);
}
}
}
```
## 相关包
- [`model`](../model/README.md) - System 操作 Model 的数据
- [`events`](../events/README.md) - System 通过事件通信
- [`command`](../command/README.md) - System 中可以发送 Command
- [`query`](../query/README.md) - System 中可以发送 Query
- [`utility`](../utility/README.md) - System 可以使用 Utility
- [`architecture`](../architecture/README.md) - 在架构中注册 System

View File

@ -0,0 +1,9 @@
using GFramework.framework.rule;
namespace GFramework.framework.utility;
/// <summary>
/// 定义一个接口,表示可以获取工具类的对象
/// 该接口继承自IBelongToArchitecture表明实现此接口的类型属于某个架构组件
/// </summary>
public interface ICanGetUtility : IBelongToArchitecture;

View File

@ -0,0 +1,7 @@
namespace GFramework.framework.utility;
/// <summary>
/// IUtility接口定义了通用工具类的基本契约
/// 该接口作为所有工具类实现的基础接口,提供统一的工具方法规范
/// </summary>
public interface IUtility;

584
framework/utility/README.md Normal file
View File

@ -0,0 +1,584 @@
# Utility 包使用说明
## 概述
Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如数学计算、文件操作、序列化等通用工具方法。与 System 不同Utility 不依赖架构状态,是纯粹的工具函数集合。
## 核心接口
### [`ICanGetUtility`](ICanGetUtility.cs)
标记接口,表示该类型可以获取 Utility。
**继承关系:**
```csharp
public interface ICanGetUtility : IBelongToArchitecture
```
### [`IUtility`](IUtility.cs)
Utility 标记接口,所有工具类都应实现此接口。
**接口定义:**
```csharp
public interface IUtility
{
// 标记接口,无方法定义
}
```
## 基本使用
### 1. 定义 Utility
```csharp
// 存储工具类
public class StorageUtility : IUtility
{
private const string SavePath = "user://save_data.json";
public void Save<T>(T data)
{
string json = Json.Stringify(data);
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreString(json);
}
public T Load<T>() where T : new()
{
if (!FileAccess.FileExists(SavePath))
return new T();
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
string json = file.GetAsText();
return Json.Parse<T>(json);
}
public void Delete()
{
if (FileAccess.FileExists(SavePath))
{
DirAccess.RemoveAbsolute(SavePath);
}
}
}
// 数学工具类
public class MathUtility : IUtility
{
public float Lerp(float a, float b, float t)
{
return a + (b - a) * Mathf.Clamp(t, 0f, 1f);
}
public Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, float t)
{
float u = 1 - t;
return u * u * p0 + 2 * u * t * p1 + t * t * p2;
}
public bool IsInRange(Vector3 point, Vector3 center, float radius)
{
return point.DistanceTo(center) <= radius;
}
public int RollDice(int sides)
{
return GD.RandRange(1, sides);
}
}
// 时间工具类
public class TimeUtility : IUtility
{
public string FormatTime(float seconds)
{
int minutes = (int)(seconds / 60);
int secs = (int)(seconds % 60);
return $"{minutes:D2}:{secs:D2}";
}
public long GetCurrentTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
public bool IsExpired(long timestamp, int durationSeconds)
{
return GetCurrentTimestamp() > timestamp + durationSeconds;
}
}
```
### 2. 注册 Utility
```csharp
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 注册 Utility不需要初始化
this.RegisterUtility<StorageUtility>(new StorageUtility());
this.RegisterUtility<MathUtility>(new MathUtility());
this.RegisterUtility<TimeUtility>(new TimeUtility());
}
}
```
### 3. 使用 Utility
```csharp
// 在 System 中使用
public class SaveSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<SaveGameEvent>(OnSaveGame);
this.RegisterEvent<LoadGameEvent>(OnLoadGame);
}
private void OnSaveGame(SaveGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
PlayerName = playerModel.Name.Value,
Level = playerModel.Level.Value,
Gold = playerModel.Gold.Value,
Timestamp = this.GetUtility<TimeUtility>().GetCurrentTimestamp()
};
storage.Save(saveData);
this.SendEvent(new GameSavedEvent());
}
private void OnLoadGame(LoadGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var saveData = storage.Load<SaveData>();
if (saveData != null)
{
var playerModel = this.GetModel<PlayerModel>();
playerModel.Name.Value = saveData.PlayerName;
playerModel.Level.Value = saveData.Level;
playerModel.Gold.Value = saveData.Gold;
this.SendEvent(new GameLoadedEvent());
}
}
}
// 在 Command 中使用
public class MovePlayerCommand : AbstractCommand
{
public Vector3 TargetPosition { get; set; }
public float Speed { get; set; }
protected override void OnExecute()
{
var playerModel = this.GetModel<PlayerModel>();
var mathUtil = this.GetUtility<MathUtility>();
// 使用工具类计算
Vector3 currentPos = playerModel.Position.Value;
Vector3 direction = (TargetPosition - currentPos).Normalized();
Vector3 newPos = currentPos + direction * Speed;
playerModel.Position.Value = newPos;
}
}
```
## 常见 Utility 类型
### 1. 序列化/反序列化工具
```csharp
public class JsonUtility : IUtility
{
public string Serialize<T>(T obj)
{
return Json.Stringify(obj);
}
public T Deserialize<T>(string json) where T : new()
{
return Json.Parse<T>(json);
}
public bool TryDeserialize<T>(string json, out T result) where T : new()
{
try
{
result = Json.Parse<T>(json);
return true;
}
catch
{
result = default;
return false;
}
}
}
```
### 2. 随机数工具
```csharp
public class RandomUtility : IUtility
{
private Random _random = new Random();
public int Range(int min, int max)
{
return _random.Next(min, max + 1);
}
public float Range(float min, float max)
{
return min + (float)_random.NextDouble() * (max - min);
}
public T Choose<T>(params T[] items)
{
return items[Range(0, items.Length - 1)];
}
public List<T> Shuffle<T>(List<T> list)
{
var shuffled = new List<T>(list);
for (int i = shuffled.Count - 1; i > 0; i--)
{
int j = Range(0, i);
(shuffled[i], shuffled[j]) = (shuffled[j], shuffled[i]);
}
return shuffled;
}
public bool Probability(float chance)
{
return _random.NextDouble() < chance;
}
}
```
### 3. 字符串工具
```csharp
public class StringUtility : IUtility
{
public string Truncate(string text, int maxLength, string suffix = "...")
{
if (text.Length <= maxLength)
return text;
return text.Substring(0, maxLength - suffix.Length) + suffix;
}
public string FormatNumber(int number)
{
if (number >= 1000000)
return $"{number / 1000000.0:F1}M";
if (number >= 1000)
return $"{number / 1000.0:F1}K";
return number.ToString();
}
public string ToTitleCase(string text)
{
return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text.ToLower());
}
public bool IsValidEmail(string email)
{
return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}
}
```
### 4. 加密工具
```csharp
public class EncryptionUtility : IUtility
{
private const string EncryptionKey = "YourSecretKey123";
public string Encrypt(string plainText)
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = EncryptBytes(data);
return Convert.ToBase64String(encrypted);
}
public string Decrypt(string encryptedText)
{
byte[] data = Convert.FromBase64String(encryptedText);
byte[] decrypted = DecryptBytes(data);
return System.Text.Encoding.UTF8.GetString(decrypted);
}
private byte[] EncryptBytes(byte[] data)
{
// 简单的 XOR 加密示例(实际项目应使用更安全的算法)
byte[] key = System.Text.Encoding.UTF8.GetBytes(EncryptionKey);
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[i % key.Length]);
}
return result;
}
private byte[] DecryptBytes(byte[] data)
{
return EncryptBytes(data); // XOR 解密与加密相同
}
}
```
### 5. 对象池工具
```csharp
public class ObjectPoolUtility : IUtility
{
private Dictionary<Type, Queue<object>> _pools = new();
public T Get<T>() where T : new()
{
Type type = typeof(T);
if (_pools.ContainsKey(type) && _pools[type].Count > 0)
{
return (T)_pools[type].Dequeue();
}
return new T();
}
public void Return<T>(T obj)
{
Type type = typeof(T);
if (!_pools.ContainsKey(type))
{
_pools[type] = new Queue<object>();
}
_pools[type].Enqueue(obj);
}
public void Clear<T>()
{
Type type = typeof(T);
if (_pools.ContainsKey(type))
{
_pools[type].Clear();
}
}
public void ClearAll()
{
_pools.Clear();
}
}
```
### 6. 日志工具
```csharp
public class LogUtility : IUtility
{
public enum LogLevel
{
Debug,
Info,
Warning,
Error
}
public void Log(string message, LogLevel level = LogLevel.Info)
{
string prefix = level switch
{
LogLevel.Debug => "[DEBUG]",
LogLevel.Info => "[INFO]",
LogLevel.Warning => "[WARN]",
LogLevel.Error => "[ERROR]",
_ => ""
};
string timestamp = DateTime.Now.ToString("HH:mm:ss");
GD.Print($"{timestamp} {prefix} {message}");
}
public void Debug(string message) => Log(message, LogLevel.Debug);
public void Info(string message) => Log(message, LogLevel.Info);
public void Warning(string message) => Log(message, LogLevel.Warning);
public void Error(string message) => Log(message, LogLevel.Error);
}
```
## Utility vs System
### Utility工具层
- **无状态** - 不存储业务数据
- **纯函数** - 相同输入产生相同输出
- **独立性** - 不依赖架构状态
- **可复用** - 可在多个项目中使用
### System逻辑层
- **有状态** - 可能存储临时状态
- **业务逻辑** - 处理特定业务流程
- **架构依赖** - 需要访问 Model
- **项目特定** - 针对特定项目设计
```csharp
// ✅ Utility: 无状态的工具方法
public class MathUtility : IUtility
{
public float CalculateDamage(float attackPower, float defense)
{
return Math.Max(1, attackPower - defense * 0.5f);
}
}
// ✅ System: 有状态的业务逻辑
public class CombatSystem : AbstractSystem
{
private List<CombatInstance> _activeCombats = new();
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var mathUtil = this.GetUtility<MathUtility>();
var playerModel = this.GetModel<PlayerModel>();
// 使用 Utility 计算,但在 System 中处理业务逻辑
float damage = mathUtil.CalculateDamage(e.AttackPower, playerModel.Defense.Value);
playerModel.Health.Value -= (int)damage;
_activeCombats.Add(new CombatInstance { Damage = damage });
}
}
```
## 最佳实践
1. **保持无状态** - Utility 不应存储业务状态
2. **纯函数优先** - 相同输入应产生相同输出
3. **单一职责** - 每个 Utility 专注于一类功能
4. **避免依赖** - 不依赖 Model、System 等架构组件
5. **可测试** - 易于单元测试的纯函数
## 错误示例
```csharp
// ❌ 错误Utility 中存储状态
public class BadUtility : IUtility
{
private int _counter = 0; // 不应该有状态
public int GetNextId()
{
return ++_counter; // 依赖内部状态
}
}
// ❌ 错误Utility 中访问 Model
public class BadUtility2 : IUtility
{
public void DoSomething()
{
// Utility 不应该访问架构组件
var model = this.GetModel<PlayerModel>(); // 编译错误!
}
}
// ✅ 正确:如果需要状态,应该使用 System
public class IdGeneratorSystem : AbstractSystem
{
private int _counter = 0;
protected override void OnInit() { }
public int GetNextId()
{
return ++_counter;
}
}
```
## 性能优化
### 1. 缓存计算结果
```csharp
public class PathfindingUtility : IUtility
{
private Dictionary<(Vector3, Vector3), List<Vector3>> _pathCache = new();
public List<Vector3> FindPath(Vector3 start, Vector3 end, bool useCache = true)
{
var key = (start, end);
if (useCache && _pathCache.ContainsKey(key))
{
return _pathCache[key];
}
var path = CalculatePath(start, end);
if (useCache)
{
_pathCache[key] = path;
}
return path;
}
private List<Vector3> CalculatePath(Vector3 start, Vector3 end)
{
// A* 算法等复杂计算...
return new List<Vector3>();
}
public void ClearCache()
{
_pathCache.Clear();
}
}
```
### 2. 对象复用
```csharp
public class CollectionUtility : IUtility
{
private List<Vector3> _tempList = new();
public List<Vector3> GetPointsInRadius(Vector3 center, float radius, List<Vector3> points)
{
_tempList.Clear();
foreach (var point in points)
{
if (point.DistanceTo(center) <= radius)
{
_tempList.Add(point);
}
}
return new List<Vector3>(_tempList); // 返回副本
}
}
```
## 相关包
- [`system`](../system/README.md) - System 中使用 Utility
- [`command`](../command/README.md) - Command 中可以使用 Utility
- [`architecture`](../architecture/README.md) - 在架构中注册 Utility
- [`ioc`](../ioc/README.md) - Utility 通过 IoC 容器管理
- [`extensions`](../extensions/README.md) - 提供 GetUtility 扩展方法