mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-24 20:34:29 +08:00
docs(core): 更新核心模块文档说明
- 移除架构README中接口链接的方括号标记 - 在架构README中添加构造函数参数说明和特性优化 - 移除命令README中继承能力接口的冗余说明 - 重构命令抽象基类为泛型版本并添加输入参数结构 - 更新事件README中接口引用路径和事件总线相关说明 - 重命名事件类名去掉Easy前缀统一为Event - 更新扩展方法README为ContextAware扩展并添加新的扩展方法 - 移除控制器示例中的Godot特定生命周期方法 - 更新IOC容器README中线程安全实现和方法扩展说明 - [skip ci]
This commit is contained in:
parent
8adce14b43
commit
de1dd9002a
@ -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 RegisterModel<TModel>(TModel model) where TModel : IModel;
|
||||||
void RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility;
|
void RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility;
|
||||||
|
|
||||||
// 组件获取
|
// 组件获取(通过容器)
|
||||||
T GetSystem<T>() where T : class, ISystem;
|
|
||||||
T GetModel<T>() where T : class, IModel;
|
T GetModel<T>() where T : class, IModel;
|
||||||
|
T GetSystem<T>() where T : class, ISystem;
|
||||||
T GetUtility<T>() where T : class, IUtility;
|
T GetUtility<T>() where T : class, IUtility;
|
||||||
|
|
||||||
// 命令处理
|
// 命令处理
|
||||||
@ -47,7 +47,7 @@ IUnRegister RegisterEvent<T>(Action<T> onEvent);
|
|||||||
void UnRegisterEvent<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);
|
void OnArchitecturePhase(ArchitecturePhase phase);
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`IArchitectureModule`](IArchitectureModule.cs)
|
### IArchitectureModule
|
||||||
|
|
||||||
架构模块接口,支持模块化架构扩展。
|
架构模块接口,支持模块化架构扩展。
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ void OnArchitecturePhase(ArchitecturePhase phase);
|
|||||||
void Install(IArchitecture architecture);
|
void Install(IArchitecture architecture);
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`IAsyncInitializable`](IAsyncInitializable.cs)
|
### IAsyncInitializable
|
||||||
|
|
||||||
异步初始化接口,支持组件异步初始化。
|
异步初始化接口,支持组件异步初始化。
|
||||||
|
|
||||||
@ -83,6 +83,17 @@ Task InitializeAsync();
|
|||||||
|
|
||||||
架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。
|
架构基类,实现了 `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 基类)
|
// 1. 定义你的架构(继承 Architecture 基类)
|
||||||
public class GameArchitecture : Architecture
|
public class GameArchitecture : Architecture
|
||||||
{
|
{
|
||||||
@ -196,7 +207,7 @@ public class GameController : IController
|
|||||||
|
|
||||||
**高级特性:**
|
**高级特性:**
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 1. 使用自定义配置
|
// 1. 使用自定义配置
|
||||||
var config = new ArchitectureConfiguration();
|
var config = new ArchitectureConfiguration();
|
||||||
var architecture = new GameArchitecture(configuration: config);
|
var architecture = new GameArchitecture(configuration: config);
|
||||||
@ -238,7 +249,7 @@ public class LifecycleHook : IArchitectureLifecycle
|
|||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
var config = new ArchitectureConfiguration
|
var config = new ArchitectureConfiguration
|
||||||
{
|
{
|
||||||
// 严格阶段验证
|
// 严格阶段验证
|
||||||
|
|||||||
@ -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
|
```csharp
|
||||||
@ -36,30 +26,39 @@ void Execute(); // 执行命令
|
|||||||
TResult 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
|
```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 playerModel = this.GetModel<PlayerModel>();
|
||||||
var gameModel = this.GetModel<GameModel>();
|
var gameModel = this.GetModel<GameModel>();
|
||||||
|
|
||||||
// 执行业务逻辑
|
// 执行业务逻辑
|
||||||
playerModel.Health.Value = 100;
|
playerModel.PlayerName.Value = input.PlayerName;
|
||||||
|
gameModel.CurrentLevel.Value = input.LevelId;
|
||||||
gameModel.GameState.Value = GameState.Playing;
|
gameModel.GameState.Value = GameState.Playing;
|
||||||
|
|
||||||
// 发送事件通知其他模块
|
// 发送事件通知其他模块
|
||||||
@ -74,41 +73,44 @@ public class GameController : IController
|
|||||||
|
|
||||||
public void OnStartButtonClicked()
|
public void OnStartButtonClicked()
|
||||||
{
|
{
|
||||||
// 方式1:发送命令实例
|
// 发送命令实例
|
||||||
this.SendCommand(new StartGameCommand());
|
this.SendCommand(new StartGameCommand(new StartGameCommandInput
|
||||||
|
{
|
||||||
// 方式2:通过泛型发送(需要无参构造函数)
|
LevelId = 1,
|
||||||
this.SendCommand<StartGameCommand>();
|
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
|
```csharp
|
||||||
// 定义一个计算伤害的命令
|
// 定义一个计算伤害的命令输入
|
||||||
public class CalculateDamageCommand : AbstractCommand<int>
|
public struct CalculateDamageCommandInput : ICommandInput
|
||||||
{
|
{
|
||||||
private readonly int _attackPower;
|
public int AttackerAttackPower { get; set; }
|
||||||
private readonly int _defense;
|
public int DefenderDefense { get; set; }
|
||||||
|
}
|
||||||
public CalculateDamageCommand(int attackPower, int defense)
|
|
||||||
|
// 定义一个计算伤害的命令
|
||||||
|
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 config = this.GetModel<GameConfigModel>();
|
||||||
|
|
||||||
// 计算最终伤害
|
// 计算最终伤害
|
||||||
var baseDamage = _attackPower - _defense;
|
var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
|
||||||
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
|
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
|
||||||
|
|
||||||
return (int)finalDamage;
|
return (int)finalDamage;
|
||||||
@ -123,9 +125,11 @@ public class CombatSystem : AbstractSystem
|
|||||||
public void Attack(Character attacker, Character defender)
|
public void Attack(Character attacker, Character defender)
|
||||||
{
|
{
|
||||||
// 发送命令并获取返回值
|
// 发送命令并获取返回值
|
||||||
var damage = this.SendCommand(
|
var damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput
|
||||||
new CalculateDamageCommand(attacker.AttackPower, defender.Defense)
|
{
|
||||||
);
|
AttackerAttackPower = attacker.AttackPower,
|
||||||
|
DefenderDefense = defender.Defense
|
||||||
|
}));
|
||||||
|
|
||||||
// 应用伤害
|
// 应用伤害
|
||||||
defender.Health -= damage;
|
defender.Health -= damage;
|
||||||
@ -139,26 +143,78 @@ public class CombatSystem : AbstractSystem
|
|||||||
## 命令的生命周期
|
## 命令的生命周期
|
||||||
|
|
||||||
1. **创建命令**:实例化命令对象,传入必要的参数
|
1. **创建命令**:实例化命令对象,传入必要的参数
|
||||||
2. **设置架构**:框架自动调用 `SetArchitecture()` 设置架构引用
|
2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
|
||||||
3. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
|
3. **返回结果**:对于带返回值的命令,返回执行结果
|
||||||
4. **返回结果**:对于带返回值的命令,返回执行结果
|
4. **命令销毁**:命令执行完毕后可以被垃圾回收
|
||||||
5. **命令销毁**:命令执行完毕后可以被垃圾回收
|
|
||||||
|
## 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. 用户交互操作
|
### 1. 用户交互操作
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// UI 按钮点击
|
public struct SaveGameCommandInput : ICommandInput
|
||||||
public class SaveGameCommand : AbstractCommand
|
|
||||||
{
|
{
|
||||||
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 saveSystem = this.GetSystem<SaveSystem>();
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
|
|
||||||
saveSystem.SavePlayerData(playerModel);
|
saveSystem.SavePlayerData(playerModel, input.SaveSlot);
|
||||||
this.SendEvent(new GameSavedEvent());
|
this.SendEvent(new GameSavedEvent(input.SaveSlot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -166,17 +222,18 @@ public class SaveGameCommand : AbstractCommand
|
|||||||
### 2. 业务流程控制
|
### 2. 业务流程控制
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 关卡切换
|
public struct LoadLevelCommandInput : ICommandInput
|
||||||
public class LoadLevelCommand : AbstractCommand
|
|
||||||
{
|
{
|
||||||
private readonly int _levelId;
|
public int LevelId { get; set; }
|
||||||
|
}
|
||||||
public LoadLevelCommand(int levelId)
|
|
||||||
|
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 levelSystem = this.GetSystem<LevelSystem>();
|
||||||
var uiSystem = this.GetSystem<UISystem>();
|
var uiSystem = this.GetSystem<UISystem>();
|
||||||
@ -185,99 +242,50 @@ public class LoadLevelCommand : AbstractCommand
|
|||||||
uiSystem.ShowLoadingScreen();
|
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. **保持命令原子性**:一个命令应该完成一个完整的业务操作
|
1. **保持命令原子性**:一个命令应该完成一个完整的业务操作
|
||||||
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
|
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
|
||||||
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
|
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
|
||||||
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
|
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
|
||||||
5. **合理使用返回值**:只在确实需要返回结果时使用 `ICommand<TResult>`
|
5. **合理使用返回值**:只在确实需要返回结果时使用 `AbstractCommand<TInput, TResult>`
|
||||||
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand`、`SavePlayerCommand`
|
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand`、`SavePlayerCommand`
|
||||||
|
7. **输入参数结构化**:使用 `ICommandInput` 接口的实现类来组织命令参数
|
||||||
|
|
||||||
## 扩展功能
|
## 扩展功能
|
||||||
|
|
||||||
### 命令撤销/重做(可扩展)
|
### 命令撤销/重做(可扩展)
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 可撤销命令接口
|
public struct MoveCommandInput : ICommandInput
|
||||||
public interface IUndoableCommand : ICommand
|
|
||||||
{
|
{
|
||||||
void Undo();
|
public Vector3 NewPosition { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实现可撤销命令
|
// 实现可撤销命令
|
||||||
public class MoveCommand : AbstractCommand, IUndoableCommand
|
public class MoveCommand : AbstractCommand<MoveCommandInput>, IUndoableCommand
|
||||||
{
|
{
|
||||||
private Vector3 _oldPosition;
|
private Vector3 _oldPosition;
|
||||||
private Vector3 _newPosition;
|
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>();
|
var player = this.GetModel<PlayerModel>();
|
||||||
_oldPosition = player.Position;
|
_oldPosition = player.Position;
|
||||||
player.Position = _newPosition;
|
player.Position = input.NewPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Undo()
|
public void Undo()
|
||||||
|
|||||||
218
GFramework.Core/environment/README.md
Normal file
218
GFramework.Core/environment/README.md
Normal 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容器管理
|
||||||
@ -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); // 注册事件处理函数
|
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(); // 执行注销操作
|
void UnRegister(); // 执行注销操作
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. [`IUnRegisterList`](IUnRegisterList.cs)
|
### 3. [
|
||||||
|
`IUnRegisterList`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegisterList.cs#L6-L10)
|
||||||
|
|
||||||
注销列表接口,用于批量管理注销对象。
|
注销列表接口,用于批量管理注销对象。
|
||||||
|
|
||||||
**属性:**
|
**属性:**
|
||||||
|
|
||||||
```csharp
|
```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();
|
unregister.UnRegister();
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. [`EasyEvent<T>`](EasyEventGeneric.cs)
|
### 2. [`Event<T>`](EasyEventGeneric.cs)
|
||||||
|
|
||||||
单参数泛型事件类,支持一个参数的事件。
|
单参数泛型事件类,支持一个参数的事件。
|
||||||
|
|
||||||
@ -74,7 +82,7 @@ unregister.UnRegister();
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 创建带参数的事件
|
// 创建带参数的事件
|
||||||
var onScoreChanged = new EasyEvent<int>();
|
var onScoreChanged = new Event<int>();
|
||||||
|
|
||||||
// 注册监听
|
// 注册监听
|
||||||
onScoreChanged.Register(newScore =>
|
onScoreChanged.Register(newScore =>
|
||||||
@ -86,7 +94,7 @@ onScoreChanged.Register(newScore =>
|
|||||||
onScoreChanged.Trigger(100);
|
onScoreChanged.Trigger(100);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. [`EasyEvent<T, TK>`](EasyEventGeneric.cs)
|
### 3. [`Event<T, TK>`](EasyEventGeneric.cs)
|
||||||
|
|
||||||
双参数泛型事件类。
|
双参数泛型事件类。
|
||||||
|
|
||||||
@ -94,7 +102,7 @@ onScoreChanged.Trigger(100);
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 伤害事件:攻击者、伤害值
|
// 伤害事件:攻击者、伤害值
|
||||||
var onDamageDealt = new EasyEvent<string, int>();
|
var onDamageDealt = new Event<string, int>();
|
||||||
|
|
||||||
onDamageDealt.Register((attacker, damage) =>
|
onDamageDealt.Register((attacker, damage) =>
|
||||||
{
|
{
|
||||||
@ -104,25 +112,7 @@ onDamageDealt.Register((attacker, damage) =>
|
|||||||
onDamageDealt.Trigger("Player", 50);
|
onDamageDealt.Trigger("Player", 50);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. [`EasyEvent<T, TK, TS>`](EasyEventGeneric.cs)
|
### 4. [`EasyEvents`](EasyEvents.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)
|
|
||||||
|
|
||||||
全局事件管理器,提供类型安全的事件注册和获取。
|
全局事件管理器,提供类型安全的事件注册和获取。
|
||||||
|
|
||||||
@ -145,7 +135,7 @@ gameStartEvent.Register(() =>
|
|||||||
gameStartEvent.Trigger();
|
gameStartEvent.Trigger();
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. [`TypeEventSystem`](TypeEventSystem.cs)
|
### 5. [`EventBus`](EventBus.cs)
|
||||||
|
|
||||||
类型化事件系统,支持基于类型的事件发送和注册。
|
类型化事件系统,支持基于类型的事件发送和注册。
|
||||||
|
|
||||||
@ -153,25 +143,25 @@ gameStartEvent.Trigger();
|
|||||||
|
|
||||||
```csharp
|
```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}");
|
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)
|
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();
|
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` - 架构初始化失败
|
||||||
|
|
||||||
## 在架构中使用事件
|
## 在架构中使用事件
|
||||||
|
|
||||||
### 定义事件类
|
### 定义事件类
|
||||||
|
|||||||
@ -6,64 +6,69 @@ Extensions 包提供了一系列扩展方法,简化了框架各个接口的使
|
|||||||
|
|
||||||
## 扩展方法类别
|
## 扩展方法类别
|
||||||
|
|
||||||
### 1. 获取组件扩展 ([`CanGetExtensions.cs`](CanGetExtensions.cs))
|
### 1. ContextAware 扩展 ([`ContextAwareExtensions.cs`](ContextAwareExtensions.cs))
|
||||||
|
|
||||||
为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[
|
为 [`IContextAware`](../../GFramework.Core.Abstractions/rule/IContextAware.cs)
|
||||||
`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。
|
提供扩展方法,允许直接从实现了 [IContextAware](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs)
|
||||||
|
的对象获取架构组件。
|
||||||
|
|
||||||
#### CanGetModelExtension
|
#### GetSystem 扩展方法
|
||||||
|
|
||||||
```csharp
|
```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
|
```csharp
|
||||||
// 在 Controller、Command、Query 中使用
|
// 在实现了 IContextAware 的类中使用
|
||||||
public class PlayerController : IController
|
public class PlayerController : IController
|
||||||
{
|
{
|
||||||
public void UpdateUI()
|
public void UpdateUI()
|
||||||
{
|
{
|
||||||
// 直接通过 this 调用
|
// 直接通过 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 playerModel = this.GetModel<PlayerModel>();
|
||||||
var inventoryModel = this.GetModel<InventoryModel>();
|
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
|
```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
|
```csharp
|
||||||
public class GameModel : AbstractModel
|
public class GameModel : AbstractModel, IContextAware
|
||||||
{
|
{
|
||||||
protected override void OnInit()
|
protected override void OnInit()
|
||||||
{
|
{
|
||||||
@ -74,24 +79,14 @@ public class GameModel : AbstractModel
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 发送命令扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
|
#### SendCommand 扩展方法
|
||||||
|
|
||||||
为 [`ICanSendCommand`](../command/ICanSendCommand.cs) 提供扩展方法。
|
|
||||||
|
|
||||||
#### CanSendCommandExtension
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 发送无参命令(通过类型)
|
// 发送无返回值的命令
|
||||||
public static void SendCommand<T>(this ICanSendCommand self)
|
public static void SendCommand(this IContextAware contextAware, ICommand command)
|
||||||
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,
|
public static TResult SendCommand<TResult>(this IContextAware contextAware, ICommand<TResult> command)
|
||||||
ICommand<TResult> command)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
@ -101,36 +96,51 @@ public class GameController : IController
|
|||||||
{
|
{
|
||||||
public void OnStartButtonClicked()
|
public void OnStartButtonClicked()
|
||||||
{
|
{
|
||||||
// 方式1:通过类型发送(需要无参构造函数)
|
// 发送命令实例
|
||||||
this.SendCommand<StartGameCommand>();
|
this.SendCommand(new StartGameCommand());
|
||||||
|
|
||||||
// 方式2:发送命令实例
|
// 发送带返回值的命令
|
||||||
this.SendCommand(new LoadLevelCommand(levelId: 1));
|
var result = this.SendCommand(new CalculateScoreCommand());
|
||||||
|
|
||||||
// 方式3:发送带返回值的命令
|
|
||||||
var score = this.SendCommand(new CalculateScoreCommand());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 发送事件扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
|
#### SendQuery 扩展方法
|
||||||
|
|
||||||
为 [`ICanSendEvent`](../events/ICanSendEvent.cs) 提供扩展方法。
|
|
||||||
|
|
||||||
#### CanSendEventExtension
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 发送无参事件
|
public static TResult SendQuery<TResult>(this IContextAware contextAware, IQuery<TResult> query)
|
||||||
public static void SendEvent<T>(this ICanSendEvent self) where T : new()
|
|
||||||
|
|
||||||
// 发送事件实例
|
|
||||||
public static void SendEvent<T>(this ICanSendEvent self, T e)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
```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)
|
public void TakeDamage(int damage)
|
||||||
{
|
{
|
||||||
@ -152,57 +162,20 @@ public class PlayerModel : AbstractModel
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 发送查询扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
|
#### RegisterEvent 扩展方法
|
||||||
|
|
||||||
为 [`ICanSendQuery`](../query/ICanSendQuery.cs) 提供扩展方法。
|
|
||||||
|
|
||||||
#### CanSendQueryExtension
|
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public static TResult SendQuery<TResult>(this ICanSendQuery self,
|
public static IUnRegister RegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> handler)
|
||||||
IQuery<TResult> query)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class InventoryController : IController
|
public class GameController : 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();
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
public override void _Ready()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// 注册事件监听
|
// 注册事件监听
|
||||||
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
|
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
|
#### OrEventExtensions
|
||||||
|
|
||||||
```csharp
|
```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);
|
.Or(onPoisonDamage);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. UnRegister 扩展 ([`UnRegisterExtension.cs`](UnRegisterExtension.cs))
|
### 4. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.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) 提供批量管理功能。
|
为 [`IUnRegister`](../events/IUnRegister.cs) 和 [`IUnRegisterList`](../events/IUnRegisterList.cs) 提供批量管理功能。
|
||||||
|
|
||||||
@ -296,11 +364,11 @@ public static void UnRegisterAll(this IUnRegisterList self)
|
|||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class ComplexController : Node, IController
|
public class ComplexController : IController
|
||||||
{
|
{
|
||||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
public override void _Ready()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// 所有注册都添加到列表中
|
// 所有注册都添加到列表中
|
||||||
this.RegisterEvent<Event1>(OnEvent1)
|
this.RegisterEvent<Event1>(OnEvent1)
|
||||||
@ -316,7 +384,7 @@ public class ComplexController : Node, IController
|
|||||||
.AddToUnregisterList(_unregisterList);
|
.AddToUnregisterList(_unregisterList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _ExitTree()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
// 一次性注销所有
|
// 一次性注销所有
|
||||||
_unregisterList.UnRegisterAll();
|
_unregisterList.UnRegisterAll();
|
||||||
@ -329,13 +397,11 @@ public class ComplexController : Node, IController
|
|||||||
### Controller 示例
|
### Controller 示例
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public partial class GameplayController : Node, IController
|
public partial class GameplayController : IController
|
||||||
{
|
{
|
||||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||||
|
|
||||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
public void Initialize()
|
||||||
|
|
||||||
public override void _Ready()
|
|
||||||
{
|
{
|
||||||
// 使用扩展方法获取 Model
|
// 使用扩展方法获取 Model
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
@ -348,34 +414,24 @@ public partial class GameplayController : Node, IController
|
|||||||
// 监听可绑定属性
|
// 监听可绑定属性
|
||||||
playerModel.Health.Register(OnHealthChanged)
|
playerModel.Health.Register(OnHealthChanged)
|
||||||
.AddToUnregisterList(_unregisterList);
|
.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)
|
||||||
{
|
{
|
||||||
// 使用扩展方法发送查询
|
this.SendCommand<UseHealthPotionCommand>();
|
||||||
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
|
|
||||||
if (hasPotion)
|
|
||||||
{
|
|
||||||
this.SendCommand<UseHealthPotionCommand>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGameStarted(GameStartedEvent e)
|
private void OnGameStarted(GameStartedEvent e)
|
||||||
{
|
{
|
||||||
GD.Print("Game started!");
|
Console.WriteLine("Game started!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHealthChanged(int health)
|
private void OnHealthChanged(int health)
|
||||||
@ -383,12 +439,7 @@ public partial class GameplayController : Node, IController
|
|||||||
UpdateHealthBar(health);
|
UpdateHealthBar(health);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnScoreChanged(int score)
|
public void Cleanup()
|
||||||
{
|
|
||||||
UpdateScoreDisplay(score);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void _ExitTree()
|
|
||||||
{
|
{
|
||||||
_unregisterList.UnRegisterAll();
|
_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. **类型安全**:编译时检查类型
|
2. **类型安全**:编译时检查类型
|
||||||
3. **可读性高**:代码意图更清晰
|
3. **可读性高**:代码意图更清晰
|
||||||
4. **智能提示**:IDE 可以提供完整的自动补全
|
4. **智能提示**:IDE 可以提供完整的自动补全
|
||||||
@ -475,21 +527,17 @@ public class AchievementSystem : AbstractSystem
|
|||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **确保引用命名空间**:
|
1. **确保引用命名空间:**
|
||||||
```csharp
|
```csharp
|
||||||
using GFramework.framework.extensions;
|
using GFramework.Core.extensions;
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **理解扩展方法本质**:
|
2. **理解扩展方法本质:**
|
||||||
- 扩展方法是静态方法的语法糖
|
- 扩展方法是静态方法的语法糖
|
||||||
- 不会改变原始类型的结构
|
- 不会改变原始类型的结构
|
||||||
- 仅在编译时解析
|
- 仅在编译时解析
|
||||||
|
|
||||||
3. **Godot 特定功能**:
|
3. **性能考虑:**
|
||||||
- `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用
|
|
||||||
- 使用 `#if GODOT` 编译指令控制
|
|
||||||
|
|
||||||
4. **性能考虑**:
|
|
||||||
- 扩展方法本身无性能开销
|
- 扩展方法本身无性能开销
|
||||||
- 实际调用的是底层方法
|
- 实际调用的是底层方法
|
||||||
|
|
||||||
|
|||||||
@ -16,20 +16,24 @@ IoC 容器类,负责管理对象的注册和获取。
|
|||||||
- 注册实例到容器
|
- 注册实例到容器
|
||||||
- 从容器中获取实例
|
- 从容器中获取实例
|
||||||
- 类型安全的依赖管理
|
- 类型安全的依赖管理
|
||||||
|
- 线程安全操作
|
||||||
|
- 容器冻结保护
|
||||||
|
|
||||||
## 核心方法
|
## 核心方法
|
||||||
|
|
||||||
### 1. Register<T>
|
### 1. Register<T> 和 Register(Type, object)
|
||||||
|
|
||||||
注册一个实例到容器中。
|
注册一个实例到容器中。
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public void Register<T>(T instance)
|
public void Register<T>(T instance)
|
||||||
|
public void Register(Type type, object instance)
|
||||||
```
|
```
|
||||||
|
|
||||||
**参数:**
|
**参数:**
|
||||||
|
|
||||||
- `instance`: 要注册的实例对象
|
- `instance`: 要注册的实例对象
|
||||||
|
- `type`: 要注册的类型(重载方法)
|
||||||
|
|
||||||
**使用示例:**
|
**使用示例:**
|
||||||
|
|
||||||
@ -42,29 +46,104 @@ container.Register<IGameSystem>(new GameSystem());
|
|||||||
container.Register<IStorageUtility>(new StorageUtility());
|
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
|
```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
|
```csharp
|
||||||
// 获取已注册的实例
|
// 获取已注册的实例
|
||||||
var playerModel = container.Get<IPlayerModel>();
|
var playerModel = container.Get<IPlayerModel>();
|
||||||
var gameSystem = container.Get<IGameSystem>();
|
var gameSystems = container.GetAll<IGameSystem>();
|
||||||
|
|
||||||
// 如果类型未注册,返回 null
|
// 如果类型未注册,Get 返回 null,GetAll 返回空数组
|
||||||
var unknownService = container.Get<IUnknownService>(); // null
|
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 中的应用
|
### Architecture 中的应用
|
||||||
@ -142,27 +221,66 @@ public class PlayerController : IController
|
|||||||
```csharp
|
```csharp
|
||||||
public class IocContainer
|
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)
|
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);
|
_lock.EnterReadLock();
|
||||||
if (_mInstances.TryGetValue(key, out var retInstance))
|
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)
|
IocContainer.Register<T>(system)
|
||||||
↓
|
↓
|
||||||
Dictionary[typeof(T)] = system
|
加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet
|
||||||
```
|
```
|
||||||
|
|
||||||
### 获取流程
|
### 获取流程
|
||||||
@ -186,9 +304,9 @@ Architecture.GetSystem<T>()
|
|||||||
↓
|
↓
|
||||||
IocContainer.Get<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");
|
dataService.SaveData("game data");
|
||||||
```
|
```
|
||||||
|
|
||||||
### 覆盖注册
|
### 注册多个实现
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var container = new IocContainer();
|
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. 简单轻量
|
### 1. 简单轻量
|
||||||
|
|
||||||
- 只有两个核心方法:`Register` 和 `Get`
|
- 支持多种注册方式:普通注册、单例注册、多实例注册
|
||||||
- 基于字典实现,性能高效
|
- 基于字典和哈希集实现,性能高效
|
||||||
- 无复杂的依赖解析逻辑
|
- 无复杂的依赖解析逻辑
|
||||||
|
|
||||||
### 2. 手动注册
|
### 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. 类型安全
|
### 4. 类型安全
|
||||||
|
|
||||||
@ -286,6 +405,17 @@ var config = container.Get<IConfig>(); // CustomConfig
|
|||||||
- 避免字符串键导致的错误
|
- 避免字符串键导致的错误
|
||||||
- IDE 友好,支持自动补全
|
- IDE 友好,支持自动补全
|
||||||
|
|
||||||
|
### 5. 线程安全
|
||||||
|
|
||||||
|
- 使用读写锁确保多线程环境下的安全操作
|
||||||
|
- 读操作可以并发执行
|
||||||
|
- 写操作独占锁,防止并发修改冲突
|
||||||
|
|
||||||
|
### 6. 容器冻结
|
||||||
|
|
||||||
|
- 提供 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372) 方法,防止进一步修改容器内容
|
||||||
|
- 防止在初始化后意外修改注册内容
|
||||||
|
|
||||||
## 与其他 IoC 容器的区别
|
## 与其他 IoC 容器的区别
|
||||||
|
|
||||||
### 本框架的 IocContainer
|
### 本框架的 IocContainer
|
||||||
@ -301,6 +431,8 @@ var service = container.Get<MyService>();
|
|||||||
|
|
||||||
- ✅ 简单易用
|
- ✅ 简单易用
|
||||||
- ✅ 性能高
|
- ✅ 性能高
|
||||||
|
- ✅ 线程安全
|
||||||
|
- ✅ 支持多实例
|
||||||
- ❌ 不支持构造函数注入
|
- ❌ 不支持构造函数注入
|
||||||
- ❌ 不支持自动解析依赖
|
- ❌ 不支持自动解析依赖
|
||||||
- ❌ 不支持生命周期管理(Transient/Scoped/Singleton)
|
- ❌ 不支持生命周期管理(Transient/Scoped/Singleton)
|
||||||
@ -394,56 +526,37 @@ else
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 注意事项
|
### 5. 合理使用容器冻结
|
||||||
|
|
||||||
1. **类型唯一性**
|
|
||||||
- 每个类型只能注册一个实例
|
|
||||||
- 重复注册会覆盖之前的实例
|
|
||||||
|
|
||||||
2. **手动管理依赖顺序**
|
|
||||||
- 组件的依赖关系需要手动保证
|
|
||||||
- 先注册被依赖的组件
|
|
||||||
|
|
||||||
3. **无生命周期管理**
|
|
||||||
- 实例一旦注册就一直存在
|
|
||||||
- 需要手动管理实例的生命周期
|
|
||||||
|
|
||||||
4. **线程安全**
|
|
||||||
- 当前实现非线程安全
|
|
||||||
- 避免多线程同时访问
|
|
||||||
|
|
||||||
## 扩展可能性
|
|
||||||
|
|
||||||
如果需要更复杂的功能,可以扩展 `IocContainer`:
|
|
||||||
|
|
||||||
```csharp
|
```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
|
// 冻结容器
|
||||||
{
|
Container.Freeze(); // 此后无法再注册新组件
|
||||||
_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
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 容器管理所有组件
|
- [`architecture`](../architecture/README.md) - 使用 IoC 容器管理所有组件
|
||||||
|
|||||||
@ -6,7 +6,7 @@ Logging 包提供了灵活的日志系统,支持多级别日志记录。默认
|
|||||||
|
|
||||||
## 核心接口
|
## 核心接口
|
||||||
|
|
||||||
### [`ILogger`](ILogger.cs)
|
### [ILogger](ILogger.cs)
|
||||||
|
|
||||||
日志记录器接口,定义了日志记录的基本功能。
|
日志记录器接口,定义了日志记录的基本功能。
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ void Fatal(string msg, Exception t);
|
|||||||
string Name();
|
string Name();
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`ILoggerFactory`](ILoggerFactory.cs)
|
### [ILoggerFactory](ILoggerFactory.cs)
|
||||||
|
|
||||||
日志工厂接口,用于创建日志记录器实例。
|
日志工厂接口,用于创建日志记录器实例。
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ string Name();
|
|||||||
ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info);
|
ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info);
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`ILoggerFactoryProvider`](ILoggerFactory.cs)
|
### [ILoggerFactoryProvider](ILoggerFactoryProvider.cs)
|
||||||
|
|
||||||
日志工厂提供程序接口,用于获取日志工厂。
|
日志工厂提供程序接口,用于获取日志工厂。
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ ILoggerFactory GetLoggerFactory();
|
|||||||
ILogger CreateLogger(string name);
|
ILogger CreateLogger(string name);
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`LogLevel`](LogLevel.cs)
|
### [LogLevel](LogLevel.cs)
|
||||||
|
|
||||||
日志级别枚举。
|
日志级别枚举。
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ public enum LogLevel
|
|||||||
|
|
||||||
## 核心类
|
## 核心类
|
||||||
|
|
||||||
### [`AbstractLogger`](AbstractLogger.cs)
|
### [AbstractLogger](AbstractLogger.cs)
|
||||||
|
|
||||||
抽象日志基类,封装了日志级别判断、格式化与异常处理逻辑。平台日志器只需实现 `Write` 方法即可。
|
抽象日志基类,封装了日志级别判断、格式化与异常处理逻辑。平台日志器只需实现 `Write` 方法即可。
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ public class CustomLogger : AbstractLogger
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`ConsoleLogger`](ConsoleLogger.cs)
|
### [ConsoleLogger](ConsoleLogger.cs)
|
||||||
|
|
||||||
控制台日志记录器实现,支持彩色输出。
|
控制台日志记录器实现,支持彩色输出。
|
||||||
|
|
||||||
@ -162,7 +162,14 @@ logger.Fatal("致命错误");
|
|||||||
- **Error**: 红色
|
- **Error**: 红色
|
||||||
- **Fatal**: 洋红色
|
- **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("日志记录器创建成功");
|
logger.Info("日志记录器创建成功");
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`ConsoleLoggerFactoryProvider`](ConsoleLoggerFactoryProvider.cs)
|
### [ConsoleLoggerFactoryProvider](ConsoleLoggerFactoryProvider.cs)
|
||||||
|
|
||||||
控制台日志工厂提供程序实现。
|
控制台日志工厂提供程序实现。
|
||||||
|
|
||||||
@ -182,11 +189,12 @@ logger.Info("日志记录器创建成功");
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var provider = new ConsoleLoggerFactoryProvider();
|
var provider = new ConsoleLoggerFactoryProvider();
|
||||||
var factory = provider.GetLoggerFactory();
|
provider.MinLevel = LogLevel.Debug; // 设置最低日志级别
|
||||||
var logger = factory.GetLogger("MyApp", LogLevel.Info);
|
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)
|
if (level >= LogLevel.Debug)
|
||||||
{
|
{
|
||||||
GD.Print($"[{level}] {message}");
|
Console.WriteLine($"[{level}] {message}");
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
GD.Print(exception);
|
Console.WriteLine(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,15 +349,16 @@ public class DebugLogger : AbstractLogger
|
|||||||
- 支持字符串格式化参数
|
- 支持字符串格式化参数
|
||||||
- 支持异常信息传递
|
- 支持异常信息传递
|
||||||
|
|
||||||
3. **自定义比较器**:
|
3. **ConsoleLogger 的额外参数**:
|
||||||
- 在 `BindableProperty` 中可以使用 `WithComparer` 设置自定义比较器
|
- ConsoleLogger 现在支持自定义TextWriter输出流
|
||||||
|
- 支持禁用颜色输出的功能(useColors参数)
|
||||||
|
|
||||||
## 相关包
|
## 相关包
|
||||||
|
|
||||||
- [`architecture`](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
|
- [architecture](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
|
||||||
- [`property`](../property/README.md) - 可绑定属性基于事件系统实现
|
- [property](../property/README.md) - 可绑定属性基于事件系统实现
|
||||||
- [`extensions`](../extensions/README.md) - 提供便捷的扩展方法
|
- [extensions](../extensions/README.md) - 提供便捷的扩展方法
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**许可证**: Apache 2.0
|
**许可证**: Apache 2.0
|
||||||
@ -13,14 +13,14 @@ Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的
|
|||||||
|
|
||||||
**继承的能力接口:**
|
**继承的能力接口:**
|
||||||
|
|
||||||
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构引用
|
- [`IContextAware`](../rule/IContextAware.cs) - 上下文感知接口
|
||||||
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具类
|
- [`ILogAware`](../rule/ILogAware.cs) - 日志感知接口
|
||||||
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
|
|
||||||
|
|
||||||
**核心方法:**
|
**核心方法:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
void Init(); // 初始化模型
|
void Init(); // 初始化模型
|
||||||
|
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
|
||||||
```
|
```
|
||||||
|
|
||||||
### [`ICanGetModel`](ICanGetModel.cs)
|
### [`ICanGetModel`](ICanGetModel.cs)
|
||||||
@ -31,7 +31,8 @@ void Init(); // 初始化模型
|
|||||||
|
|
||||||
### [`AbstractModel`](AbstractModel.cs)
|
### [`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. **提供数据访问接口**
|
2. **提供数据访问接口**
|
||||||
3. **监听自身属性变化并做出响应**
|
3. **监听自身属性变化并做出响应**
|
||||||
4. **发送数据变化事件**
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -7,17 +7,7 @@ Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。
|
|||||||
|
|
||||||
## 核心接口
|
## 核心接口
|
||||||
|
|
||||||
### [`ICanSendQuery`](ICanSendQuery.cs)
|
### IQuery<TResult>
|
||||||
|
|
||||||
标记接口,表示该类型可以发送查询。
|
|
||||||
|
|
||||||
**继承关系:**
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public interface ICanSendQuery : IBelongToArchitecture
|
|
||||||
```
|
|
||||||
|
|
||||||
### [`IQuery<TResult>`](IQuery.cs)
|
|
||||||
|
|
||||||
查询接口,定义了查询的基本契约。
|
查询接口,定义了查询的基本契约。
|
||||||
|
|
||||||
@ -27,26 +17,50 @@ public interface ICanSendQuery : IBelongToArchitecture
|
|||||||
TResult Do(); // 执行查询并返回结果
|
TResult Do(); // 执行查询并返回结果
|
||||||
```
|
```
|
||||||
|
|
||||||
**继承的能力:**
|
|
||||||
|
|
||||||
- `ICanSetArchitecture` - 可设置架构
|
|
||||||
- `ICanGetModel` - 可获取 Model
|
|
||||||
- `ICanGetSystem` - 可获取 System
|
|
||||||
- `ICanSendQuery` - 可发送其他 Query
|
|
||||||
|
|
||||||
## 核心类
|
## 核心类
|
||||||
|
|
||||||
### [`AbstractQuery<T>`](AbstractQuery.cs)
|
### [`AbstractQuery<TInput, TResult>`](AbstractQuery.cs)
|
||||||
|
|
||||||
抽象查询基类,提供了查询的基础实现。
|
抽象查询基类,提供了查询的基础实现。它接受一个泛型输入参数 TInput,该参数必须实现 IQueryInput 接口。
|
||||||
|
|
||||||
**使用方式:**
|
**使用方式:**
|
||||||
|
|
||||||
```csharp
|
```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();
|
public TResult Do() => OnDo(input); // 执行查询,传入输入参数
|
||||||
protected abstract T OnDo(); // 子类实现查询逻辑
|
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. 定义查询
|
### 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;
|
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;
|
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; }
|
public GetItemCountQuery(string itemId) : base(new GetItemCountQueryInput(itemId))
|
||||||
|
{
|
||||||
protected override int OnDo()
|
}
|
||||||
|
|
||||||
|
protected override int OnDo(GetItemCountQueryInput input)
|
||||||
{
|
{
|
||||||
var inventory = this.GetModel<InventoryModel>();
|
var inventory = this.GetModel<InventoryModel>();
|
||||||
return inventory.GetItemCount(ItemId);
|
return inventory.GetItemCount(input.ItemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 发送查询(在 Controller 中)
|
### 2. 发送查询(在 Controller 中)
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
public partial class ShopUI : Control, IController
|
public partial class ShopUI : Control, IController
|
||||||
{
|
{
|
||||||
[Export] private Button _buyButton;
|
[Export] private Button _buyButton;
|
||||||
@ -121,7 +152,7 @@ public partial class ShopUI : Control, IController
|
|||||||
|
|
||||||
### 3. 在 System 中使用
|
### 3. 在 System 中使用
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
public class CombatSystem : AbstractSystem
|
public class CombatSystem : AbstractSystem
|
||||||
{
|
{
|
||||||
protected override void OnInit()
|
protected override void OnInit()
|
||||||
@ -148,7 +179,7 @@ public class CombatSystem : AbstractSystem
|
|||||||
|
|
||||||
### 1. 带参数的复杂查询
|
### 1. 带参数的复杂查询
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 查询指定范围内的敌人列表
|
// 查询指定范围内的敌人列表
|
||||||
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
|
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
|
||||||
{
|
{
|
||||||
@ -172,7 +203,7 @@ var enemies = this.SendQuery(new GetEnemiesInRangeQuery
|
|||||||
|
|
||||||
### 2. 组合查询
|
### 2. 组合查询
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 查询玩家是否可以使用技能
|
// 查询玩家是否可以使用技能
|
||||||
public class CanUseSkillQuery : AbstractQuery<bool>
|
public class CanUseSkillQuery : AbstractQuery<bool>
|
||||||
{
|
{
|
||||||
@ -215,7 +246,7 @@ public class IsSkillOnCooldownQuery : AbstractQuery<bool>
|
|||||||
|
|
||||||
### 3. 聚合数据查询
|
### 3. 聚合数据查询
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 查询玩家战斗力
|
// 查询玩家战斗力
|
||||||
public class GetPlayerPowerQuery : AbstractQuery<int>
|
public class GetPlayerPowerQuery : AbstractQuery<int>
|
||||||
{
|
{
|
||||||
@ -254,7 +285,7 @@ public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
|
|||||||
|
|
||||||
### 4. 跨 System 查询
|
### 4. 跨 System 查询
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 在 AI System 中查询玩家状态
|
// 在 AI System 中查询玩家状态
|
||||||
public class EnemyAISystem : AbstractSystem
|
public class EnemyAISystem : AbstractSystem
|
||||||
{
|
{
|
||||||
@ -303,7 +334,7 @@ public class EnemyAISystem : AbstractSystem
|
|||||||
- **返回值**:有返回值
|
- **返回值**:有返回值
|
||||||
- **示例**:获取金币数量、检查技能冷却、查询玩家位置
|
- **示例**:获取金币数量、检查技能冷却、查询玩家位置
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// ❌ 错误:在 Query 中修改状态
|
// ❌ 错误:在 Query 中修改状态
|
||||||
public class BadQuery : AbstractQuery<int>
|
public class BadQuery : AbstractQuery<int>
|
||||||
{
|
{
|
||||||
@ -349,7 +380,7 @@ public class AddGoldCommand : AbstractCommand
|
|||||||
|
|
||||||
### 1. 缓存查询结果
|
### 1. 缓存查询结果
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 在 Model 中缓存复杂计算
|
// 在 Model 中缓存复杂计算
|
||||||
public class PlayerModel : AbstractModel
|
public class PlayerModel : AbstractModel
|
||||||
{
|
{
|
||||||
@ -379,7 +410,7 @@ public class PlayerModel : AbstractModel
|
|||||||
|
|
||||||
### 2. 批量查询
|
### 2. 批量查询
|
||||||
|
|
||||||
```csharp
|
``csharp
|
||||||
// 一次查询多个数据,而不是多次单独查询
|
// 一次查询多个数据,而不是多次单独查询
|
||||||
public class GetMultipleItemCountsQuery : AbstractQuery<Dictionary<string, int>>
|
public class GetMultipleItemCountsQuery : AbstractQuery<Dictionary<string, int>>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,85 +6,84 @@ Rule 包定义了框架的核心规则接口,这些接口规定了框架各个
|
|||||||
|
|
||||||
## 核心接口
|
## 核心接口
|
||||||
|
|
||||||
### [`IBelongToArchitecture`](IBelongToArchitecture.cs)
|
### IContextAware
|
||||||
|
|
||||||
标记接口,表示某个对象属于特定的架构体系。
|
标记接口,表示该类型可以感知架构上下文。
|
||||||
|
|
||||||
**接口定义:**
|
**接口定义:**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public interface IBelongToArchitecture
|
public interface IContextAware
|
||||||
{
|
{
|
||||||
IArchitecture GetArchitecture();
|
void SetContext(IArchitectureContext context);
|
||||||
|
IArchitectureContext GetContext();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**实现此接口的类型:**
|
**实现此接口的类型:**
|
||||||
|
|
||||||
- Controller
|
|
||||||
- System
|
- System
|
||||||
|
- Query
|
||||||
- Model
|
- Model
|
||||||
- Command
|
- Command
|
||||||
- Query
|
- 以及其他需要感知架构上下文的组件
|
||||||
- Event 处理器
|
|
||||||
|
|
||||||
**作用:**
|
**作用:**
|
||||||
所有实现此接口的类型都能够获取其所属的架构实例,从而访问架构提供的各种能力。
|
所有实现此接口的类型都能够获取其所属的架构上下文实例,从而访问架构提供的各种能力。
|
||||||
|
|
||||||
### [`ICanSetArchitecture`](ICanSetArchitecture.cs)
|
## 核心类
|
||||||
|
|
||||||
定义可以设置架构实例的能力。
|
### [`ContextAwareBase`](ContextAwareBase.cs)
|
||||||
|
|
||||||
**接口定义:**
|
上下文感知基类,实现了 IContextAware 接口,为需要感知架构上下文的类提供基础实现。
|
||||||
|
|
||||||
|
**使用方式:**
|
||||||
|
|
||||||
```csharp
|
```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 (属于架构)
|
IContextAware (上下文感知接口)
|
||||||
↓ 被继承
|
↓ 被继承于
|
||||||
├── ICanGetModel (可获取 Model)
|
├── AbstractSystem (抽象系统基类)
|
||||||
├── ICanGetSystem (可获取 System)
|
├── AbstractQuery<TInput, TResult> (抽象查询基类)
|
||||||
├── ICanGetUtility (可获取 Utility)
|
├── AbstractModel (抽象模型基类)
|
||||||
├── ICanSendCommand (可发送 Command)
|
└── AbstractCommand (抽象命令基类)
|
||||||
├── ICanSendEvent (可发送 Event)
|
|
||||||
├── ICanSendQuery (可发送 Query)
|
|
||||||
└── ICanRegisterEvent (可注册 Event)
|
|
||||||
|
|
||||||
ICanSetArchitecture (可设置架构)
|
|
||||||
↓ 被继承
|
|
||||||
├── ICommand (命令接口)
|
|
||||||
└── IQuery<T> (查询接口)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用场景
|
## 使用场景
|
||||||
|
|
||||||
### 1. Controller 实现 IBelongToArchitecture
|
### 1. Component 继承 ContextAwareBase
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Controller 通过实现 IBelongToArchitecture 获得架构访问能力
|
// 组件通过继承 ContextAwareBase 获得架构上下文访问能力
|
||||||
public partial class PlayerController : Node, IController
|
public partial class PlayerController : Node, IController
|
||||||
{
|
{
|
||||||
// 实现 IBelongToArchitecture 接口
|
// 不再需要手动实现 IContextAware,基类已处理
|
||||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
// 可以直接使用扩展方法
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
// 因为实现了 IBelongToArchitecture,所以可以使用扩展方法
|
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
this.SendCommand(new InitPlayerCommand());
|
this.SendCommand(new InitPlayerCommand());
|
||||||
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
|
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
|
||||||
@ -97,17 +96,17 @@ public partial class PlayerController : Node, IController
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Command 实现 ICanSetArchitecture
|
### 2. Command 继承 AbstractCommand (IContextAware)
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Command 实现 ICanSetArchitecture,框架会自动注入架构
|
// Command 继承 AbstractCommand,自动成为 IContextAware
|
||||||
public class BuyItemCommand : AbstractCommand
|
public class BuyItemCommand : AbstractCommand
|
||||||
{
|
{
|
||||||
public string ItemId { get; set; }
|
public string ItemId { get; set; }
|
||||||
|
|
||||||
protected override void OnExecute()
|
protected override void OnExecute()
|
||||||
{
|
{
|
||||||
// 框架已经通过 SetArchitecture 注入了架构实例
|
// 框架或上下文系统会自动注入 IArchitectureContext
|
||||||
// 所以这里可以直接使用 this.GetModel
|
// 所以这里可以直接使用 this.GetModel
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
var shopModel = this.GetModel<ShopModel>();
|
var shopModel = this.GetModel<ShopModel>();
|
||||||
@ -125,21 +124,15 @@ public class BuyItemCommand : AbstractCommand
|
|||||||
### 3. 自定义组件遵循规则
|
### 3. 自定义组件遵循规则
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 自定义管理器遵循框架规则
|
// 自定义管理器遵循框架规则,继承 ContextAwareBase
|
||||||
public class SaveManager : IBelongToArchitecture
|
public class SaveManager : ContextAwareBase
|
||||||
{
|
{
|
||||||
private IArchitecture _architecture;
|
// 不再需要手动构造函数传参,上下文由框架注入
|
||||||
|
// protected override void OnContextReady() 可用于初始化
|
||||||
public SaveManager(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
_architecture = architecture;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IArchitecture GetArchitecture() => _architecture;
|
|
||||||
|
|
||||||
public void SaveGame()
|
public void SaveGame()
|
||||||
{
|
{
|
||||||
// 因为实现了 IBelongToArchitecture,可以使用扩展方法
|
// 因为继承了 ContextAwareBase,可以使用扩展方法
|
||||||
var playerModel = this.GetModel<PlayerModel>();
|
var playerModel = this.GetModel<PlayerModel>();
|
||||||
var saveData = new SaveData
|
var saveData = new SaveData
|
||||||
{
|
{
|
||||||
@ -219,9 +212,10 @@ Rule 接口体现了依赖注入(DI)的思想:
|
|||||||
|
|
||||||
```csharp
|
```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)
|
public static void SendCommand<T>(this ICanSendCommand self, T command)
|
||||||
where T : ICommand
|
where T : ICommand
|
||||||
{
|
{
|
||||||
// 自动注入架构依赖
|
// 自动注入架构上下文依赖
|
||||||
command.SetArchitecture(self.GetArchitecture());
|
command.SetContext(self.GetContext());
|
||||||
command.Execute();
|
command.Execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,8 +239,8 @@ Rule 接口遵循接口隔离原则,每个接口职责单一:
|
|||||||
// ❌ 不好的设计:一个大接口包含所有能力
|
// ❌ 不好的设计:一个大接口包含所有能力
|
||||||
public interface IBigInterface
|
public interface IBigInterface
|
||||||
{
|
{
|
||||||
IArchitecture GetArchitecture();
|
void SetContext(IArchitectureContext context);
|
||||||
void SetArchitecture(IArchitecture architecture);
|
IArchitectureContext GetContext();
|
||||||
T GetModel<T>() where T : class, IModel;
|
T GetModel<T>() where T : class, IModel;
|
||||||
T GetSystem<T>() where T : class, ISystem;
|
T GetSystem<T>() where T : class, ISystem;
|
||||||
void SendCommand<T>(T command) where T : ICommand;
|
void SendCommand<T>(T command) where T : ICommand;
|
||||||
@ -254,8 +248,7 @@ public interface IBigInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 好的设计:小接口组合
|
// ✅ 好的设计:小接口组合
|
||||||
public interface IBelongToArchitecture { ... } // 只负责获取架构
|
public interface IContextAware { ... } // 只负责上下文的设置与获取
|
||||||
public interface ICanSetArchitecture { ... } // 只负责设置架构
|
|
||||||
public interface ICanGetModel { ... } // 只负责获取 Model
|
public interface ICanGetModel { ... } // 只负责获取 Model
|
||||||
public interface ICanSendCommand { ... } // 只负责发送 Command
|
public interface ICanSendCommand { ... } // 只负责发送 Command
|
||||||
```
|
```
|
||||||
@ -271,14 +264,14 @@ public interface IController : ICanGetModel, ICanGetSystem, ICanSendCommand,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command 需要设置架构和获取 Model/System
|
// Command 需要上下文感知和获取 Model/System
|
||||||
public interface ICommand : ICanSetArchitecture, ICanGetModel, ICanGetSystem,
|
public interface ICommand : IContextAware, ICanGetModel, ICanGetSystem,
|
||||||
ICanSendEvent, ICanSendQuery
|
ICanSendEvent, ICanSendQuery
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// System 只需要获取其他组件
|
// System 只需要获取其他组件
|
||||||
public interface ISystem : ICanGetModel, ICanGetUtility, ICanGetSystem,
|
public interface ISystem : IContextAware, ICanGetModel, ICanGetUtility, ICanGetSystem,
|
||||||
ICanRegisterEvent, ICanSendEvent, ICanSendQuery
|
ICanRegisterEvent, ICanSendEvent, ICanSendQuery
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -324,10 +317,10 @@ public class DatabaseCommand : AbstractCommand, ICanAccessDatabase
|
|||||||
|
|
||||||
## 相关包
|
## 相关包
|
||||||
|
|
||||||
- [`architecture`](../architecture/README.md) - 定义 IArchitecture 接口
|
- [`architecture`](../architecture/README.md) - 定义 IArchitectureContext 接口
|
||||||
- [`command`](../command/README.md) - Command 实现 ICanSetArchitecture
|
- [`command`](../command/README.md) - Command 继承 AbstractCommand (IContextAware)
|
||||||
- [`query`](../query/README.md) - Query 实现 ICanSetArchitecture
|
- [`query`](../query/README.md) - Query 继承 AbstractQuery (IContextAware)
|
||||||
- [`controller`](../controller/README.md) - Controller 实现 IBelongToArchitecture
|
- [`controller`](../controller/README.md) - Controller 实现 ICanSendCommand 等接口
|
||||||
- [`system`](../system/README.md) - System 实现 IBelongToArchitecture
|
- [`system`](../system/README.md) - System 继承 AbstractSystem (IContextAware)
|
||||||
- [`model`](../model/README.md) - Model 实现 IBelongToArchitecture
|
- [`model`](../model/README.md) - Model 继承 AbstractModel (IContextAware)
|
||||||
- [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法
|
- [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法
|
||||||
@ -24,6 +24,8 @@ System 接口,定义了系统的基本行为。
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
void Init(); // 系统初始化方法
|
void Init(); // 系统初始化方法
|
||||||
|
void Destroy(); // 系统销毁方法
|
||||||
|
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
|
||||||
```
|
```
|
||||||
|
|
||||||
**继承的能力:**
|
**继承的能力:**
|
||||||
@ -39,15 +41,18 @@ void Init(); // 系统初始化方法
|
|||||||
|
|
||||||
### [`AbstractSystem`](AbstractSystem.cs)
|
### [`AbstractSystem`](AbstractSystem.cs)
|
||||||
|
|
||||||
抽象 System 基类,提供了 System 的基础实现。
|
抽象 System 基类,提供了 System 的基础实现。继承自 ContextAwareBase,具有上下文感知能力。
|
||||||
|
|
||||||
**使用方式:**
|
**使用方式:**
|
||||||
|
|
||||||
```csharp
|
```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 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);
|
return Math.Max(1, attackPower - defense / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnDestroy()
|
||||||
|
{
|
||||||
|
// 清理资源
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -7,17 +7,7 @@ Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如
|
|||||||
|
|
||||||
## 核心接口
|
## 核心接口
|
||||||
|
|
||||||
### [`ICanGetUtility`](ICanGetUtility.cs)
|
### IUtility
|
||||||
|
|
||||||
标记接口,表示该类型可以获取 Utility。
|
|
||||||
|
|
||||||
**继承关系:**
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public interface ICanGetUtility : IBelongToArchitecture
|
|
||||||
```
|
|
||||||
|
|
||||||
### [`IUtility`](IUtility.cs)
|
|
||||||
|
|
||||||
Utility 标记接口,所有工具类都应实现此接口。
|
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
|
### 1. 定义 Utility
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// 存储工具类
|
// 存储工具类,继承自AbstractContextUtility
|
||||||
public class StorageUtility : IUtility
|
public class StorageUtility : AbstractContextUtility
|
||||||
{
|
{
|
||||||
private const string SavePath = "user://save_data.json";
|
private const string SavePath = "user://save_data.json";
|
||||||
|
|
||||||
public void Save<T>(T data)
|
protected override void OnInit()
|
||||||
{
|
{
|
||||||
string json = Json.Stringify(data);
|
Logger.Info("StorageUtility initialized");
|
||||||
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
|
|
||||||
file.StoreString(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Load<T>() where T : new()
|
public void Save<T>(T data)
|
||||||
{
|
{
|
||||||
if (!FileAccess.FileExists(SavePath))
|
string json = JsonSerializer.Serialize(data);
|
||||||
return new T();
|
// 实际保存逻辑
|
||||||
|
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.ReadAllText(SavePath);
|
||||||
string json = file.GetAsText();
|
return JsonSerializer.Deserialize<T>(json);
|
||||||
return Json.Parse<T>(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete()
|
public void Delete()
|
||||||
{
|
{
|
||||||
if (FileAccess.FileExists(SavePath))
|
if (File.Exists(SavePath))
|
||||||
{
|
{
|
||||||
DirAccess.RemoveAbsolute(SavePath);
|
File.Delete(SavePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数学工具类
|
// 数学工具类,作为普通Utility
|
||||||
public class MathUtility : IUtility
|
public class MathUtility : IUtility
|
||||||
{
|
{
|
||||||
public float Lerp(float a, float b, float t)
|
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 value >= min && value <= max;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user