docs(core): 更新核心模块文档说明

- 移除架构README中接口链接的方括号标记
- 在架构README中添加构造函数参数说明和特性优化
- 移除命令README中继承能力接口的冗余说明
- 重构命令抽象基类为泛型版本并添加输入参数结构
- 更新事件README中接口引用路径和事件总线相关说明
- 重命名事件类名去掉Easy前缀统一为Event
- 更新扩展方法README为ContextAware扩展并添加新的扩展方法
- 移除控制器示例中的Godot特定生命周期方法
- 更新IOC容器README中线程安全实现和方法扩展说明
- [skip ci]
This commit is contained in:
GeWuYou 2026-01-12 11:10:41 +08:00
parent 8adce14b43
commit de1dd9002a
12 changed files with 1129 additions and 605 deletions

View File

@ -8,7 +8,7 @@ Architecture 包是整个框架的核心,提供了基于 MVC 架构模式的
## 核心接口
### [`IArchitecture`](IArchitecture.cs)
### IArchitecture
架构接口,定义了框架的核心功能契约。
@ -28,9 +28,9 @@ void RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem;
void RegisterModel<TModel>(TModel model) where TModel : IModel;
void RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility;
// 组件获取
T GetSystem<T>() where T : class, ISystem;
// 组件获取(通过容器)
T GetModel<T>() where T : class, IModel;
T GetSystem<T>() where T : class, ISystem;
T GetUtility<T>() where T : class, IUtility;
// 命令处理
@ -47,7 +47,7 @@ IUnRegister RegisterEvent<T>(Action<T> onEvent);
void UnRegisterEvent<T>(Action<T> onEvent);
```
### [`IArchitecturePhaseAware`](IArchitecturePhaseAware.cs)
### IArchitecturePhaseAware
架构阶段感知接口,允许组件监听架构阶段变化。
@ -57,7 +57,7 @@ void UnRegisterEvent<T>(Action<T> onEvent);
void OnArchitecturePhase(ArchitecturePhase phase);
```
### [`IArchitectureModule`](IArchitectureModule.cs)
### IArchitectureModule
架构模块接口,支持模块化架构扩展。
@ -67,7 +67,7 @@ void OnArchitecturePhase(ArchitecturePhase phase);
void Install(IArchitecture architecture);
```
### [`IAsyncInitializable`](IAsyncInitializable.cs)
### IAsyncInitializable
异步初始化接口,支持组件异步初始化。
@ -83,6 +83,17 @@ Task InitializeAsync();
架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。
**构造函数参数:**
```csharp
public abstract class Architecture(
IArchitectureConfiguration? configuration = null,
IEnvironment? environment = null,
IArchitectureServices? services = null,
IArchitectureContext? context = null
)
```
**特性:**
- **阶段式生命周期管理**
@ -132,7 +143,7 @@ public enum ArchitecturePhase
**使用示例:**
```csharp
``csharp
// 1. 定义你的架构(继承 Architecture 基类)
public class GameArchitecture : Architecture
{
@ -196,7 +207,7 @@ public class GameController : IController
**高级特性:**
```csharp
``csharp
// 1. 使用自定义配置
var config = new ArchitectureConfiguration();
var architecture = new GameArchitecture(configuration: config);
@ -238,7 +249,7 @@ public class LifecycleHook : IArchitectureLifecycle
**使用示例:**
```csharp
``csharp
var config = new ArchitectureConfiguration
{
// 严格阶段验证

View File

@ -10,16 +10,6 @@ Command 包实现了命令模式Command Pattern用于封装用户操作
无返回值命令接口,定义了命令的基本契约。
**继承的能力接口:**
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构
- [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统
- [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
- [`ICanSendCommand`](ICanSendCommand.cs) - 可发送命令
- [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询
**核心方法:**
```csharp
@ -36,30 +26,39 @@ void Execute(); // 执行命令
TResult Execute(); // 执行命令并返回结果
```
### 3. [`ICanSendCommand`](ICanSendCommand.cs)
标记接口,表示实现者可以发送命令。继承自 [`IBelongToArchitecture`](../rule/IBelongToArchitecture.cs)。
## 核心类
### 1. [`AbstractCommand`](AbstractCommand.cs)
### 1. [`AbstractCommand<TInput>`](AbstractCommand.cs)
无返回值命令的抽象基类,提供了命令的基础实现。
无返回值命令的抽象基类,提供了命令的基础实现。它继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)
,具有上下文感知能力。
**使用示例:**
```csharp
// 定义一个开始游戏的命令
public class StartGameCommand : AbstractCommand
// 定义一个命令输入参数
public struct StartGameCommandInput : ICommandInput
{
protected override void OnExecute()
public int LevelId { get; set; }
public string PlayerName { get; set; }
}
// 定义一个开始游戏的命令
public class StartGameCommand : AbstractCommand<StartGameCommandInput>
{
public StartGameCommand(StartGameCommandInput input) : base(input)
{
}
protected override void OnExecute(StartGameCommandInput input)
{
// 获取需要的模型
var playerModel = this.GetModel<PlayerModel>();
var gameModel = this.GetModel<GameModel>();
// 执行业务逻辑
playerModel.Health.Value = 100;
playerModel.PlayerName.Value = input.PlayerName;
gameModel.CurrentLevel.Value = input.LevelId;
gameModel.GameState.Value = GameState.Playing;
// 发送事件通知其他模块
@ -74,41 +73,44 @@ public class GameController : IController
public void OnStartButtonClicked()
{
// 方式1发送命令实例
this.SendCommand(new StartGameCommand());
// 方式2通过泛型发送需要无参构造函数
this.SendCommand<StartGameCommand>();
// 发送命令实例
this.SendCommand(new StartGameCommand(new StartGameCommandInput
{
LevelId = 1,
PlayerName = "Player1"
}));
}
}
```
### 2. [`AbstractCommand<TResult>`](AbstractCommand.cs)
### 2. [`AbstractCommand<TInput, TResult>`](AbstractCommand.cs)
带返回值命令的抽象基类。
带返回值命令的抽象基类,同样继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)
**使用示例:**
```csharp
// 定义一个计算伤害的命令
public class CalculateDamageCommand : AbstractCommand<int>
// 定义一个计算伤害的命令输入
public struct CalculateDamageCommandInput : ICommandInput
{
private readonly int _attackPower;
private readonly int _defense;
public CalculateDamageCommand(int attackPower, int defense)
public int AttackerAttackPower { get; set; }
public int DefenderDefense { get; set; }
}
// 定义一个计算伤害的命令
public class CalculateDamageCommand : AbstractCommand<CalculateDamageCommandInput, int>
{
public CalculateDamageCommand(CalculateDamageCommandInput input) : base(input)
{
_attackPower = attackPower;
_defense = defense;
}
protected override int OnExecute()
protected override int OnExecute(CalculateDamageCommandInput input)
{
// 获取游戏配置
var config = this.GetModel<GameConfigModel>();
// 计算最终伤害
var baseDamage = _attackPower - _defense;
var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
return (int)finalDamage;
@ -123,9 +125,11 @@ public class CombatSystem : AbstractSystem
public void Attack(Character attacker, Character defender)
{
// 发送命令并获取返回值
var damage = this.SendCommand(
new CalculateDamageCommand(attacker.AttackPower, defender.Defense)
);
var damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput
{
AttackerAttackPower = attacker.AttackPower,
DefenderDefense = defender.Defense
}));
// 应用伤害
defender.Health -= damage;
@ -139,26 +143,78 @@ public class CombatSystem : AbstractSystem
## 命令的生命周期
1. **创建命令**:实例化命令对象,传入必要的参数
2. **设置架构**:框架自动调用 `SetArchitecture()` 设置架构引用
3. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
4. **返回结果**:对于带返回值的命令,返回执行结果
5. **命令销毁**:命令执行完毕后可以被垃圾回收
2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
3. **返回结果**:对于带返回值的命令,返回执行结果
4. **命令销毁**:命令执行完毕后可以被垃圾回收
## CommandBus - 命令总线
### 功能说明
[CommandBus](file:///d:/Project/Rider/GFramework/GFramework.Core/command/CommandBus.cs#L8-L34) 是命令执行的核心组件,负责发送和执行命令。
**主要方法:**
```csharp
void Send(ICommand command); // 发送无返回值命令
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令
```
### 使用示例
```csharp
var commandBus = new CommandBus();
// 发送无返回值命令
commandBus.Send(new StartGameCommand(new StartGameCommandInput()));
// 发送带返回值命令
var result = commandBus.Send(new CalculateDamageCommand(new CalculateDamageCommandInput()));
```
## EmptyCommandInput - 空命令输入
当命令不需要输入参数时,可以使用 [EmptyCommentInput](file:///d:/Project/Rider/GFramework/GFramework.Core/command/EmptyCommentInput.cs#L7-L11)
类:
```csharp
public class SimpleActionCommand : AbstractCommand<EmptyCommentInput>
{
public SimpleActionCommand(EmptyCommentInput input) : base(input)
{
}
protected override void OnExecute(EmptyCommentInput input)
{
// 执行简单操作,无需额外参数
this.SendEvent(new SimpleActionEvent());
}
}
```
## 使用场景
### 1. 用户交互操作
```csharp
// UI 按钮点击
public class SaveGameCommand : AbstractCommand
public struct SaveGameCommandInput : ICommandInput
{
protected override void OnExecute()
public string SaveSlot { get; set; }
}
public class SaveGameCommand : AbstractCommand<SaveGameCommandInput>
{
public SaveGameCommand(SaveGameCommandInput input) : base(input)
{
}
protected override void OnExecute(SaveGameCommandInput input)
{
var saveSystem = this.GetSystem<SaveSystem>();
var playerModel = this.GetModel<PlayerModel>();
saveSystem.SavePlayerData(playerModel);
this.SendEvent(new GameSavedEvent());
saveSystem.SavePlayerData(playerModel, input.SaveSlot);
this.SendEvent(new GameSavedEvent(input.SaveSlot));
}
}
```
@ -166,17 +222,18 @@ public class SaveGameCommand : AbstractCommand
### 2. 业务流程控制
```csharp
// 关卡切换
public class LoadLevelCommand : AbstractCommand
public struct LoadLevelCommandInput : ICommandInput
{
private readonly int _levelId;
public LoadLevelCommand(int levelId)
public int LevelId { get; set; }
}
public class LoadLevelCommand : AbstractCommand<LoadLevelCommandInput>
{
public LoadLevelCommand(LoadLevelCommandInput input) : base(input)
{
_levelId = levelId;
}
protected override void OnExecute()
protected override void OnExecute(LoadLevelCommandInput input)
{
var levelSystem = this.GetSystem<LevelSystem>();
var uiSystem = this.GetSystem<UISystem>();
@ -185,99 +242,50 @@ public class LoadLevelCommand : AbstractCommand
uiSystem.ShowLoadingScreen();
// 加载关卡
levelSystem.LoadLevel(_levelId);
levelSystem.LoadLevel(input.LevelId);
// 发送事件
this.SendEvent(new LevelLoadedEvent(_levelId));
this.SendEvent(new LevelLoadedEvent(input.LevelId));
}
}
```
### 3. 网络请求封装
```csharp
// 登录命令
public class LoginCommand : AbstractCommand<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>`
5. **合理使用返回值**:只在确实需要返回结果时使用 `AbstractCommand<TInput, TResult>`
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand``SavePlayerCommand`
7. **输入参数结构化**:使用 `ICommandInput` 接口的实现类来组织命令参数
## 扩展功能
### 命令撤销/重做(可扩展)
```csharp
// 可撤销命令接口
public interface IUndoableCommand : ICommand
public struct MoveCommandInput : ICommandInput
{
void Undo();
public Vector3 NewPosition { get; set; }
}
// 实现可撤销命令
public class MoveCommand : AbstractCommand, IUndoableCommand
public class MoveCommand : AbstractCommand<MoveCommandInput>, IUndoableCommand
{
private Vector3 _oldPosition;
private Vector3 _newPosition;
protected override void OnExecute()
public MoveCommand(MoveCommandInput input) : base(input)
{
_newPosition = input.NewPosition;
}
protected override void OnExecute(MoveCommandInput input)
{
var player = this.GetModel<PlayerModel>();
_oldPosition = player.Position;
player.Position = _newPosition;
player.Position = input.NewPosition;
}
public void Undo()

View File

@ -0,0 +1,218 @@
# Environment 包使用说明
## 概述
Environment 包定义了环境配置功能。Environment 提供了一个键值对存储系统,用于在运行时存储和获取各种环境特定的值,如配置选项、路径设置等。它允许应用程序在不同环境下灵活调整行为。
## 核心接口
### IEnvironment
环境接口,定义了环境值的存储和获取功能。
**核心成员:**
```csharp
string Name { get; } // 环境名称
T? Get<T>(string key) where T : class; // 根据键获取值
bool TryGet<T>(string key, out T value) where T : class; // 尝试获取值
T GetRequired<T>(string key) where T : class; // 获取必需值(不存在则抛异常)
void Register(string key, object value); // 注册键值对
void Initialize(); // 初始化环境
```
## 核心类
### [`EnvironmentBase`](EnvironmentBase.cs)
环境基础抽象类,实现了 IEnvironment 接口,提供环境值的存储和获取功能。
**使用方式:**
```csharp
public abstract class EnvironmentBase : ContextAwareBase, IEnvironment
{
protected readonly Dictionary<string, object> Values = new(); // 存储环境值
public abstract string Name { get; } // 环境名称
public virtual T? Get<T>(string key) where T : class
{
return TryGet<T>(key, out var value) ? value : null;
}
public virtual bool TryGet<T>(string key, out T value) where T : class
{
if (Values.TryGetValue(key, out var obj) && obj is T typed)
{
value = typed;
return true;
}
value = null!;
return false;
}
public virtual T GetRequired<T>(string key) where T : class
{
if (TryGet<T>(key, out var value))
return value;
throw new InvalidOperationException(
$"Environment [{Name}] missing required value: key='{key}', type='{typeof(T).Name}'");
}
void IEnvironment.Register(string key, object value);
protected void Register(string key, object value); // 注册键值对
public abstract void Initialize(); // 初始化环境
}
```
### [`DefaultEnvironment`](DefaultEnvironment.cs)
默认环境实现类,继承自 EnvironmentBase。
**使用方式:**
```csharp
public class DefaultEnvironment : EnvironmentBase
{
public override string Name { get; } = "Default"; // 环境名称
public override void Initialize()
{
// 默认环境初始化逻辑
}
}
```
## 基本使用
### 1. 定义自定义环境
```csharp
public class GameEnvironment : EnvironmentBase
{
public override string Name { get; } = "Game";
public override void Initialize()
{
// 注册一些环境特定的值
Register("GameMode", "Survival");
Register("MaxPlayers", 4);
Register("ServerAddress", "localhost");
}
// 便捷方法
public string GameMode => Get<string>("GameMode") ?? "Default";
public int MaxPlayers => Get<int>("MaxPlayers") ?? 1;
public string ServerAddress => Get<string>("ServerAddress") ?? "localhost";
}
```
### 2. 在架构中使用环境
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 环境已经在架构初始化过程中自动初始化
// 但是我们可以在需要的时候获取环境值
var gameMode = this.Context.Environment.Get<string>("GameMode");
// 注册模型和系统
RegisterModel(new PlayerModel());
RegisterSystem(new GameplaySystem());
}
}
```
### 3. 使用环境值
```csharp
public class NetworkSystem : AbstractSystem
{
private string _serverAddress;
protected override void OnInit()
{
// 从环境中获取服务器地址
_serverAddress = this.GetContext().Environment.Get<string>("ServerAddress") ?? "localhost";
// 注册事件
this.RegisterEvent<ConnectToServerEvent>(OnConnectToServer);
}
private void OnConnectToServer(ConnectToServerEvent e)
{
// 使用环境中的服务器地址
Connect(_serverAddress, e.Port);
}
private void Connect(string address, int port)
{
// 连接逻辑
}
}
```
## Environment vs Configuration
### Environment环境
- **动态性**:运行时可以更改
- **灵活性**:根据部署环境调整行为
- **存储类型**:运行时值、连接信息等
- **访问方式**:通过键值对访问
### Configuration配置
- **静态性**:通常在启动时确定
- **持久性**:保存在文件或外部源
- **存储类型**:应用设置、参数等
- **访问方式**:通过配置对象访问
## 最佳实践
1. **明确环境名称**:为每个环境提供有意义的名称
2. **类型安全**:使用泛型方法确保类型安全
3. **错误处理**:使用 GetRequired 方法获取必需值
4. **初始化时机**:在架构初始化期间完成环境设置
5. **避免过度使用**:仅存储环境相关的值
## 错误示例
```csharp
// ❌ 错误:获取必需值但不存在
public class BadExampleSystem : AbstractSystem
{
protected override void OnInit()
{
// 如果"RequiredValue"不存在,这会抛出异常
var value = this.GetContext().Environment.GetRequired<string>("RequiredValue");
}
}
// ✅ 正确:安全获取值
public class GoodExampleSystem : AbstractSystem
{
protected override void OnInit()
{
// 使用 TryGet 安全获取值
if (this.GetContext().Environment.TryGet<string>("OptionalValue", out var value))
{
// 处理值
}
// 或者提供默认值
var gameMode = this.GetContext().Environment.Get<string>("GameMode") ?? "Default";
}
}
```
## 相关包
- [`architecture`](../architecture/README.md) - 在架构中使用环境配置
- [`rule`](../rule/README.md) - 环境基类继承自 ContextAwareBase
- [`ioc`](../ioc/README.md) - 环境值可通过IoC容器管理

View File

@ -6,7 +6,7 @@ Events 包提供了一套完整的事件系统实现了观察者模式Obse
## 核心接口
### 1. [`IEasyEvent`](IEasyEvent.cs)
### 1. [`IEvent`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEvent.cs#L7-L11)
基础事件接口,定义了事件注册的基本功能。
@ -16,7 +16,7 @@ Events 包提供了一套完整的事件系统实现了观察者模式Obse
IUnRegister Register(Action onEvent); // 注册事件处理函数
```
### 2. [`IUnRegister`](IUnRegister.cs)
### 2. [`IUnRegister`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegister.cs#L6-L10)
注销接口,用于取消事件注册。
@ -26,20 +26,28 @@ IUnRegister Register(Action onEvent); // 注册事件处理函数
void UnRegister(); // 执行注销操作
```
### 3. [`IUnRegisterList`](IUnRegisterList.cs)
### 3. [
`IUnRegisterList`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegisterList.cs#L6-L10)
注销列表接口,用于批量管理注销对象。
**属性:**
```csharp
List<IUnRegister> UnregisterList { get; } // 获取注销列表
IList<IUnRegister> UnregisterList { get; } // 获取注销列表
```
### 4. 能力接口
### 4. [`IEventBus`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEventBus.cs#L6-L22)
- [`ICanRegisterEvent`](ICanRegisterEvent.cs) - 标记可以注册事件的组件
- [`ICanSendEvent`](ICanSendEvent.cs) - 标记可以发送事件的组件
事件总线接口,提供基于类型的事件发送和注册。
**核心方法:**
```csharp
IUnRegister Register<T>(Action<T> onEvent); // 注册类型化事件
void Send<T>(T e); // 发送事件实例
void Send<T>() where T : new(); // 发送事件(自动创建实例)
```
## 核心类
@ -66,7 +74,7 @@ onClicked.Trigger();
unregister.UnRegister();
```
### 2. [`EasyEvent<T>`](EasyEventGeneric.cs)
### 2. [`Event<T>`](EasyEventGeneric.cs)
单参数泛型事件类,支持一个参数的事件。
@ -74,7 +82,7 @@ unregister.UnRegister();
```csharp
// 创建带参数的事件
var onScoreChanged = new EasyEvent<int>();
var onScoreChanged = new Event<int>();
// 注册监听
onScoreChanged.Register(newScore =>
@ -86,7 +94,7 @@ onScoreChanged.Register(newScore =>
onScoreChanged.Trigger(100);
```
### 3. [`EasyEvent<T, TK>`](EasyEventGeneric.cs)
### 3. [`Event<T, TK>`](EasyEventGeneric.cs)
双参数泛型事件类。
@ -94,7 +102,7 @@ onScoreChanged.Trigger(100);
```csharp
// 伤害事件:攻击者、伤害值
var onDamageDealt = new EasyEvent<string, int>();
var onDamageDealt = new Event<string, int>();
onDamageDealt.Register((attacker, damage) =>
{
@ -104,25 +112,7 @@ onDamageDealt.Register((attacker, 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)
### 4. [`EasyEvents`](EasyEvents.cs)
全局事件管理器,提供类型安全的事件注册和获取。
@ -145,7 +135,7 @@ gameStartEvent.Register(() =>
gameStartEvent.Trigger();
```
### 6. [`TypeEventSystem`](TypeEventSystem.cs)
### 5. [`EventBus`](EventBus.cs)
类型化事件系统,支持基于类型的事件发送和注册。
@ -153,25 +143,25 @@ gameStartEvent.Trigger();
```csharp
// 使用全局事件系统
var eventSystem = TypeEventSystem.Global;
var eventBus = new EventBus();
// 注册类型化事件
eventSystem.Register<PlayerDiedEvent>(e =>
eventBus.Register<PlayerDiedEvent>(e =>
{
GD.Print($"Player died at position: {e.Position}");
});
// 发送事件(自动创建实例)
eventSystem.Send<PlayerDiedEvent>();
// 发送事件(传递实例)
eventSystem.Send(new PlayerDiedEvent
eventBus.Send(new PlayerDiedEvent
{
Position = new Vector3(10, 0, 5)
});
// 发送事件(自动创建实例)
eventBus.Send<PlayerDiedEvent>();
```
### 7. [`DefaultUnRegister`](DefaultUnRegister.cs)
### 6. [`DefaultUnRegister`](DefaultUnRegister.cs)
默认注销器实现,封装注销回调。
@ -185,7 +175,7 @@ var unregister = new DefaultUnRegister(onUnregister);
unregister.UnRegister();
```
### 8. [`OrEvent`](OrEvent.cs)
### 7. [`OrEvent`](OrEvent.cs)
事件或运算组合器,当任意一个事件触发时触发。
@ -204,6 +194,33 @@ onAnyInput.Register(() =>
});
```
### 8. [`UnRegisterList`](UnRegisterList.cs)
批量管理注销对象的列表。
**使用示例:**
```csharp
var unregisterList = new UnRegisterList();
// 添加到列表
someEvent.Register(OnEvent).AddToUnregisterList(unregisterList);
// 批量注销
unregisterList.UnRegisterAll();
```
### 9. [`ArchitectureEvents`](ArchitectureEvents.cs)
定义了架构生命周期相关的事件。
**包含事件:**
- `ArchitectureLifecycleReadyEvent` - 架构生命周期准备就绪
- `ArchitectureDestroyingEvent` - 架构销毁中
- `ArchitectureDestroyedEvent` - 架构已销毁
- `ArchitectureFailedInitializationEvent` - 架构初始化失败
## 在架构中使用事件
### 定义事件类

View File

@ -6,64 +6,69 @@ Extensions 包提供了一系列扩展方法,简化了框架各个接口的使
## 扩展方法类别
### 1. 获取组件扩展 ([`CanGetExtensions.cs`](CanGetExtensions.cs))
### 1. ContextAware 扩展 ([`ContextAwareExtensions.cs`](ContextAwareExtensions.cs))
为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[
`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。
为 [`IContextAware`](../../GFramework.Core.Abstractions/rule/IContextAware.cs)
提供扩展方法,允许直接从实现了 [IContextAware](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs)
的对象获取架构组件。
#### CanGetModelExtension
#### GetSystem 扩展方法
```csharp
public static T GetModel<T>(this ICanGetModel self) where T : class, IModel
public static TSystem? GetSystem<TSystem>(this IContextAware contextAware)
where TSystem : class, ISystem
```
**使用示例:**
```csharp
// 在 Controller、Command、Query 中使用
// 在实现了 IContextAware 的类中使用
public class PlayerController : IController
{
public void UpdateUI()
{
// 直接通过 this 调用
var playerSystem = this.GetSystem<PlayerSystem>();
var inventorySystem = this.GetSystem<InventorySystem>();
}
}
```
#### GetModel 扩展方法
```csharp
public static TModel? GetModel<TModel>(this IContextAware contextAware)
where TModel : class, IModel
```
**使用示例:**
```csharp
public class PlayerController : IController
{
public void UpdateStats()
{
// 获取模型
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();
// 使用模型数据
playerModel.Health += 10;
}
}
```
#### CanGetUtilityExtension
#### GetUtility 扩展方法
```csharp
public static T GetUtility<T>(this ICanGetUtility self) where T : class, IUtility
public static TUtility? GetUtility<TUtility>(this IContextAware contextAware)
where TUtility : class, IUtility
```
**使用示例:**
```csharp
public class GameModel : AbstractModel
public class GameModel : AbstractModel, IContextAware
{
protected override void OnInit()
{
@ -74,24 +79,14 @@ public class GameModel : AbstractModel
}
```
### 2. 发送命令扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
为 [`ICanSendCommand`](../command/ICanSendCommand.cs) 提供扩展方法。
#### CanSendCommandExtension
#### SendCommand 扩展方法
```csharp
// 发送无参命令(通过类型)
public static void SendCommand<T>(this ICanSendCommand self)
where T : ICommand, new()
// 发送命令实例
public static void SendCommand<T>(this ICanSendCommand self, T command)
where T : ICommand
// 发送无返回值的命令
public static void SendCommand(this IContextAware contextAware, ICommand command)
// 发送带返回值的命令
public static TResult SendCommand<TResult>(this ICanSendCommand self,
ICommand<TResult> command)
public static TResult SendCommand<TResult>(this IContextAware contextAware, ICommand<TResult> command)
```
**使用示例:**
@ -101,36 +96,51 @@ public class GameController : IController
{
public void OnStartButtonClicked()
{
// 方式1通过类型发送需要无参构造函数
this.SendCommand<StartGameCommand>();
// 发送命令实例
this.SendCommand(new StartGameCommand());
// 方式2发送命令实例
this.SendCommand(new LoadLevelCommand(levelId: 1));
// 方式3发送带返回值的命令
var score = this.SendCommand(new CalculateScoreCommand());
// 发送带返回值的命令
var result = this.SendCommand(new CalculateScoreCommand());
}
}
```
### 3. 发送事件扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
为 [`ICanSendEvent`](../events/ICanSendEvent.cs) 提供扩展方法。
#### CanSendEventExtension
#### SendQuery 扩展方法
```csharp
// 发送无参事件
public static void SendEvent<T>(this ICanSendEvent self) where T : new()
// 发送事件实例
public static void SendEvent<T>(this ICanSendEvent self, T e)
public static TResult SendQuery<TResult>(this IContextAware contextAware, IQuery<TResult> query)
```
**使用示例:**
```csharp
public class PlayerModel : AbstractModel
public class InventoryController : IController
{
public void ShowInventory()
{
// 发送查询获取数据
var items = this.SendQuery(new GetInventoryItemsQuery());
var gold = this.SendQuery(new GetPlayerGoldQuery());
UpdateInventoryUI(items, gold);
}
}
```
#### SendEvent 扩展方法
```csharp
// 发送无参事件
public static void SendEvent<T>(this IContextAware contextAware) where T : new()
// 发送事件实例
public static void SendEvent<T>(this IContextAware contextAware, T e) where T : class
```
**使用示例:**
```csharp
public class PlayerModel : AbstractModel, IContextAware
{
public void TakeDamage(int damage)
{
@ -152,57 +162,20 @@ public class PlayerModel : AbstractModel
}
```
### 4. 发送查询扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
为 [`ICanSendQuery`](../query/ICanSendQuery.cs) 提供扩展方法。
#### CanSendQueryExtension
#### RegisterEvent 扩展方法
```csharp
public static TResult SendQuery<TResult>(this ICanSendQuery self,
IQuery<TResult> query)
public static IUnRegister RegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> handler)
```
**使用示例:**
```csharp
public class InventoryController : IController
{
public void ShowInventory()
{
// 发送查询获取数据
var items = this.SendQuery(new GetInventoryItemsQuery());
var gold = this.SendQuery(new GetPlayerGoldQuery());
UpdateInventoryUI(items, gold);
}
}
```
### 5. 注册事件扩展 ([`CanRegisterEventExtensions.cs`](CanRegisterEventExtensions.cs))
为 [`ICanRegisterEvent`](../events/ICanRegisterEvent.cs) 提供扩展方法。
#### CanRegisterEventExtensions
```csharp
// 注册事件
public static IUnRegister RegisterEvent<T>(this ICanRegisterEvent self,
Action<T> onEvent)
// 注销事件
public static void UnRegisterEvent<T>(this ICanRegisterEvent self,
Action<T> onEvent)
```
**使用示例:**
```csharp
public class GameController : Node, IController
public class GameController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
public void Initialize()
{
// 注册事件监听
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
@ -217,14 +190,143 @@ public class GameController : Node, IController
}
```
### 6. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs))
#### UnRegisterEvent 扩展方法
为 [`IEasyEvent`](../events/IEasyEvent.cs) 提供事件组合功能。
```csharp
public static void UnRegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> onEvent)
```
### GetEnvironment 扩展方法
```csharp
public static T? GetEnvironment<T>(this IContextAware contextAware) where T : class
public static IEnvironment GetEnvironment(this IContextAware contextAware)
```
### 2. Object 扩展 ([`ObjectExtensions.cs`](ObjectExtensions.cs))
提供基于运行时类型判断的对象扩展方法,用于简化类型分支、链式调用和架构分派逻辑。
#### IfType 扩展方法
```csharp
// 最简单的类型判断
public static bool IfType<T>(this object obj, Action<T> action)
// 带条件的类型判断
public static bool IfType<T>(
this object obj,
Func<T, bool> predicate,
Action<T> action
)
// 条件判断,带不匹配时的处理
public static void IfType<T>(
this object obj,
Action<T> whenMatch,
Action<object>? whenNotMatch = null
)
```
**使用示例:**
```csharp
object obj = new MyRule();
// 简单类型判断
bool executed = obj.IfType<MyRule>(rule =>
{
rule.Initialize();
});
// 带条件的类型判断
obj.IfType<MyRule>(
r => r.Enabled, // 条件
r => r.Execute() // 执行动作
);
// 带不匹配处理的类型判断
obj.IfType<IRule>(
rule => rule.Execute(),
other => Logger.Warn($"Unsupported type: {other.GetType()}")
);
```
#### IfType<T, TResult> 扩展方法
```csharp
public static TResult? IfType<T, TResult>(
this object obj,
Func<T, TResult> func
)
```
**使用示例:**
```csharp
object obj = new MyRule { Name = "TestRule" };
string? name = obj.IfType<MyRule, string>(r => r.Name);
```
#### As 和 Do 扩展方法
```csharp
// 安全类型转换
public static T? As<T>(this object obj) where T : class
// 流式调用
public static T Do<T>(this T obj, Action<T> action)
```
**使用示例:**
```csharp
// 安全类型转换
obj.As<MyRule>()
?.Execute();
// 流式调用
obj.As<MyRule>()
?.Do(r => r.Initialize())
?.Do(r => r.Execute());
// 组合使用
obj.As<MyRule>()
?.Do(rule =>
{
if (rule.Enabled)
rule.Execute();
});
```
#### SwitchType 扩展方法
```csharp
public static void SwitchType(
this object obj,
params (Type type, Action<object> action)[] handlers
)
```
**使用示例:**
```csharp
obj.SwitchType(
(typeof(IRule), o => HandleRule((IRule)o)),
(typeof(ISystem), o => HandleSystem((ISystem)o)),
(typeof(IModel), o => HandleModel((IModel)o))
);
```
### 3. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs))
为 [`IEvent`](../../GFramework.Core.Abstractions/events/IEvent.cs) 提供事件组合功能。
#### OrEventExtensions
```csharp
public static OrEvent Or(this IEasyEvent self, IEasyEvent e)
public static OrEvent Or(this IEvent self, IEvent e)
```
**使用示例:**
@ -244,41 +346,7 @@ var onAnyDamage = onPhysicalDamage
.Or(onPoisonDamage);
```
### 7. UnRegister 扩展 ([`UnRegisterExtension.cs`](UnRegisterExtension.cs))
为 [`IUnRegister`](../events/IUnRegister.cs) 提供 Godot 生命周期绑定。
#### UnRegisterExtension
```csharp
public static IUnRegister UnRegisterWhenNodeExitTree(this IUnRegister unRegister,
Node node)
```
**使用示例:**
```csharp
#if GODOT
public class PlayerController : Node, IController
{
public override void _Ready()
{
// 当节点退出场景树时自动注销
this.RegisterEvent<GameEvent>(OnGameEvent)
.UnRegisterWhenNodeExitTree(this);
this.GetModel<PlayerModel>()
.Health
.Register(OnHealthChanged)
.UnRegisterWhenNodeExitTree(this);
}
// 不需要手动在 _ExitTree 中注销
}
#endif
```
### 8. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs))
### 4. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs))
为 [`IUnRegister`](../events/IUnRegister.cs) 和 [`IUnRegisterList`](../events/IUnRegisterList.cs) 提供批量管理功能。
@ -296,11 +364,11 @@ public static void UnRegisterAll(this IUnRegisterList self)
**使用示例:**
```csharp
public class ComplexController : Node, IController
public class ComplexController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public override void _Ready()
public void Initialize()
{
// 所有注册都添加到列表中
this.RegisterEvent<Event1>(OnEvent1)
@ -316,7 +384,7 @@ public class ComplexController : Node, IController
.AddToUnregisterList(_unregisterList);
}
public override void _ExitTree()
public void Cleanup()
{
// 一次性注销所有
_unregisterList.UnRegisterAll();
@ -329,13 +397,11 @@ public class ComplexController : Node, IController
### Controller 示例
```csharp
public partial class GameplayController : Node, IController
public partial class GameplayController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public override void _Ready()
public void Initialize()
{
// 使用扩展方法获取 Model
var playerModel = this.GetModel<PlayerModel>();
@ -348,34 +414,24 @@ public partial class GameplayController : Node, IController
// 监听可绑定属性
playerModel.Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
// 或者使用 Godot 特定的自动注销
gameModel.Score.Register(OnScoreChanged)
.UnRegisterWhenNodeExitTree(this);
}
public override void _Process(double delta)
public void Process(double delta)
{
if (Input.IsActionJustPressed("attack"))
{
// 使用扩展方法发送命令
this.SendCommand(new AttackCommand(targetId: 1));
}
// 发送命令
this.SendCommand(new AttackCommand(targetId: 1));
if (Input.IsActionJustPressed("use_item"))
// 发送查询
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
if (hasPotion)
{
// 使用扩展方法发送查询
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
if (hasPotion)
{
this.SendCommand<UseHealthPotionCommand>();
}
this.SendCommand<UseHealthPotionCommand>();
}
}
private void OnGameStarted(GameStartedEvent e)
{
GD.Print("Game started!");
Console.WriteLine("Game started!");
}
private void OnHealthChanged(int health)
@ -383,12 +439,7 @@ public partial class GameplayController : Node, IController
UpdateHealthBar(health);
}
private void OnScoreChanged(int score)
{
UpdateScoreDisplay(score);
}
public override void _ExitTree()
public void Cleanup()
{
_unregisterList.UnRegisterAll();
}
@ -467,7 +518,8 @@ public class AchievementSystem : AbstractSystem
## 扩展方法的优势
1. **简洁的语法**:不需要显式调用 `GetArchitecture()`
1. **简洁的语法**
:不需要显式调用 [GetContext()](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L13-L15)
2. **类型安全**:编译时检查类型
3. **可读性高**:代码意图更清晰
4. **智能提示**IDE 可以提供完整的自动补全
@ -475,21 +527,17 @@ public class AchievementSystem : AbstractSystem
## 注意事项
1. **确保引用命名空间**
1. **确保引用命名空间**
```csharp
using GFramework.framework.extensions;
using GFramework.Core.extensions;
```
2. **理解扩展方法本质**
2. **理解扩展方法本质**
- 扩展方法是静态方法的语法糖
- 不会改变原始类型的结构
- 仅在编译时解析
3. **Godot 特定功能**
- `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用
- 使用 `#if GODOT` 编译指令控制
4. **性能考虑**
3. **性能考虑:**
- 扩展方法本身无性能开销
- 实际调用的是底层方法

View File

@ -16,20 +16,24 @@ IoC 容器类,负责管理对象的注册和获取。
- 注册实例到容器
- 从容器中获取实例
- 类型安全的依赖管理
- 线程安全操作
- 容器冻结保护
## 核心方法
### 1. Register<T>
### 1. Register<T> 和 Register(Type, object)
注册一个实例到容器中。
```csharp
public void Register<T>(T instance)
public void Register(Type type, object instance)
```
**参数:**
- `instance`: 要注册的实例对象
- `type`: 要注册的类型(重载方法)
**使用示例:**
@ -42,29 +46,104 @@ container.Register<IGameSystem>(new GameSystem());
container.Register<IStorageUtility>(new StorageUtility());
```
### 2. Get<T>
### 2. RegisterSingleton<T>
注册单例实例到容器中。一个类型只允许一个实例。
```csharp
public void RegisterSingleton<T>(T instance)
```
**参数:**
- `instance`: 要注册的单例实例
**使用示例:**
```csharp
var container = new IocContainer();
// 注册单例
container.RegisterSingleton<IPlayerModel>(new PlayerModel());
```
### 3. RegisterPlurality
注册多个实例,将实例注册到其实现的所有接口和具体类型上。
```csharp
public void RegisterPlurality(object instance)
```
**参数:**
- `instance`: 要注册的实例
### 4. RegisterSystem
注册系统实例,将其绑定到其所有实现的接口上。
```csharp
public void RegisterSystem(ISystem system)
```
**参数:**
- `system`: 系统实例对象
### 5. Get<T> 和 GetAll<T>
从容器中获取指定类型的实例。
```csharp
public T Get<T>() where T : class
public T? Get<T>() where T : class
public IReadOnlyList<T> GetAll<T>() where T : class
```
**返回值:**
- 返回指定类型的实例,如果未找到则返回 `null`
- `Get<T>`: 返回指定类型的实例,如果未找到则返回 `null`
- `GetAll<T>`: 返回指定类型的所有实例列表,如果未找到则返回空数组
**使用示例:**
```csharp
// 获取已注册的实例
var playerModel = container.Get<IPlayerModel>();
var gameSystem = container.Get<IGameSystem>();
var gameSystems = container.GetAll<IGameSystem>();
// 如果类型未注册,返回 null
// 如果类型未注册,Get 返回 nullGetAll 返回空数组
var unknownService = container.Get<IUnknownService>(); // null
```
### 6. GetRequired<T>
获取指定类型的必需实例,如果没有注册或注册了多个实例会抛出异常。
```csharp
public T GetRequired<T>() where T : class
```
**返回值:**
- 返回找到的唯一实例
### 7. GetAllSorted<T>
获取并排序(系统调度专用)。
```csharp
public IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class
```
**参数:**
- `comparison`: 比较器委托,定义排序规则
**返回值:**
- 按指定方式排序后的实例列表
## 在框架中的使用
### Architecture 中的应用
@ -142,27 +221,66 @@ public class PlayerController : IController
```csharp
public class IocContainer
{
// 使用字典存储类型到实例的映射
private readonly Dictionary<Type, object> _mInstances = new();
// 使用字典存储类型到实例集合的映射
private readonly Dictionary<Type, HashSet<object>> _typeIndex = new();
private readonly HashSet<object> _objects = [];
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
private volatile bool _frozen = false;
public void Register<T>(T instance)
{
var key = typeof(T);
_mInstances[key] = instance; // 注册或覆盖
// 获取写锁以确保线程安全
_lock.EnterWriteLock();
try
{
RegisterInternal(typeof(T), instance!);
}
finally
{
_lock.ExitWriteLock();
}
}
public T Get<T>() where T : class
public T? Get<T>() where T : class
{
var key = typeof(T);
if (_mInstances.TryGetValue(key, out var retInstance))
_lock.EnterReadLock();
try
{
return retInstance as T; // 类型转换
if (_typeIndex.TryGetValue(typeof(T), out var set) && set.Count > 0)
{
var result = set.First() as T;
return result;
}
return null;
}
return null;
finally
{
_lock.ExitReadLock();
}
}
private void RegisterInternal(Type type, object instance)
{
if (_frozen) throw new InvalidOperationException("IocContainer is frozen");
_objects.Add(instance);
if (!_typeIndex.TryGetValue(type, out var set))
{
set = [];
_typeIndex[type] = set;
}
set.Add(instance);
}
}
```
### 线程安全机制
容器使用 [ReaderWriterLockSlim](xref:System.Threading.ReaderWriterLockSlim) 来确保线程安全操作,允许多个线程同时读取,但在写入时阻止其他线程访问。
### 注册流程
```
@ -172,7 +290,7 @@ RegisterSystem<T>(system)
IocContainer.Register<T>(system)
Dictionary[typeof(T)] = system
加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet
```
### 获取流程
@ -186,9 +304,9 @@ Architecture.GetSystem<T>()
IocContainer.Get<T>()
Dictionary.TryGetValue(typeof(T))
加读锁 -> Dictionary.TryGetValue(typeof(T)) 获取HashSet
返回实例或 null
返回HashSet中第一个实例或 null
```
## 使用示例
@ -245,27 +363,28 @@ var dataService = container.Get<IDataService>();
dataService.SaveData("game data");
```
### 覆盖注册
### 注册多个实现
```csharp
var container = new IocContainer();
// 首次注册
container.Register<IConfig>(new DefaultConfig());
// 注册多个相同接口的不同实现
container.Register<IDataService>(new LocalDataService());
container.Register<IDataService>(new CloudDataService());
// 后续注册会覆盖
container.Register<IConfig>(new CustomConfig());
// 获取单个实例(返回第一个)
var singleService = container.Get<IDataService>(); // 返回第一个注册的实例
// 获取到的是最后注册的实例
var config = container.Get<IConfig>(); // CustomConfig
// 获取所有实例
var allServices = container.GetAll<IDataService>(); // 返回两个实例的列表
```
## 设计特点
### 1. 简单轻量
- 只有两个核心方法:`Register``Get`
- 基于字典实现,性能高效
- 支持多种注册方式:普通注册、单例注册、多实例注册
- 基于字典和哈希集实现,性能高效
- 无复杂的依赖解析逻辑
### 2. 手动注册
@ -274,11 +393,11 @@ var config = container.Get<IConfig>(); // CustomConfig
- 不支持自动依赖注入
- 完全可控的组件生命周期
### 3. 单例模式
### 3. 多实例支持
- 每个类型只能注册一个实例
- 适合管理全局单例服务
- 后续注册会覆盖前面的实例
- 每个类型可以注册多个实例
- 提供 [GetAll](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L244-L261) 方法获取所有实例
- 提供 [Get](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L208-L225) 方法获取单个实例
### 4. 类型安全
@ -286,6 +405,17 @@ var config = container.Get<IConfig>(); // CustomConfig
- 避免字符串键导致的错误
- IDE 友好,支持自动补全
### 5. 线程安全
- 使用读写锁确保多线程环境下的安全操作
- 读操作可以并发执行
- 写操作独占锁,防止并发修改冲突
### 6. 容器冻结
- 提供 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372) 方法,防止进一步修改容器内容
- 防止在初始化后意外修改注册内容
## 与其他 IoC 容器的区别
### 本框架的 IocContainer
@ -301,6 +431,8 @@ var service = container.Get<MyService>();
- ✅ 简单易用
- ✅ 性能高
- ✅ 线程安全
- ✅ 支持多实例
- ❌ 不支持构造函数注入
- ❌ 不支持自动解析依赖
- ❌ 不支持生命周期管理Transient/Scoped/Singleton
@ -394,56 +526,37 @@ else
}
```
## 注意事项
1. **类型唯一性**
- 每个类型只能注册一个实例
- 重复注册会覆盖之前的实例
2. **手动管理依赖顺序**
- 组件的依赖关系需要手动保证
- 先注册被依赖的组件
3. **无生命周期管理**
- 实例一旦注册就一直存在
- 需要手动管理实例的生命周期
4. **线程安全**
- 当前实现非线程安全
- 避免多线程同时访问
## 扩展可能性
如果需要更复杂的功能,可以扩展 `IocContainer`
### 5. 合理使用容器冻结
```csharp
// 支持工厂模式
public class AdvancedIocContainer : IocContainer
// 在架构初始化完成后冻结容器,防止意外修改
protected override void OnInit()
{
private Dictionary<Type, Func<object>> _factories = new();
// 注册所有组件
RegisterModel(new PlayerModel());
RegisterSystem(new GameSystem());
// ...
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;
}
// 冻结容器
Container.Freeze(); // 此后无法再注册新组件
}
```
## 注意事项
1. **线程安全操作**:容器内部使用读写锁确保线程安全,无需额外同步
2. **容器冻结**:一旦调用 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372)
方法,将不能再注册新实例
3. **单例注册限制
**[RegisterSingleton](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L84-L106)
方法确保一个类型只能有一个实例,重复注册会抛出异常
4. **内存管理**:容器持有的实例不会自动释放,需要注意内存泄漏问题
5. **注册顺序**:组件的依赖关系需要手动保证,先注册被依赖的组件
## 相关包
- [`architecture`](../architecture/README.md) - 使用 IoC 容器管理所有组件

View File

@ -6,7 +6,7 @@ Logging 包提供了灵活的日志系统,支持多级别日志记录。默认
## 核心接口
### [`ILogger`](ILogger.cs)
### [ILogger](ILogger.cs)
日志记录器接口,定义了日志记录的基本功能。
@ -62,7 +62,7 @@ void Fatal(string msg, Exception t);
string Name();
```
### [`ILoggerFactory`](ILoggerFactory.cs)
### [ILoggerFactory](ILoggerFactory.cs)
日志工厂接口,用于创建日志记录器实例。
@ -72,7 +72,7 @@ string Name();
ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info);
```
### [`ILoggerFactoryProvider`](ILoggerFactory.cs)
### [ILoggerFactoryProvider](ILoggerFactoryProvider.cs)
日志工厂提供程序接口,用于获取日志工厂。
@ -83,7 +83,7 @@ ILoggerFactory GetLoggerFactory();
ILogger CreateLogger(string name);
```
### [`LogLevel`](LogLevel.cs)
### [LogLevel](LogLevel.cs)
日志级别枚举。
@ -101,7 +101,7 @@ public enum LogLevel
## 核心类
### [`AbstractLogger`](AbstractLogger.cs)
### [AbstractLogger](AbstractLogger.cs)
抽象日志基类,封装了日志级别判断、格式化与异常处理逻辑。平台日志器只需实现 `Write` 方法即可。
@ -127,7 +127,7 @@ public class CustomLogger : AbstractLogger
}
```
### [`ConsoleLogger`](ConsoleLogger.cs)
### [ConsoleLogger](ConsoleLogger.cs)
控制台日志记录器实现,支持彩色输出。
@ -162,7 +162,14 @@ logger.Fatal("致命错误");
- **Error**: 红色
- **Fatal**: 洋红色
### [`ConsoleLoggerFactory`](ConsoleLoggerFactory.cs)
**构造函数参数:**
- `name`:日志器名称,默认为 "ROOT"
- `minLevel`:最低日志级别,默认为 LogLevel.Info
- `writer`TextWriter 输出流,默认为 Console.Out
- `useColors`:是否使用颜色,默认为 true仅在输出到控制台时生效
### [ConsoleLoggerFactory](ConsoleLoggerFactory.cs)
控制台日志工厂,用于创建控制台日志记录器实例。
@ -174,7 +181,7 @@ var logger = factory.GetLogger("MyModule", LogLevel.Debug);
logger.Info("日志记录器创建成功");
```
### [`ConsoleLoggerFactoryProvider`](ConsoleLoggerFactoryProvider.cs)
### [ConsoleLoggerFactoryProvider](ConsoleLoggerFactoryProvider.cs)
控制台日志工厂提供程序实现。
@ -182,11 +189,12 @@ logger.Info("日志记录器创建成功");
```csharp
var provider = new ConsoleLoggerFactoryProvider();
var factory = provider.GetLoggerFactory();
var logger = factory.GetLogger("MyApp", LogLevel.Info);
provider.MinLevel = LogLevel.Debug; // 设置最低日志级别
var logger = provider.CreateLogger("MyApp");
logger.Info("应用程序启动");
```
### [`LoggerFactoryResolver`](LoggerFactoryResolver.cs)
### [LoggerFactoryResolver](LoggerFactoryResolver.cs)
日志工厂提供程序解析器,用于管理和提供日志工厂提供程序实例。
@ -270,9 +278,9 @@ public class DebugLogger : AbstractLogger
// 只输出调试及更高级别的日志
if (level >= LogLevel.Debug)
{
GD.Print($"[{level}] {message}");
Console.WriteLine($"[{level}] {message}");
if (exception != null)
GD.Print(exception);
Console.WriteLine(exception);
}
}
}
@ -341,15 +349,16 @@ public class DebugLogger : AbstractLogger
- 支持字符串格式化参数
- 支持异常信息传递
3. **自定义比较器**
- 在 `BindableProperty` 中可以使用 `WithComparer` 设置自定义比较器
3. **ConsoleLogger 的额外参数**
- ConsoleLogger 现在支持自定义TextWriter输出流
- 支持禁用颜色输出的功能useColors参数
## 相关包
- [`architecture`](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
- [`property`](../property/README.md) - 可绑定属性基于事件系统实现
- [`extensions`](../extensions/README.md) - 提供便捷的扩展方法
- [architecture](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
- [property](../property/README.md) - 可绑定属性基于事件系统实现
- [extensions](../extensions/README.md) - 提供便捷的扩展方法
---
**许可证**: Apache 2.0
**许可证**: Apache 2.0

View File

@ -13,14 +13,14 @@ Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的
**继承的能力接口:**
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构引用
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具类
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
- [`IContextAware`](../rule/IContextAware.cs) - 上下文感知接口
- [`ILogAware`](../rule/ILogAware.cs) - 日志感知接口
**核心方法:**
```csharp
void Init(); // 初始化模型
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
```
### [`ICanGetModel`](ICanGetModel.cs)
@ -31,7 +31,8 @@ void Init(); // 初始化模型
### [`AbstractModel`](AbstractModel.cs)
抽象模型基类,提供模型的基础实现。
抽象模型基类实现IModel接口提供模型的基础实现。该类继承自[
`ContextAwareBase`](file:///d:/Project/Rider/GFramework/GFramework.Core/rule/ContextAwareBase.cs#L11-L37),提供了上下文感知能力。
**使用示例:**
@ -55,6 +56,20 @@ public class PlayerModel : AbstractModel
}
});
}
public override void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.Initializing:
// 架构初始化阶段的处理
break;
case ArchitecturePhase.Ready:
// 架构就绪阶段的处理
break;
// ... 其他阶段处理
}
}
}
```
@ -66,6 +81,7 @@ public class PlayerModel : AbstractModel
2. **提供数据访问接口**
3. **监听自身属性变化并做出响应**
4. **发送数据变化事件**
5. **处理架构生命周期事件**
### ❌ 不应该做的事
@ -99,6 +115,19 @@ public class PlayerModel : AbstractModel
}
});
}
public override void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.Ready:
// 模型准备好后的处理
_log?.Log("PlayerModel is ready.");
break;
default:
break;
}
}
}
```
@ -123,6 +152,18 @@ public class GameModel : AbstractModel
}
});
}
public override void OnArchitecturePhase(ArchitecturePhase phase)
{
switch (phase)
{
case ArchitecturePhase.ShuttingDown:
// 游戏模型清理工作
break;
default:
break;
}
}
}
```

View File

@ -7,17 +7,7 @@ Query 包实现了 CQRS命令查询职责分离模式中的查询部分。
## 核心接口
### [`ICanSendQuery`](ICanSendQuery.cs)
标记接口,表示该类型可以发送查询。
**继承关系:**
```csharp
public interface ICanSendQuery : IBelongToArchitecture
```
### [`IQuery<TResult>`](IQuery.cs)
### IQuery<TResult>
查询接口,定义了查询的基本契约。
@ -27,26 +17,50 @@ public interface ICanSendQuery : IBelongToArchitecture
TResult Do(); // 执行查询并返回结果
```
**继承的能力:**
- `ICanSetArchitecture` - 可设置架构
- `ICanGetModel` - 可获取 Model
- `ICanGetSystem` - 可获取 System
- `ICanSendQuery` - 可发送其他 Query
## 核心类
### [`AbstractQuery<T>`](AbstractQuery.cs)
### [`AbstractQuery<TInput, TResult>`](AbstractQuery.cs)
抽象查询基类,提供了查询的基础实现。
抽象查询基类,提供了查询的基础实现。它接受一个泛型输入参数 TInput该参数必须实现 IQueryInput 接口。
**使用方式:**
```csharp
public abstract class AbstractQuery<T> : IQuery<T>
public abstract class AbstractQuery<TInput, TResult>(TInput input) : ContextAwareBase, IQuery<TResult>
where TInput : IQueryInput
{
public T Do() => OnDo();
protected abstract T OnDo(); // 子类实现查询逻辑
public TResult Do() => OnDo(input); // 执行查询,传入输入参数
protected abstract TResult OnDo(TInput input); // 子类实现查询逻辑
}
```
### [`EmptyQueryInput`](EmptyQueryInput.cs)
空查询输入类,用于表示不需要任何输入参数的查询操作。
**使用方式:**
```csharp
public sealed class EmptyQueryInput : IQueryInput
{
// 作为占位符使用,适用于那些不需要额外输入参数的查询场景
}
```
### [`QueryBus`](QueryBus.cs)
查询总线实现,负责执行查询并返回结果。
**使用方式:**
```csharp
public sealed class QueryBus : IQueryBus
{
public TResult Send<TResult>(IQuery<TResult> query)
{
ArgumentNullException.ThrowIfNull(query);
return query.Do();
}
}
```
@ -54,41 +68,58 @@ public abstract class AbstractQuery<T> : IQuery<T>
### 1. 定义查询
```csharp
``csharp
// 定义查询输入参数
public record GetPlayerGoldQueryInput : IQueryInput;
// 查询玩家金币数量
public class GetPlayerGoldQuery : AbstractQuery<int>
public class GetPlayerGoldQuery : AbstractQuery<GetPlayerGoldQueryInput, int>
{
protected override int OnDo()
public GetPlayerGoldQuery() : base(new EmptyQueryInput())
{
}
protected override int OnDo(GetPlayerGoldQueryInput input)
{
return this.GetModel<PlayerModel>().Gold.Value;
}
}
// 查询玩家是否死亡
public class IsPlayerDeadQuery : AbstractQuery<bool>
public record IsPlayerDeadQueryInput : IQueryInput;
public class IsPlayerDeadQuery : AbstractQuery<IsPlayerDeadQueryInput, bool>
{
protected override bool OnDo()
public IsPlayerDeadQuery() : base(new EmptyQueryInput())
{
}
protected override bool OnDo(IsPlayerDeadQueryInput input)
{
return this.GetModel<PlayerModel>().Health.Value <= 0;
}
}
// 查询背包中指定物品的数量
public class GetItemCountQuery : AbstractQuery<int>
public record GetItemCountQueryInput(string ItemId) : IQueryInput;
public class GetItemCountQuery : AbstractQuery<GetItemCountQueryInput, int>
{
public string ItemId { get; set; }
protected override int OnDo()
public GetItemCountQuery(string itemId) : base(new GetItemCountQueryInput(itemId))
{
}
protected override int OnDo(GetItemCountQueryInput input)
{
var inventory = this.GetModel<InventoryModel>();
return inventory.GetItemCount(ItemId);
return inventory.GetItemCount(input.ItemId);
}
}
```
### 2. 发送查询(在 Controller 中)
```csharp
``csharp
public partial class ShopUI : Control, IController
{
[Export] private Button _buyButton;
@ -121,7 +152,7 @@ public partial class ShopUI : Control, IController
### 3. 在 System 中使用
```csharp
``csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
@ -148,7 +179,7 @@ public class CombatSystem : AbstractSystem
### 1. 带参数的复杂查询
```csharp
``csharp
// 查询指定范围内的敌人列表
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
{
@ -172,7 +203,7 @@ var enemies = this.SendQuery(new GetEnemiesInRangeQuery
### 2. 组合查询
```csharp
``csharp
// 查询玩家是否可以使用技能
public class CanUseSkillQuery : AbstractQuery<bool>
{
@ -215,7 +246,7 @@ public class IsSkillOnCooldownQuery : AbstractQuery<bool>
### 3. 聚合数据查询
```csharp
``csharp
// 查询玩家战斗力
public class GetPlayerPowerQuery : AbstractQuery<int>
{
@ -254,7 +285,7 @@ public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
### 4. 跨 System 查询
```csharp
``csharp
// 在 AI System 中查询玩家状态
public class EnemyAISystem : AbstractSystem
{
@ -303,7 +334,7 @@ public class EnemyAISystem : AbstractSystem
- **返回值**:有返回值
- **示例**:获取金币数量、检查技能冷却、查询玩家位置
```csharp
``csharp
// ❌ 错误:在 Query 中修改状态
public class BadQuery : AbstractQuery<int>
{
@ -349,7 +380,7 @@ public class AddGoldCommand : AbstractCommand
### 1. 缓存查询结果
```csharp
``csharp
// 在 Model 中缓存复杂计算
public class PlayerModel : AbstractModel
{
@ -379,7 +410,7 @@ public class PlayerModel : AbstractModel
### 2. 批量查询
```csharp
``csharp
// 一次查询多个数据,而不是多次单独查询
public class GetMultipleItemCountsQuery : AbstractQuery<Dictionary<string, int>>
{

View File

@ -6,85 +6,84 @@ Rule 包定义了框架的核心规则接口,这些接口规定了框架各个
## 核心接口
### [`IBelongToArchitecture`](IBelongToArchitecture.cs)
### IContextAware
标记接口,表示某个对象属于特定的架构体系
标记接口,表示该类型可以感知架构上下文
**接口定义:**
```csharp
public interface IBelongToArchitecture
public interface IContextAware
{
IArchitecture GetArchitecture();
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
}
```
**实现此接口的类型:**
- Controller
- System
- Query
- Model
- Command
- Query
- Event 处理器
- 以及其他需要感知架构上下文的组件
**作用:**
所有实现此接口的类型都能够获取其所属的架构实例,从而访问架构提供的各种能力。
所有实现此接口的类型都能够获取其所属的架构上下文实例,从而访问架构提供的各种能力。
### [`ICanSetArchitecture`](ICanSetArchitecture.cs)
## 核心类
定义可以设置架构实例的能力。
### [`ContextAwareBase`](ContextAwareBase.cs)
**接口定义:**
上下文感知基类,实现了 IContextAware 接口,为需要感知架构上下文的类提供基础实现。
**使用方式:**
```csharp
public interface ICanSetArchitecture
public abstract class ContextAwareBase : IContextAware
{
void SetArchitecture(IArchitecture architecture);
protected IArchitectureContext? Context { get; set; }
void IContextAware.SetContext(IArchitectureContext context)
{
Context = context;
OnContextReady(); // 上下文准备好后调用此方法
}
IArchitectureContext IContextAware.GetContext()
{
Context ??= GameContext.GetFirstArchitectureContext();
return Context;
}
protected virtual void OnContextReady() // 子类可以重写此方法进行初始化
{
}
}
```
**实现此接口的类型:**
- Command
- Query
**作用:**
在 Command 和 Query 执行前,框架会自动调用 `SetArchitecture` 方法注入架构实例,使其能够访问 Model、System 等组件。
## 接口关系图
```
IBelongToArchitecture (属于架构)
↓ 被继承
├── ICanGetModel (可获取 Model)
├── ICanGetSystem (可获取 System)
├── ICanGetUtility (可获取 Utility)
├── ICanSendCommand (可发送 Command)
├── ICanSendEvent (可发送 Event)
├── ICanSendQuery (可发送 Query)
└── ICanRegisterEvent (可注册 Event)
ICanSetArchitecture (可设置架构)
↓ 被继承
├── ICommand (命令接口)
└── IQuery<T> (查询接口)
IContextAware (上下文感知接口)
↓ 被继承于
├── AbstractSystem (抽象系统基类)
├── AbstractQuery<TInput, TResult> (抽象查询基类)
├── AbstractModel (抽象模型基类)
└── AbstractCommand (抽象命令基类)
```
## 使用场景
### 1. Controller 实现 IBelongToArchitecture
### 1. Component 继承 ContextAwareBase
```csharp
// Controller 通过实现 IBelongToArchitecture 获得架构访问能力
// 组件通过继承 ContextAwareBase 获得架构上下文访问能力
public partial class PlayerController : Node, IController
{
// 实现 IBelongToArchitecture 接口
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
// 不再需要手动实现 IContextAware基类已处理
// 可以直接使用扩展方法
public override void _Ready()
{
// 因为实现了 IBelongToArchitecture所以可以使用扩展方法
var playerModel = this.GetModel<PlayerModel>();
this.SendCommand(new InitPlayerCommand());
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
@ -97,17 +96,17 @@ public partial class PlayerController : Node, IController
}
```
### 2. Command 实现 ICanSetArchitecture
### 2. Command 继承 AbstractCommand (IContextAware)
```csharp
// Command 实现 ICanSetArchitecture框架会自动注入架构
// Command 继承 AbstractCommand自动成为 IContextAware
public class BuyItemCommand : AbstractCommand
{
public string ItemId { get; set; }
protected override void OnExecute()
{
// 框架已经通过 SetArchitecture 注入了架构实例
// 框架或上下文系统会自动注入 IArchitectureContext
// 所以这里可以直接使用 this.GetModel
var playerModel = this.GetModel<PlayerModel>();
var shopModel = this.GetModel<ShopModel>();
@ -125,21 +124,15 @@ public class BuyItemCommand : AbstractCommand
### 3. 自定义组件遵循规则
```csharp
// 自定义管理器遵循框架规则
public class SaveManager : IBelongToArchitecture
// 自定义管理器遵循框架规则,继承 ContextAwareBase
public class SaveManager : ContextAwareBase
{
private IArchitecture _architecture;
public SaveManager(IArchitecture architecture)
{
_architecture = architecture;
}
public IArchitecture GetArchitecture() => _architecture;
// 不再需要手动构造函数传参,上下文由框架注入
// protected override void OnContextReady() 可用于初始化
public void SaveGame()
{
// 因为实现了 IBelongToArchitecture可以使用扩展方法
// 因为继承了 ContextAwareBase可以使用扩展方法
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
@ -219,9 +212,10 @@ Rule 接口体现了依赖注入DI的思想
```csharp
// 接口定义了"需要什么"
public interface IBelongToArchitecture
public interface IContextAware
{
IArchitecture GetArchitecture();
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
}
// 框架负责"提供什么"
@ -230,8 +224,8 @@ public static class CanSendExtensions
public static void SendCommand<T>(this ICanSendCommand self, T command)
where T : ICommand
{
// 自动注入架构依赖
command.SetArchitecture(self.GetArchitecture());
// 自动注入架构上下文依赖
command.SetContext(self.GetContext());
command.Execute();
}
}
@ -245,8 +239,8 @@ Rule 接口遵循接口隔离原则,每个接口职责单一:
// ❌ 不好的设计:一个大接口包含所有能力
public interface IBigInterface
{
IArchitecture GetArchitecture();
void SetArchitecture(IArchitecture architecture);
void SetContext(IArchitectureContext context);
IArchitectureContext GetContext();
T GetModel<T>() where T : class, IModel;
T GetSystem<T>() where T : class, ISystem;
void SendCommand<T>(T command) where T : ICommand;
@ -254,8 +248,7 @@ public interface IBigInterface
}
// ✅ 好的设计:小接口组合
public interface IBelongToArchitecture { ... } // 只负责获取架构
public interface ICanSetArchitecture { ... } // 只负责设置架构
public interface IContextAware { ... } // 只负责上下文的设置与获取
public interface ICanGetModel { ... } // 只负责获取 Model
public interface ICanSendCommand { ... } // 只负责发送 Command
```
@ -271,14 +264,14 @@ public interface IController : ICanGetModel, ICanGetSystem, ICanSendCommand,
{
}
// Command 需要设置架构和获取 Model/System
public interface ICommand : ICanSetArchitecture, ICanGetModel, ICanGetSystem,
// Command 需要上下文感知和获取 Model/System
public interface ICommand : IContextAware, ICanGetModel, ICanGetSystem,
ICanSendEvent, ICanSendQuery
{
}
// System 只需要获取其他组件
public interface ISystem : ICanGetModel, ICanGetUtility, ICanGetSystem,
public interface ISystem : IContextAware, ICanGetModel, ICanGetUtility, ICanGetSystem,
ICanRegisterEvent, ICanSendEvent, ICanSendQuery
{
}
@ -324,10 +317,10 @@ public class DatabaseCommand : AbstractCommand, ICanAccessDatabase
## 相关包
- [`architecture`](../architecture/README.md) - 定义 IArchitecture 接口
- [`command`](../command/README.md) - Command 实现 ICanSetArchitecture
- [`query`](../query/README.md) - Query 实现 ICanSetArchitecture
- [`controller`](../controller/README.md) - Controller 实现 IBelongToArchitecture
- [`system`](../system/README.md) - System 实现 IBelongToArchitecture
- [`model`](../model/README.md) - Model 实现 IBelongToArchitecture
- [`architecture`](../architecture/README.md) - 定义 IArchitectureContext 接口
- [`command`](../command/README.md) - Command 继承 AbstractCommand (IContextAware)
- [`query`](../query/README.md) - Query 继承 AbstractQuery (IContextAware)
- [`controller`](../controller/README.md) - Controller 实现 ICanSendCommand 等接口
- [`system`](../system/README.md) - System 继承 AbstractSystem (IContextAware)
- [`model`](../model/README.md) - Model 继承 AbstractModel (IContextAware)
- [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法

View File

@ -24,6 +24,8 @@ System 接口,定义了系统的基本行为。
```csharp
void Init(); // 系统初始化方法
void Destroy(); // 系统销毁方法
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
```
**继承的能力:**
@ -39,15 +41,18 @@ void Init(); // 系统初始化方法
### [`AbstractSystem`](AbstractSystem.cs)
抽象 System 基类,提供了 System 的基础实现。
抽象 System 基类,提供了 System 的基础实现。继承自 ContextAwareBase具有上下文感知能力。
**使用方式:**
```csharp
public abstract class AbstractSystem : ISystem
public abstract class AbstractSystem : ContextAwareBase, ISystem
{
void ISystem.Init() => OnInit();
void ISystem.Init() => OnInit(); // 系统初始化,内部调用抽象方法 OnInit()
void ISystem.Destroy() => OnDestroy(); // 系统销毁,内部调用 OnDestroy()
protected abstract void OnInit(); // 子类实现初始化逻辑
protected virtual void OnDestroy(); // 子类可选择重写销毁逻辑
public virtual void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
}
```
@ -99,6 +104,12 @@ public class CombatSystem : AbstractSystem
{
return Math.Max(1, attackPower - defense / 2);
}
protected override void OnDestroy()
{
// 清理资源
base.OnDestroy();
}
}
```

View File

@ -7,17 +7,7 @@ Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如
## 核心接口
### [`ICanGetUtility`](ICanGetUtility.cs)
标记接口,表示该类型可以获取 Utility。
**继承关系:**
```csharp
public interface ICanGetUtility : IBelongToArchitecture
```
### [`IUtility`](IUtility.cs)
### IUtility
Utility 标记接口,所有工具类都应实现此接口。
@ -30,64 +20,98 @@ public interface IUtility
}
```
### IContextUtility
上下文工具接口扩展了IUtility接口为需要感知架构上下文的工具类提供基础能力。
**接口定义:**
```csharp
public interface IContextUtility : IUtility
{
void Init(); // 初始化上下文工具
}
```
## 核心类
### [`AbstractContextUtility`](AbstractContextUtility.cs)
抽象上下文工具类,提供上下文相关的通用功能实现。继承自 ContextAwareBase 并实现 IContextUtility 接口。
**使用方式:**
```csharp
public abstract class AbstractContextUtility : ContextAwareBase, IContextUtility
{
protected ILogger Logger = null!;
void IContextUtility.Init()
{
var name = GetType().Name;
Logger = LoggerFactoryResolver.Provider.CreateLogger(name);
Logger.Debug($"Initializing Context Utility: {name}");
OnInit(); // 子类实现初始化逻辑
Logger.Info($"Context Utility initialized: {name}");
}
protected abstract void OnInit(); // 子类实现具体的初始化逻辑
}
```
## 基本使用
### 1. 定义 Utility
```csharp
// 存储工具类
public class StorageUtility : IUtility
// 存储工具类继承自AbstractContextUtility
public class StorageUtility : AbstractContextUtility
{
private const string SavePath = "user://save_data.json";
public void Save<T>(T data)
protected override void OnInit()
{
string json = Json.Stringify(data);
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreString(json);
Logger.Info("StorageUtility initialized");
}
public T Load<T>() where T : new()
public void Save<T>(T data)
{
if (!FileAccess.FileExists(SavePath))
return new T();
string json = JsonSerializer.Serialize(data);
// 实际保存逻辑
File.WriteAllText(SavePath, json);
}
public T Load<T>()
{
if (!File.Exists(SavePath))
return default(T);
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
string json = file.GetAsText();
return Json.Parse<T>(json);
string json = File.ReadAllText(SavePath);
return JsonSerializer.Deserialize<T>(json);
}
public void Delete()
{
if (FileAccess.FileExists(SavePath))
if (File.Exists(SavePath))
{
DirAccess.RemoveAbsolute(SavePath);
File.Delete(SavePath);
}
}
}
// 数学工具类
// 数学工具类作为普通Utility
public class MathUtility : IUtility
{
public float Lerp(float a, float b, float t)
{
return a + (b - a) * Mathf.Clamp(t, 0f, 1f);
return a + (b - a) * Math.Clamp(t, 0f, 1f);
}
public Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, float t)
public bool IsInRange(float value, float min, float max)
{
float u = 1 - t;
return u * u * p0 + 2 * u * t * p1 + t * t * p2;
}
public bool IsInRange(Vector3 point, Vector3 center, float radius)
{
return point.DistanceTo(center) <= radius;
}
public int RollDice(int sides)
{
return GD.RandRange(1, sides);
return value >= min && value <= max;
}
}