mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +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 RegisterUtility<TUtility>(TUtility utility) where TUtility : IUtility;
|
||||
|
||||
// 组件获取
|
||||
T GetSystem<T>() where T : class, ISystem;
|
||||
// 组件获取(通过容器)
|
||||
T GetModel<T>() where T : class, IModel;
|
||||
T GetSystem<T>() where T : class, ISystem;
|
||||
T GetUtility<T>() where T : class, IUtility;
|
||||
|
||||
// 命令处理
|
||||
@ -47,7 +47,7 @@ IUnRegister RegisterEvent<T>(Action<T> onEvent);
|
||||
void UnRegisterEvent<T>(Action<T> onEvent);
|
||||
```
|
||||
|
||||
### [`IArchitecturePhaseAware`](IArchitecturePhaseAware.cs)
|
||||
### IArchitecturePhaseAware
|
||||
|
||||
架构阶段感知接口,允许组件监听架构阶段变化。
|
||||
|
||||
@ -57,7 +57,7 @@ void UnRegisterEvent<T>(Action<T> onEvent);
|
||||
void OnArchitecturePhase(ArchitecturePhase phase);
|
||||
```
|
||||
|
||||
### [`IArchitectureModule`](IArchitectureModule.cs)
|
||||
### IArchitectureModule
|
||||
|
||||
架构模块接口,支持模块化架构扩展。
|
||||
|
||||
@ -67,7 +67,7 @@ void OnArchitecturePhase(ArchitecturePhase phase);
|
||||
void Install(IArchitecture architecture);
|
||||
```
|
||||
|
||||
### [`IAsyncInitializable`](IAsyncInitializable.cs)
|
||||
### IAsyncInitializable
|
||||
|
||||
异步初始化接口,支持组件异步初始化。
|
||||
|
||||
@ -83,6 +83,17 @@ Task InitializeAsync();
|
||||
|
||||
架构基类,实现了 `IArchitecture` 接口,提供完整的架构功能实现。
|
||||
|
||||
**构造函数参数:**
|
||||
|
||||
```csharp
|
||||
public abstract class Architecture(
|
||||
IArchitectureConfiguration? configuration = null,
|
||||
IEnvironment? environment = null,
|
||||
IArchitectureServices? services = null,
|
||||
IArchitectureContext? context = null
|
||||
)
|
||||
```
|
||||
|
||||
**特性:**
|
||||
|
||||
- **阶段式生命周期管理**
|
||||
@ -132,7 +143,7 @@ public enum ArchitecturePhase
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 1. 定义你的架构(继承 Architecture 基类)
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
@ -196,7 +207,7 @@ public class GameController : IController
|
||||
|
||||
**高级特性:**
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 1. 使用自定义配置
|
||||
var config = new ArchitectureConfiguration();
|
||||
var architecture = new GameArchitecture(configuration: config);
|
||||
@ -238,7 +249,7 @@ public class LifecycleHook : IArchitectureLifecycle
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
var config = new ArchitectureConfiguration
|
||||
{
|
||||
// 严格阶段验证
|
||||
|
||||
@ -10,16 +10,6 @@ Command 包实现了命令模式(Command Pattern),用于封装用户操作
|
||||
|
||||
无返回值命令接口,定义了命令的基本契约。
|
||||
|
||||
**继承的能力接口:**
|
||||
|
||||
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构
|
||||
- [`ICanGetSystem`](../system/ICanGetSystem.cs) - 可获取系统
|
||||
- [`ICanGetModel`](../model/ICanGetModel.cs) - 可获取模型
|
||||
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具
|
||||
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
|
||||
- [`ICanSendCommand`](ICanSendCommand.cs) - 可发送命令
|
||||
- [`ICanSendQuery`](../query/ICanSendQuery.cs) - 可发送查询
|
||||
|
||||
**核心方法:**
|
||||
|
||||
```csharp
|
||||
@ -36,30 +26,39 @@ void Execute(); // 执行命令
|
||||
TResult Execute(); // 执行命令并返回结果
|
||||
```
|
||||
|
||||
### 3. [`ICanSendCommand`](ICanSendCommand.cs)
|
||||
|
||||
标记接口,表示实现者可以发送命令。继承自 [`IBelongToArchitecture`](../rule/IBelongToArchitecture.cs)。
|
||||
|
||||
## 核心类
|
||||
|
||||
### 1. [`AbstractCommand`](AbstractCommand.cs)
|
||||
### 1. [`AbstractCommand<TInput>`](AbstractCommand.cs)
|
||||
|
||||
无返回值命令的抽象基类,提供了命令的基础实现。
|
||||
无返回值命令的抽象基类,提供了命令的基础实现。它继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)
|
||||
,具有上下文感知能力。
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
// 定义一个开始游戏的命令
|
||||
public class StartGameCommand : AbstractCommand
|
||||
// 定义一个命令输入参数
|
||||
public struct StartGameCommandInput : ICommandInput
|
||||
{
|
||||
protected override void OnExecute()
|
||||
public int LevelId { get; set; }
|
||||
public string PlayerName { get; set; }
|
||||
}
|
||||
|
||||
// 定义一个开始游戏的命令
|
||||
public class StartGameCommand : AbstractCommand<StartGameCommandInput>
|
||||
{
|
||||
public StartGameCommand(StartGameCommandInput input) : base(input)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnExecute(StartGameCommandInput input)
|
||||
{
|
||||
// 获取需要的模型
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
var gameModel = this.GetModel<GameModel>();
|
||||
|
||||
// 执行业务逻辑
|
||||
playerModel.Health.Value = 100;
|
||||
playerModel.PlayerName.Value = input.PlayerName;
|
||||
gameModel.CurrentLevel.Value = input.LevelId;
|
||||
gameModel.GameState.Value = GameState.Playing;
|
||||
|
||||
// 发送事件通知其他模块
|
||||
@ -74,41 +73,44 @@ public class GameController : IController
|
||||
|
||||
public void OnStartButtonClicked()
|
||||
{
|
||||
// 方式1:发送命令实例
|
||||
this.SendCommand(new StartGameCommand());
|
||||
|
||||
// 方式2:通过泛型发送(需要无参构造函数)
|
||||
this.SendCommand<StartGameCommand>();
|
||||
// 发送命令实例
|
||||
this.SendCommand(new StartGameCommand(new StartGameCommandInput
|
||||
{
|
||||
LevelId = 1,
|
||||
PlayerName = "Player1"
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. [`AbstractCommand<TResult>`](AbstractCommand.cs)
|
||||
### 2. [`AbstractCommand<TInput, TResult>`](AbstractCommand.cs)
|
||||
|
||||
带返回值命令的抽象基类。
|
||||
带返回值命令的抽象基类,同样继承自 [ContextAwareBase](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L4-L28)。
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
// 定义一个计算伤害的命令
|
||||
public class CalculateDamageCommand : AbstractCommand<int>
|
||||
// 定义一个计算伤害的命令输入
|
||||
public struct CalculateDamageCommandInput : ICommandInput
|
||||
{
|
||||
private readonly int _attackPower;
|
||||
private readonly int _defense;
|
||||
|
||||
public CalculateDamageCommand(int attackPower, int defense)
|
||||
public int AttackerAttackPower { get; set; }
|
||||
public int DefenderDefense { get; set; }
|
||||
}
|
||||
|
||||
// 定义一个计算伤害的命令
|
||||
public class CalculateDamageCommand : AbstractCommand<CalculateDamageCommandInput, int>
|
||||
{
|
||||
public CalculateDamageCommand(CalculateDamageCommandInput input) : base(input)
|
||||
{
|
||||
_attackPower = attackPower;
|
||||
_defense = defense;
|
||||
}
|
||||
|
||||
protected override int OnExecute()
|
||||
|
||||
protected override int OnExecute(CalculateDamageCommandInput input)
|
||||
{
|
||||
// 获取游戏配置
|
||||
var config = this.GetModel<GameConfigModel>();
|
||||
|
||||
// 计算最终伤害
|
||||
var baseDamage = _attackPower - _defense;
|
||||
var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
|
||||
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
|
||||
|
||||
return (int)finalDamage;
|
||||
@ -123,9 +125,11 @@ public class CombatSystem : AbstractSystem
|
||||
public void Attack(Character attacker, Character defender)
|
||||
{
|
||||
// 发送命令并获取返回值
|
||||
var damage = this.SendCommand(
|
||||
new CalculateDamageCommand(attacker.AttackPower, defender.Defense)
|
||||
);
|
||||
var damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput
|
||||
{
|
||||
AttackerAttackPower = attacker.AttackPower,
|
||||
DefenderDefense = defender.Defense
|
||||
}));
|
||||
|
||||
// 应用伤害
|
||||
defender.Health -= damage;
|
||||
@ -139,26 +143,78 @@ public class CombatSystem : AbstractSystem
|
||||
## 命令的生命周期
|
||||
|
||||
1. **创建命令**:实例化命令对象,传入必要的参数
|
||||
2. **设置架构**:框架自动调用 `SetArchitecture()` 设置架构引用
|
||||
3. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
|
||||
4. **返回结果**:对于带返回值的命令,返回执行结果
|
||||
5. **命令销毁**:命令执行完毕后可以被垃圾回收
|
||||
2. **执行命令**:调用 `Execute()` 方法,内部委托给 `OnExecute()`
|
||||
3. **返回结果**:对于带返回值的命令,返回执行结果
|
||||
4. **命令销毁**:命令执行完毕后可以被垃圾回收
|
||||
|
||||
## CommandBus - 命令总线
|
||||
|
||||
### 功能说明
|
||||
|
||||
[CommandBus](file:///d:/Project/Rider/GFramework/GFramework.Core/command/CommandBus.cs#L8-L34) 是命令执行的核心组件,负责发送和执行命令。
|
||||
|
||||
**主要方法:**
|
||||
|
||||
```csharp
|
||||
void Send(ICommand command); // 发送无返回值命令
|
||||
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```csharp
|
||||
var commandBus = new CommandBus();
|
||||
|
||||
// 发送无返回值命令
|
||||
commandBus.Send(new StartGameCommand(new StartGameCommandInput()));
|
||||
|
||||
// 发送带返回值命令
|
||||
var result = commandBus.Send(new CalculateDamageCommand(new CalculateDamageCommandInput()));
|
||||
```
|
||||
|
||||
## EmptyCommandInput - 空命令输入
|
||||
|
||||
当命令不需要输入参数时,可以使用 [EmptyCommentInput](file:///d:/Project/Rider/GFramework/GFramework.Core/command/EmptyCommentInput.cs#L7-L11)
|
||||
类:
|
||||
|
||||
```csharp
|
||||
public class SimpleActionCommand : AbstractCommand<EmptyCommentInput>
|
||||
{
|
||||
public SimpleActionCommand(EmptyCommentInput input) : base(input)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnExecute(EmptyCommentInput input)
|
||||
{
|
||||
// 执行简单操作,无需额外参数
|
||||
this.SendEvent(new SimpleActionEvent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 1. 用户交互操作
|
||||
|
||||
```csharp
|
||||
// UI 按钮点击
|
||||
public class SaveGameCommand : AbstractCommand
|
||||
public struct SaveGameCommandInput : ICommandInput
|
||||
{
|
||||
protected override void OnExecute()
|
||||
public string SaveSlot { get; set; }
|
||||
}
|
||||
|
||||
public class SaveGameCommand : AbstractCommand<SaveGameCommandInput>
|
||||
{
|
||||
public SaveGameCommand(SaveGameCommandInput input) : base(input)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnExecute(SaveGameCommandInput input)
|
||||
{
|
||||
var saveSystem = this.GetSystem<SaveSystem>();
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
|
||||
saveSystem.SavePlayerData(playerModel);
|
||||
this.SendEvent(new GameSavedEvent());
|
||||
saveSystem.SavePlayerData(playerModel, input.SaveSlot);
|
||||
this.SendEvent(new GameSavedEvent(input.SaveSlot));
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -166,17 +222,18 @@ public class SaveGameCommand : AbstractCommand
|
||||
### 2. 业务流程控制
|
||||
|
||||
```csharp
|
||||
// 关卡切换
|
||||
public class LoadLevelCommand : AbstractCommand
|
||||
public struct LoadLevelCommandInput : ICommandInput
|
||||
{
|
||||
private readonly int _levelId;
|
||||
|
||||
public LoadLevelCommand(int levelId)
|
||||
public int LevelId { get; set; }
|
||||
}
|
||||
|
||||
public class LoadLevelCommand : AbstractCommand<LoadLevelCommandInput>
|
||||
{
|
||||
public LoadLevelCommand(LoadLevelCommandInput input) : base(input)
|
||||
{
|
||||
_levelId = levelId;
|
||||
}
|
||||
|
||||
protected override void OnExecute()
|
||||
|
||||
protected override void OnExecute(LoadLevelCommandInput input)
|
||||
{
|
||||
var levelSystem = this.GetSystem<LevelSystem>();
|
||||
var uiSystem = this.GetSystem<UISystem>();
|
||||
@ -185,99 +242,50 @@ public class LoadLevelCommand : AbstractCommand
|
||||
uiSystem.ShowLoadingScreen();
|
||||
|
||||
// 加载关卡
|
||||
levelSystem.LoadLevel(_levelId);
|
||||
levelSystem.LoadLevel(input.LevelId);
|
||||
|
||||
// 发送事件
|
||||
this.SendEvent(new LevelLoadedEvent(_levelId));
|
||||
this.SendEvent(new LevelLoadedEvent(input.LevelId));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 网络请求封装
|
||||
|
||||
```csharp
|
||||
// 登录命令
|
||||
public class LoginCommand : AbstractCommand<bool>
|
||||
{
|
||||
private readonly string _username;
|
||||
private readonly string _password;
|
||||
|
||||
public LoginCommand(string username, string password)
|
||||
{
|
||||
_username = username;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
protected override bool OnExecute()
|
||||
{
|
||||
var networkSystem = this.GetSystem<NetworkSystem>();
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
|
||||
// 发送登录请求
|
||||
var result = networkSystem.Login(_username, _password);
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
playerModel.UserId = result.UserId;
|
||||
playerModel.Username = _username;
|
||||
this.SendEvent(new LoginSuccessEvent());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SendEvent(new LoginFailedEvent(result.ErrorMessage));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 命令 vs 系统方法
|
||||
|
||||
**何时使用命令:**
|
||||
|
||||
- 需要参数化操作
|
||||
- 需要记录操作历史(用于撤销/重做)
|
||||
- 操作需要跨多个系统协调
|
||||
- 用户触发的离散操作
|
||||
|
||||
**何时使用系统方法:**
|
||||
|
||||
- 持续运行的逻辑(如每帧更新)
|
||||
- 系统内部的私有逻辑
|
||||
- 不需要外部调用的功能
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **保持命令原子性**:一个命令应该完成一个完整的业务操作
|
||||
2. **命令无状态**:命令不应该保存长期状态,执行完即可丢弃
|
||||
3. **参数通过构造函数传递**:命令需要的参数应在创建时传入
|
||||
4. **避免命令嵌套**:命令内部尽量不要发送其他命令,使用事件通信
|
||||
5. **合理使用返回值**:只在确实需要返回结果时使用 `ICommand<TResult>`
|
||||
5. **合理使用返回值**:只在确实需要返回结果时使用 `AbstractCommand<TInput, TResult>`
|
||||
6. **命令命名规范**:使用动词+名词形式,如 `StartGameCommand`、`SavePlayerCommand`
|
||||
7. **输入参数结构化**:使用 `ICommandInput` 接口的实现类来组织命令参数
|
||||
|
||||
## 扩展功能
|
||||
|
||||
### 命令撤销/重做(可扩展)
|
||||
|
||||
```csharp
|
||||
// 可撤销命令接口
|
||||
public interface IUndoableCommand : ICommand
|
||||
public struct MoveCommandInput : ICommandInput
|
||||
{
|
||||
void Undo();
|
||||
public Vector3 NewPosition { get; set; }
|
||||
}
|
||||
|
||||
// 实现可撤销命令
|
||||
public class MoveCommand : AbstractCommand, IUndoableCommand
|
||||
public class MoveCommand : AbstractCommand<MoveCommandInput>, IUndoableCommand
|
||||
{
|
||||
private Vector3 _oldPosition;
|
||||
private Vector3 _newPosition;
|
||||
|
||||
protected override void OnExecute()
|
||||
public MoveCommand(MoveCommandInput input) : base(input)
|
||||
{
|
||||
_newPosition = input.NewPosition;
|
||||
}
|
||||
|
||||
protected override void OnExecute(MoveCommandInput input)
|
||||
{
|
||||
var player = this.GetModel<PlayerModel>();
|
||||
_oldPosition = player.Position;
|
||||
player.Position = _newPosition;
|
||||
player.Position = input.NewPosition;
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
|
||||
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); // 注册事件处理函数
|
||||
```
|
||||
|
||||
### 2. [`IUnRegister`](IUnRegister.cs)
|
||||
### 2. [`IUnRegister`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegister.cs#L6-L10)
|
||||
|
||||
注销接口,用于取消事件注册。
|
||||
|
||||
@ -26,20 +26,28 @@ IUnRegister Register(Action onEvent); // 注册事件处理函数
|
||||
void UnRegister(); // 执行注销操作
|
||||
```
|
||||
|
||||
### 3. [`IUnRegisterList`](IUnRegisterList.cs)
|
||||
### 3. [
|
||||
`IUnRegisterList`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IUnRegisterList.cs#L6-L10)
|
||||
|
||||
注销列表接口,用于批量管理注销对象。
|
||||
|
||||
**属性:**
|
||||
|
||||
```csharp
|
||||
List<IUnRegister> UnregisterList { get; } // 获取注销列表
|
||||
IList<IUnRegister> UnregisterList { get; } // 获取注销列表
|
||||
```
|
||||
|
||||
### 4. 能力接口
|
||||
### 4. [`IEventBus`](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/events/IEventBus.cs#L6-L22)
|
||||
|
||||
- [`ICanRegisterEvent`](ICanRegisterEvent.cs) - 标记可以注册事件的组件
|
||||
- [`ICanSendEvent`](ICanSendEvent.cs) - 标记可以发送事件的组件
|
||||
事件总线接口,提供基于类型的事件发送和注册。
|
||||
|
||||
**核心方法:**
|
||||
|
||||
```csharp
|
||||
IUnRegister Register<T>(Action<T> onEvent); // 注册类型化事件
|
||||
void Send<T>(T e); // 发送事件实例
|
||||
void Send<T>() where T : new(); // 发送事件(自动创建实例)
|
||||
```
|
||||
|
||||
## 核心类
|
||||
|
||||
@ -66,7 +74,7 @@ onClicked.Trigger();
|
||||
unregister.UnRegister();
|
||||
```
|
||||
|
||||
### 2. [`EasyEvent<T>`](EasyEventGeneric.cs)
|
||||
### 2. [`Event<T>`](EasyEventGeneric.cs)
|
||||
|
||||
单参数泛型事件类,支持一个参数的事件。
|
||||
|
||||
@ -74,7 +82,7 @@ unregister.UnRegister();
|
||||
|
||||
```csharp
|
||||
// 创建带参数的事件
|
||||
var onScoreChanged = new EasyEvent<int>();
|
||||
var onScoreChanged = new Event<int>();
|
||||
|
||||
// 注册监听
|
||||
onScoreChanged.Register(newScore =>
|
||||
@ -86,7 +94,7 @@ onScoreChanged.Register(newScore =>
|
||||
onScoreChanged.Trigger(100);
|
||||
```
|
||||
|
||||
### 3. [`EasyEvent<T, TK>`](EasyEventGeneric.cs)
|
||||
### 3. [`Event<T, TK>`](EasyEventGeneric.cs)
|
||||
|
||||
双参数泛型事件类。
|
||||
|
||||
@ -94,7 +102,7 @@ onScoreChanged.Trigger(100);
|
||||
|
||||
```csharp
|
||||
// 伤害事件:攻击者、伤害值
|
||||
var onDamageDealt = new EasyEvent<string, int>();
|
||||
var onDamageDealt = new Event<string, int>();
|
||||
|
||||
onDamageDealt.Register((attacker, damage) =>
|
||||
{
|
||||
@ -104,25 +112,7 @@ onDamageDealt.Register((attacker, damage) =>
|
||||
onDamageDealt.Trigger("Player", 50);
|
||||
```
|
||||
|
||||
### 4. [`EasyEvent<T, TK, TS>`](EasyEventGeneric.cs)
|
||||
|
||||
三参数泛型事件类。
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
// 位置变化事件:对象、旧位置、新位置
|
||||
var onPositionChanged = new EasyEvent<string, Vector3, Vector3>();
|
||||
|
||||
onPositionChanged.Register((obj, oldPos, newPos) =>
|
||||
{
|
||||
GD.Print($"{obj} moved from {oldPos} to {newPos}");
|
||||
});
|
||||
|
||||
onPositionChanged.Trigger("Player", Vector3.Zero, new Vector3(10, 0, 0));
|
||||
```
|
||||
|
||||
### 5. [`EasyEvents`](EasyEvents.cs)
|
||||
### 4. [`EasyEvents`](EasyEvents.cs)
|
||||
|
||||
全局事件管理器,提供类型安全的事件注册和获取。
|
||||
|
||||
@ -145,7 +135,7 @@ gameStartEvent.Register(() =>
|
||||
gameStartEvent.Trigger();
|
||||
```
|
||||
|
||||
### 6. [`TypeEventSystem`](TypeEventSystem.cs)
|
||||
### 5. [`EventBus`](EventBus.cs)
|
||||
|
||||
类型化事件系统,支持基于类型的事件发送和注册。
|
||||
|
||||
@ -153,25 +143,25 @@ gameStartEvent.Trigger();
|
||||
|
||||
```csharp
|
||||
// 使用全局事件系统
|
||||
var eventSystem = TypeEventSystem.Global;
|
||||
var eventBus = new EventBus();
|
||||
|
||||
// 注册类型化事件
|
||||
eventSystem.Register<PlayerDiedEvent>(e =>
|
||||
eventBus.Register<PlayerDiedEvent>(e =>
|
||||
{
|
||||
GD.Print($"Player died at position: {e.Position}");
|
||||
});
|
||||
|
||||
// 发送事件(自动创建实例)
|
||||
eventSystem.Send<PlayerDiedEvent>();
|
||||
|
||||
// 发送事件(传递实例)
|
||||
eventSystem.Send(new PlayerDiedEvent
|
||||
eventBus.Send(new PlayerDiedEvent
|
||||
{
|
||||
Position = new Vector3(10, 0, 5)
|
||||
});
|
||||
|
||||
// 发送事件(自动创建实例)
|
||||
eventBus.Send<PlayerDiedEvent>();
|
||||
```
|
||||
|
||||
### 7. [`DefaultUnRegister`](DefaultUnRegister.cs)
|
||||
### 6. [`DefaultUnRegister`](DefaultUnRegister.cs)
|
||||
|
||||
默认注销器实现,封装注销回调。
|
||||
|
||||
@ -185,7 +175,7 @@ var unregister = new DefaultUnRegister(onUnregister);
|
||||
unregister.UnRegister();
|
||||
```
|
||||
|
||||
### 8. [`OrEvent`](OrEvent.cs)
|
||||
### 7. [`OrEvent`](OrEvent.cs)
|
||||
|
||||
事件或运算组合器,当任意一个事件触发时触发。
|
||||
|
||||
@ -204,6 +194,33 @@ onAnyInput.Register(() =>
|
||||
});
|
||||
```
|
||||
|
||||
### 8. [`UnRegisterList`](UnRegisterList.cs)
|
||||
|
||||
批量管理注销对象的列表。
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
var unregisterList = new UnRegisterList();
|
||||
|
||||
// 添加到列表
|
||||
someEvent.Register(OnEvent).AddToUnregisterList(unregisterList);
|
||||
|
||||
// 批量注销
|
||||
unregisterList.UnRegisterAll();
|
||||
```
|
||||
|
||||
### 9. [`ArchitectureEvents`](ArchitectureEvents.cs)
|
||||
|
||||
定义了架构生命周期相关的事件。
|
||||
|
||||
**包含事件:**
|
||||
|
||||
- `ArchitectureLifecycleReadyEvent` - 架构生命周期准备就绪
|
||||
- `ArchitectureDestroyingEvent` - 架构销毁中
|
||||
- `ArchitectureDestroyedEvent` - 架构已销毁
|
||||
- `ArchitectureFailedInitializationEvent` - 架构初始化失败
|
||||
|
||||
## 在架构中使用事件
|
||||
|
||||
### 定义事件类
|
||||
|
||||
@ -6,64 +6,69 @@ Extensions 包提供了一系列扩展方法,简化了框架各个接口的使
|
||||
|
||||
## 扩展方法类别
|
||||
|
||||
### 1. 获取组件扩展 ([`CanGetExtensions.cs`](CanGetExtensions.cs))
|
||||
### 1. ContextAware 扩展 ([`ContextAwareExtensions.cs`](ContextAwareExtensions.cs))
|
||||
|
||||
为 [`ICanGetModel`](../model/ICanGetModel.cs)、[`ICanGetSystem`](../system/ICanGetSystem.cs)、[
|
||||
`ICanGetUtility`](../utility/ICanGetUtility.cs) 提供扩展方法。
|
||||
为 [`IContextAware`](../../GFramework.Core.Abstractions/rule/IContextAware.cs)
|
||||
提供扩展方法,允许直接从实现了 [IContextAware](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs)
|
||||
的对象获取架构组件。
|
||||
|
||||
#### CanGetModelExtension
|
||||
#### GetSystem 扩展方法
|
||||
|
||||
```csharp
|
||||
public static T GetModel<T>(this ICanGetModel self) where T : class, IModel
|
||||
public static TSystem? GetSystem<TSystem>(this IContextAware contextAware)
|
||||
where TSystem : class, ISystem
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
// 在 Controller、Command、Query 中使用
|
||||
// 在实现了 IContextAware 的类中使用
|
||||
public class PlayerController : IController
|
||||
{
|
||||
public void UpdateUI()
|
||||
{
|
||||
// 直接通过 this 调用
|
||||
var playerSystem = this.GetSystem<PlayerSystem>();
|
||||
var inventorySystem = this.GetSystem<InventorySystem>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### GetModel 扩展方法
|
||||
|
||||
```csharp
|
||||
public static TModel? GetModel<TModel>(this IContextAware contextAware)
|
||||
where TModel : class, IModel
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class PlayerController : IController
|
||||
{
|
||||
public void UpdateStats()
|
||||
{
|
||||
// 获取模型
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
var inventoryModel = this.GetModel<InventoryModel>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### CanGetSystemExtension
|
||||
|
||||
```csharp
|
||||
public static T GetSystem<T>(this ICanGetSystem self) where T : class, ISystem
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class SaveCommand : AbstractCommand
|
||||
{
|
||||
protected override void OnExecute()
|
||||
{
|
||||
// 获取系统
|
||||
var saveSystem = this.GetSystem<SaveSystem>();
|
||||
var networkSystem = this.GetSystem<NetworkSystem>();
|
||||
|
||||
saveSystem.SaveGame();
|
||||
// 使用模型数据
|
||||
playerModel.Health += 10;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### CanGetUtilityExtension
|
||||
#### GetUtility 扩展方法
|
||||
|
||||
```csharp
|
||||
public static T GetUtility<T>(this ICanGetUtility self) where T : class, IUtility
|
||||
public static TUtility? GetUtility<TUtility>(this IContextAware contextAware)
|
||||
where TUtility : class, IUtility
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class GameModel : AbstractModel
|
||||
public class GameModel : AbstractModel, IContextAware
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
@ -74,24 +79,14 @@ public class GameModel : AbstractModel
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 发送命令扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
|
||||
|
||||
为 [`ICanSendCommand`](../command/ICanSendCommand.cs) 提供扩展方法。
|
||||
|
||||
#### CanSendCommandExtension
|
||||
#### SendCommand 扩展方法
|
||||
|
||||
```csharp
|
||||
// 发送无参命令(通过类型)
|
||||
public static void SendCommand<T>(this ICanSendCommand self)
|
||||
where T : ICommand, new()
|
||||
|
||||
// 发送命令实例
|
||||
public static void SendCommand<T>(this ICanSendCommand self, T command)
|
||||
where T : ICommand
|
||||
// 发送无返回值的命令
|
||||
public static void SendCommand(this IContextAware contextAware, ICommand command)
|
||||
|
||||
// 发送带返回值的命令
|
||||
public static TResult SendCommand<TResult>(this ICanSendCommand self,
|
||||
ICommand<TResult> command)
|
||||
public static TResult SendCommand<TResult>(this IContextAware contextAware, ICommand<TResult> command)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
@ -101,36 +96,51 @@ public class GameController : IController
|
||||
{
|
||||
public void OnStartButtonClicked()
|
||||
{
|
||||
// 方式1:通过类型发送(需要无参构造函数)
|
||||
this.SendCommand<StartGameCommand>();
|
||||
// 发送命令实例
|
||||
this.SendCommand(new StartGameCommand());
|
||||
|
||||
// 方式2:发送命令实例
|
||||
this.SendCommand(new LoadLevelCommand(levelId: 1));
|
||||
|
||||
// 方式3:发送带返回值的命令
|
||||
var score = this.SendCommand(new CalculateScoreCommand());
|
||||
// 发送带返回值的命令
|
||||
var result = this.SendCommand(new CalculateScoreCommand());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 发送事件扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
|
||||
|
||||
为 [`ICanSendEvent`](../events/ICanSendEvent.cs) 提供扩展方法。
|
||||
|
||||
#### CanSendEventExtension
|
||||
#### SendQuery 扩展方法
|
||||
|
||||
```csharp
|
||||
// 发送无参事件
|
||||
public static void SendEvent<T>(this ICanSendEvent self) where T : new()
|
||||
|
||||
// 发送事件实例
|
||||
public static void SendEvent<T>(this ICanSendEvent self, T e)
|
||||
public static TResult SendQuery<TResult>(this IContextAware contextAware, IQuery<TResult> query)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class PlayerModel : AbstractModel
|
||||
public class InventoryController : IController
|
||||
{
|
||||
public void ShowInventory()
|
||||
{
|
||||
// 发送查询获取数据
|
||||
var items = this.SendQuery(new GetInventoryItemsQuery());
|
||||
var gold = this.SendQuery(new GetPlayerGoldQuery());
|
||||
|
||||
UpdateInventoryUI(items, gold);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### SendEvent 扩展方法
|
||||
|
||||
```csharp
|
||||
// 发送无参事件
|
||||
public static void SendEvent<T>(this IContextAware contextAware) where T : new()
|
||||
|
||||
// 发送事件实例
|
||||
public static void SendEvent<T>(this IContextAware contextAware, T e) where T : class
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class PlayerModel : AbstractModel, IContextAware
|
||||
{
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
@ -152,57 +162,20 @@ public class PlayerModel : AbstractModel
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 发送查询扩展 ([`CanSendExtensions.cs`](CanSendExtensions.cs))
|
||||
|
||||
为 [`ICanSendQuery`](../query/ICanSendQuery.cs) 提供扩展方法。
|
||||
|
||||
#### CanSendQueryExtension
|
||||
#### RegisterEvent 扩展方法
|
||||
|
||||
```csharp
|
||||
public static TResult SendQuery<TResult>(this ICanSendQuery self,
|
||||
IQuery<TResult> query)
|
||||
public static IUnRegister RegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> handler)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class InventoryController : IController
|
||||
{
|
||||
public void ShowInventory()
|
||||
{
|
||||
// 发送查询获取数据
|
||||
var items = this.SendQuery(new GetInventoryItemsQuery());
|
||||
var gold = this.SendQuery(new GetPlayerGoldQuery());
|
||||
|
||||
UpdateInventoryUI(items, gold);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 注册事件扩展 ([`CanRegisterEventExtensions.cs`](CanRegisterEventExtensions.cs))
|
||||
|
||||
为 [`ICanRegisterEvent`](../events/ICanRegisterEvent.cs) 提供扩展方法。
|
||||
|
||||
#### CanRegisterEventExtensions
|
||||
|
||||
```csharp
|
||||
// 注册事件
|
||||
public static IUnRegister RegisterEvent<T>(this ICanRegisterEvent self,
|
||||
Action<T> onEvent)
|
||||
|
||||
// 注销事件
|
||||
public static void UnRegisterEvent<T>(this ICanRegisterEvent self,
|
||||
Action<T> onEvent)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class GameController : Node, IController
|
||||
public class GameController : IController
|
||||
{
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public override void _Ready()
|
||||
public void Initialize()
|
||||
{
|
||||
// 注册事件监听
|
||||
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
|
||||
@ -217,14 +190,143 @@ public class GameController : Node, IController
|
||||
}
|
||||
```
|
||||
|
||||
### 6. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs))
|
||||
#### UnRegisterEvent 扩展方法
|
||||
|
||||
为 [`IEasyEvent`](../events/IEasyEvent.cs) 提供事件组合功能。
|
||||
```csharp
|
||||
public static void UnRegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> onEvent)
|
||||
```
|
||||
|
||||
### GetEnvironment 扩展方法
|
||||
|
||||
```csharp
|
||||
public static T? GetEnvironment<T>(this IContextAware contextAware) where T : class
|
||||
public static IEnvironment GetEnvironment(this IContextAware contextAware)
|
||||
```
|
||||
|
||||
### 2. Object 扩展 ([`ObjectExtensions.cs`](ObjectExtensions.cs))
|
||||
|
||||
提供基于运行时类型判断的对象扩展方法,用于简化类型分支、链式调用和架构分派逻辑。
|
||||
|
||||
#### IfType 扩展方法
|
||||
|
||||
```csharp
|
||||
// 最简单的类型判断
|
||||
public static bool IfType<T>(this object obj, Action<T> action)
|
||||
|
||||
// 带条件的类型判断
|
||||
public static bool IfType<T>(
|
||||
this object obj,
|
||||
Func<T, bool> predicate,
|
||||
Action<T> action
|
||||
)
|
||||
|
||||
// 条件判断,带不匹配时的处理
|
||||
public static void IfType<T>(
|
||||
this object obj,
|
||||
Action<T> whenMatch,
|
||||
Action<object>? whenNotMatch = null
|
||||
)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
object obj = new MyRule();
|
||||
|
||||
// 简单类型判断
|
||||
bool executed = obj.IfType<MyRule>(rule =>
|
||||
{
|
||||
rule.Initialize();
|
||||
});
|
||||
|
||||
// 带条件的类型判断
|
||||
obj.IfType<MyRule>(
|
||||
r => r.Enabled, // 条件
|
||||
r => r.Execute() // 执行动作
|
||||
);
|
||||
|
||||
// 带不匹配处理的类型判断
|
||||
obj.IfType<IRule>(
|
||||
rule => rule.Execute(),
|
||||
other => Logger.Warn($"Unsupported type: {other.GetType()}")
|
||||
);
|
||||
```
|
||||
|
||||
#### IfType<T, TResult> 扩展方法
|
||||
|
||||
```csharp
|
||||
public static TResult? IfType<T, TResult>(
|
||||
this object obj,
|
||||
Func<T, TResult> func
|
||||
)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
object obj = new MyRule { Name = "TestRule" };
|
||||
|
||||
string? name = obj.IfType<MyRule, string>(r => r.Name);
|
||||
```
|
||||
|
||||
#### As 和 Do 扩展方法
|
||||
|
||||
```csharp
|
||||
// 安全类型转换
|
||||
public static T? As<T>(this object obj) where T : class
|
||||
|
||||
// 流式调用
|
||||
public static T Do<T>(this T obj, Action<T> action)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
// 安全类型转换
|
||||
obj.As<MyRule>()
|
||||
?.Execute();
|
||||
|
||||
// 流式调用
|
||||
obj.As<MyRule>()
|
||||
?.Do(r => r.Initialize())
|
||||
?.Do(r => r.Execute());
|
||||
|
||||
// 组合使用
|
||||
obj.As<MyRule>()
|
||||
?.Do(rule =>
|
||||
{
|
||||
if (rule.Enabled)
|
||||
rule.Execute();
|
||||
});
|
||||
```
|
||||
|
||||
#### SwitchType 扩展方法
|
||||
|
||||
```csharp
|
||||
public static void SwitchType(
|
||||
this object obj,
|
||||
params (Type type, Action<object> action)[] handlers
|
||||
)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
obj.SwitchType(
|
||||
(typeof(IRule), o => HandleRule((IRule)o)),
|
||||
(typeof(ISystem), o => HandleSystem((ISystem)o)),
|
||||
(typeof(IModel), o => HandleModel((IModel)o))
|
||||
);
|
||||
```
|
||||
|
||||
### 3. OrEvent 扩展 ([`OrEventExtensions.cs`](OrEventExtensions.cs))
|
||||
|
||||
为 [`IEvent`](../../GFramework.Core.Abstractions/events/IEvent.cs) 提供事件组合功能。
|
||||
|
||||
#### OrEventExtensions
|
||||
|
||||
```csharp
|
||||
public static OrEvent Or(this IEasyEvent self, IEasyEvent e)
|
||||
public static OrEvent Or(this IEvent self, IEvent e)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
@ -244,41 +346,7 @@ var onAnyDamage = onPhysicalDamage
|
||||
.Or(onPoisonDamage);
|
||||
```
|
||||
|
||||
### 7. UnRegister 扩展 ([`UnRegisterExtension.cs`](UnRegisterExtension.cs))
|
||||
|
||||
为 [`IUnRegister`](../events/IUnRegister.cs) 提供 Godot 生命周期绑定。
|
||||
|
||||
#### UnRegisterExtension
|
||||
|
||||
```csharp
|
||||
public static IUnRegister UnRegisterWhenNodeExitTree(this IUnRegister unRegister,
|
||||
Node node)
|
||||
```
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
#if GODOT
|
||||
public class PlayerController : Node, IController
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
// 当节点退出场景树时自动注销
|
||||
this.RegisterEvent<GameEvent>(OnGameEvent)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
|
||||
this.GetModel<PlayerModel>()
|
||||
.Health
|
||||
.Register(OnHealthChanged)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
|
||||
// 不需要手动在 _ExitTree 中注销
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### 8. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs))
|
||||
### 4. UnRegisterList 扩展 ([`UnRegisterListExtension.cs`](UnRegisterListExtension.cs))
|
||||
|
||||
为 [`IUnRegister`](../events/IUnRegister.cs) 和 [`IUnRegisterList`](../events/IUnRegisterList.cs) 提供批量管理功能。
|
||||
|
||||
@ -296,11 +364,11 @@ public static void UnRegisterAll(this IUnRegisterList self)
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
public class ComplexController : Node, IController
|
||||
public class ComplexController : IController
|
||||
{
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public override void _Ready()
|
||||
public void Initialize()
|
||||
{
|
||||
// 所有注册都添加到列表中
|
||||
this.RegisterEvent<Event1>(OnEvent1)
|
||||
@ -316,7 +384,7 @@ public class ComplexController : Node, IController
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
public void Cleanup()
|
||||
{
|
||||
// 一次性注销所有
|
||||
_unregisterList.UnRegisterAll();
|
||||
@ -329,13 +397,11 @@ public class ComplexController : Node, IController
|
||||
### Controller 示例
|
||||
|
||||
```csharp
|
||||
public partial class GameplayController : Node, IController
|
||||
public partial class GameplayController : IController
|
||||
{
|
||||
private IUnRegisterList _unregisterList = new UnRegisterList();
|
||||
|
||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||
|
||||
public override void _Ready()
|
||||
public void Initialize()
|
||||
{
|
||||
// 使用扩展方法获取 Model
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
@ -348,34 +414,24 @@ public partial class GameplayController : Node, IController
|
||||
// 监听可绑定属性
|
||||
playerModel.Health.Register(OnHealthChanged)
|
||||
.AddToUnregisterList(_unregisterList);
|
||||
|
||||
// 或者使用 Godot 特定的自动注销
|
||||
gameModel.Score.Register(OnScoreChanged)
|
||||
.UnRegisterWhenNodeExitTree(this);
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
public void Process(double delta)
|
||||
{
|
||||
if (Input.IsActionJustPressed("attack"))
|
||||
{
|
||||
// 使用扩展方法发送命令
|
||||
this.SendCommand(new AttackCommand(targetId: 1));
|
||||
}
|
||||
// 发送命令
|
||||
this.SendCommand(new AttackCommand(targetId: 1));
|
||||
|
||||
if (Input.IsActionJustPressed("use_item"))
|
||||
// 发送查询
|
||||
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
|
||||
if (hasPotion)
|
||||
{
|
||||
// 使用扩展方法发送查询
|
||||
var hasPotion = this.SendQuery(new HasItemQuery("health_potion"));
|
||||
if (hasPotion)
|
||||
{
|
||||
this.SendCommand<UseHealthPotionCommand>();
|
||||
}
|
||||
this.SendCommand<UseHealthPotionCommand>();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGameStarted(GameStartedEvent e)
|
||||
{
|
||||
GD.Print("Game started!");
|
||||
Console.WriteLine("Game started!");
|
||||
}
|
||||
|
||||
private void OnHealthChanged(int health)
|
||||
@ -383,12 +439,7 @@ public partial class GameplayController : Node, IController
|
||||
UpdateHealthBar(health);
|
||||
}
|
||||
|
||||
private void OnScoreChanged(int score)
|
||||
{
|
||||
UpdateScoreDisplay(score);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
public void Cleanup()
|
||||
{
|
||||
_unregisterList.UnRegisterAll();
|
||||
}
|
||||
@ -467,7 +518,8 @@ public class AchievementSystem : AbstractSystem
|
||||
|
||||
## 扩展方法的优势
|
||||
|
||||
1. **简洁的语法**:不需要显式调用 `GetArchitecture()`
|
||||
1. **简洁的语法**
|
||||
:不需要显式调用 [GetContext()](file:///d:/Project/Rider/GFramework/GFramework.Core.Abstractions/rule/IContextAware.cs#L13-L15)
|
||||
2. **类型安全**:编译时检查类型
|
||||
3. **可读性高**:代码意图更清晰
|
||||
4. **智能提示**:IDE 可以提供完整的自动补全
|
||||
@ -475,21 +527,17 @@ public class AchievementSystem : AbstractSystem
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **确保引用命名空间**:
|
||||
1. **确保引用命名空间:**
|
||||
```csharp
|
||||
using GFramework.framework.extensions;
|
||||
using GFramework.Core.extensions;
|
||||
```
|
||||
|
||||
2. **理解扩展方法本质**:
|
||||
2. **理解扩展方法本质:**
|
||||
- 扩展方法是静态方法的语法糖
|
||||
- 不会改变原始类型的结构
|
||||
- 仅在编译时解析
|
||||
|
||||
3. **Godot 特定功能**:
|
||||
- `UnRegisterWhenNodeExitTree` 仅在 Godot 环境下可用
|
||||
- 使用 `#if GODOT` 编译指令控制
|
||||
|
||||
4. **性能考虑**:
|
||||
3. **性能考虑:**
|
||||
- 扩展方法本身无性能开销
|
||||
- 实际调用的是底层方法
|
||||
|
||||
|
||||
@ -16,20 +16,24 @@ IoC 容器类,负责管理对象的注册和获取。
|
||||
- 注册实例到容器
|
||||
- 从容器中获取实例
|
||||
- 类型安全的依赖管理
|
||||
- 线程安全操作
|
||||
- 容器冻结保护
|
||||
|
||||
## 核心方法
|
||||
|
||||
### 1. Register<T>
|
||||
### 1. Register<T> 和 Register(Type, object)
|
||||
|
||||
注册一个实例到容器中。
|
||||
|
||||
```csharp
|
||||
public void Register<T>(T instance)
|
||||
public void Register(Type type, object instance)
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `instance`: 要注册的实例对象
|
||||
- `type`: 要注册的类型(重载方法)
|
||||
|
||||
**使用示例:**
|
||||
|
||||
@ -42,29 +46,104 @@ container.Register<IGameSystem>(new GameSystem());
|
||||
container.Register<IStorageUtility>(new StorageUtility());
|
||||
```
|
||||
|
||||
### 2. Get<T>
|
||||
### 2. RegisterSingleton<T>
|
||||
|
||||
注册单例实例到容器中。一个类型只允许一个实例。
|
||||
|
||||
```csharp
|
||||
public void RegisterSingleton<T>(T instance)
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `instance`: 要注册的单例实例
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
var container = new IocContainer();
|
||||
|
||||
// 注册单例
|
||||
container.RegisterSingleton<IPlayerModel>(new PlayerModel());
|
||||
```
|
||||
|
||||
### 3. RegisterPlurality
|
||||
|
||||
注册多个实例,将实例注册到其实现的所有接口和具体类型上。
|
||||
|
||||
```csharp
|
||||
public void RegisterPlurality(object instance)
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `instance`: 要注册的实例
|
||||
|
||||
### 4. RegisterSystem
|
||||
|
||||
注册系统实例,将其绑定到其所有实现的接口上。
|
||||
|
||||
```csharp
|
||||
public void RegisterSystem(ISystem system)
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `system`: 系统实例对象
|
||||
|
||||
### 5. Get<T> 和 GetAll<T>
|
||||
|
||||
从容器中获取指定类型的实例。
|
||||
|
||||
```csharp
|
||||
public T Get<T>() where T : class
|
||||
public T? Get<T>() where T : class
|
||||
public IReadOnlyList<T> GetAll<T>() where T : class
|
||||
```
|
||||
|
||||
**返回值:**
|
||||
|
||||
- 返回指定类型的实例,如果未找到则返回 `null`
|
||||
- `Get<T>`: 返回指定类型的实例,如果未找到则返回 `null`
|
||||
- `GetAll<T>`: 返回指定类型的所有实例列表,如果未找到则返回空数组
|
||||
|
||||
**使用示例:**
|
||||
|
||||
```csharp
|
||||
// 获取已注册的实例
|
||||
var playerModel = container.Get<IPlayerModel>();
|
||||
var gameSystem = container.Get<IGameSystem>();
|
||||
var gameSystems = container.GetAll<IGameSystem>();
|
||||
|
||||
// 如果类型未注册,返回 null
|
||||
// 如果类型未注册,Get 返回 null,GetAll 返回空数组
|
||||
var unknownService = container.Get<IUnknownService>(); // null
|
||||
```
|
||||
|
||||
### 6. GetRequired<T>
|
||||
|
||||
获取指定类型的必需实例,如果没有注册或注册了多个实例会抛出异常。
|
||||
|
||||
```csharp
|
||||
public T GetRequired<T>() where T : class
|
||||
```
|
||||
|
||||
**返回值:**
|
||||
|
||||
- 返回找到的唯一实例
|
||||
|
||||
### 7. GetAllSorted<T>
|
||||
|
||||
获取并排序(系统调度专用)。
|
||||
|
||||
```csharp
|
||||
public IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class
|
||||
```
|
||||
|
||||
**参数:**
|
||||
|
||||
- `comparison`: 比较器委托,定义排序规则
|
||||
|
||||
**返回值:**
|
||||
|
||||
- 按指定方式排序后的实例列表
|
||||
|
||||
## 在框架中的使用
|
||||
|
||||
### Architecture 中的应用
|
||||
@ -142,27 +221,66 @@ public class PlayerController : IController
|
||||
```csharp
|
||||
public class IocContainer
|
||||
{
|
||||
// 使用字典存储类型到实例的映射
|
||||
private readonly Dictionary<Type, object> _mInstances = new();
|
||||
// 使用字典存储类型到实例集合的映射
|
||||
private readonly Dictionary<Type, HashSet<object>> _typeIndex = new();
|
||||
private readonly HashSet<object> _objects = [];
|
||||
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
|
||||
private volatile bool _frozen = false;
|
||||
|
||||
public void Register<T>(T instance)
|
||||
{
|
||||
var key = typeof(T);
|
||||
_mInstances[key] = instance; // 注册或覆盖
|
||||
// 获取写锁以确保线程安全
|
||||
_lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
RegisterInternal(typeof(T), instance!);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public T Get<T>() where T : class
|
||||
public T? Get<T>() where T : class
|
||||
{
|
||||
var key = typeof(T);
|
||||
if (_mInstances.TryGetValue(key, out var retInstance))
|
||||
_lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return retInstance as T; // 类型转换
|
||||
if (_typeIndex.TryGetValue(typeof(T), out var set) && set.Count > 0)
|
||||
{
|
||||
var result = set.First() as T;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
finally
|
||||
{
|
||||
_lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterInternal(Type type, object instance)
|
||||
{
|
||||
if (_frozen) throw new InvalidOperationException("IocContainer is frozen");
|
||||
|
||||
_objects.Add(instance);
|
||||
|
||||
if (!_typeIndex.TryGetValue(type, out var set))
|
||||
{
|
||||
set = [];
|
||||
_typeIndex[type] = set;
|
||||
}
|
||||
|
||||
set.Add(instance);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 线程安全机制
|
||||
|
||||
容器使用 [ReaderWriterLockSlim](xref:System.Threading.ReaderWriterLockSlim) 来确保线程安全操作,允许多个线程同时读取,但在写入时阻止其他线程访问。
|
||||
|
||||
### 注册流程
|
||||
|
||||
```
|
||||
@ -172,7 +290,7 @@ RegisterSystem<T>(system)
|
||||
↓
|
||||
IocContainer.Register<T>(system)
|
||||
↓
|
||||
Dictionary[typeof(T)] = system
|
||||
加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet
|
||||
```
|
||||
|
||||
### 获取流程
|
||||
@ -186,9 +304,9 @@ Architecture.GetSystem<T>()
|
||||
↓
|
||||
IocContainer.Get<T>()
|
||||
↓
|
||||
Dictionary.TryGetValue(typeof(T))
|
||||
加读锁 -> Dictionary.TryGetValue(typeof(T)) 获取HashSet
|
||||
↓
|
||||
返回实例或 null
|
||||
返回HashSet中第一个实例或 null
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
@ -245,27 +363,28 @@ var dataService = container.Get<IDataService>();
|
||||
dataService.SaveData("game data");
|
||||
```
|
||||
|
||||
### 覆盖注册
|
||||
### 注册多个实现
|
||||
|
||||
```csharp
|
||||
var container = new IocContainer();
|
||||
|
||||
// 首次注册
|
||||
container.Register<IConfig>(new DefaultConfig());
|
||||
// 注册多个相同接口的不同实现
|
||||
container.Register<IDataService>(new LocalDataService());
|
||||
container.Register<IDataService>(new CloudDataService());
|
||||
|
||||
// 后续注册会覆盖
|
||||
container.Register<IConfig>(new CustomConfig());
|
||||
// 获取单个实例(返回第一个)
|
||||
var singleService = container.Get<IDataService>(); // 返回第一个注册的实例
|
||||
|
||||
// 获取到的是最后注册的实例
|
||||
var config = container.Get<IConfig>(); // CustomConfig
|
||||
// 获取所有实例
|
||||
var allServices = container.GetAll<IDataService>(); // 返回两个实例的列表
|
||||
```
|
||||
|
||||
## 设计特点
|
||||
|
||||
### 1. 简单轻量
|
||||
|
||||
- 只有两个核心方法:`Register` 和 `Get`
|
||||
- 基于字典实现,性能高效
|
||||
- 支持多种注册方式:普通注册、单例注册、多实例注册
|
||||
- 基于字典和哈希集实现,性能高效
|
||||
- 无复杂的依赖解析逻辑
|
||||
|
||||
### 2. 手动注册
|
||||
@ -274,11 +393,11 @@ var config = container.Get<IConfig>(); // CustomConfig
|
||||
- 不支持自动依赖注入
|
||||
- 完全可控的组件生命周期
|
||||
|
||||
### 3. 单例模式
|
||||
### 3. 多实例支持
|
||||
|
||||
- 每个类型只能注册一个实例
|
||||
- 适合管理全局单例服务
|
||||
- 后续注册会覆盖前面的实例
|
||||
- 每个类型可以注册多个实例
|
||||
- 提供 [GetAll](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L244-L261) 方法获取所有实例
|
||||
- 提供 [Get](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L208-L225) 方法获取单个实例
|
||||
|
||||
### 4. 类型安全
|
||||
|
||||
@ -286,6 +405,17 @@ var config = container.Get<IConfig>(); // CustomConfig
|
||||
- 避免字符串键导致的错误
|
||||
- IDE 友好,支持自动补全
|
||||
|
||||
### 5. 线程安全
|
||||
|
||||
- 使用读写锁确保多线程环境下的安全操作
|
||||
- 读操作可以并发执行
|
||||
- 写操作独占锁,防止并发修改冲突
|
||||
|
||||
### 6. 容器冻结
|
||||
|
||||
- 提供 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372) 方法,防止进一步修改容器内容
|
||||
- 防止在初始化后意外修改注册内容
|
||||
|
||||
## 与其他 IoC 容器的区别
|
||||
|
||||
### 本框架的 IocContainer
|
||||
@ -301,6 +431,8 @@ var service = container.Get<MyService>();
|
||||
|
||||
- ✅ 简单易用
|
||||
- ✅ 性能高
|
||||
- ✅ 线程安全
|
||||
- ✅ 支持多实例
|
||||
- ❌ 不支持构造函数注入
|
||||
- ❌ 不支持自动解析依赖
|
||||
- ❌ 不支持生命周期管理(Transient/Scoped/Singleton)
|
||||
@ -394,56 +526,37 @@ else
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **类型唯一性**
|
||||
- 每个类型只能注册一个实例
|
||||
- 重复注册会覆盖之前的实例
|
||||
|
||||
2. **手动管理依赖顺序**
|
||||
- 组件的依赖关系需要手动保证
|
||||
- 先注册被依赖的组件
|
||||
|
||||
3. **无生命周期管理**
|
||||
- 实例一旦注册就一直存在
|
||||
- 需要手动管理实例的生命周期
|
||||
|
||||
4. **线程安全**
|
||||
- 当前实现非线程安全
|
||||
- 避免多线程同时访问
|
||||
|
||||
## 扩展可能性
|
||||
|
||||
如果需要更复杂的功能,可以扩展 `IocContainer`:
|
||||
### 5. 合理使用容器冻结
|
||||
|
||||
```csharp
|
||||
// 支持工厂模式
|
||||
public class AdvancedIocContainer : IocContainer
|
||||
// 在架构初始化完成后冻结容器,防止意外修改
|
||||
protected override void OnInit()
|
||||
{
|
||||
private Dictionary<Type, Func<object>> _factories = new();
|
||||
// 注册所有组件
|
||||
RegisterModel(new PlayerModel());
|
||||
RegisterSystem(new GameSystem());
|
||||
// ...
|
||||
|
||||
public void RegisterFactory<T>(Func<T> factory) where T : class
|
||||
{
|
||||
_factories[typeof(T)] = () => factory();
|
||||
}
|
||||
|
||||
public new T Get<T>() where T : class
|
||||
{
|
||||
// 先尝试获取实例
|
||||
var instance = base.Get<T>();
|
||||
if (instance != null) return instance;
|
||||
|
||||
// 如果没有实例,尝试使用工厂创建
|
||||
if (_factories.TryGetValue(typeof(T), out var factory))
|
||||
{
|
||||
return factory() as T;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// 冻结容器
|
||||
Container.Freeze(); // 此后无法再注册新组件
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **线程安全操作**:容器内部使用读写锁确保线程安全,无需额外同步
|
||||
|
||||
2. **容器冻结**:一旦调用 [Freeze](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L359-L372)
|
||||
方法,将不能再注册新实例
|
||||
|
||||
3. **单例注册限制
|
||||
**:[RegisterSingleton](file:///d:/Project/Rider/GFramework/GFramework.Core/ioc/IocContainer.cs#L84-L106)
|
||||
方法确保一个类型只能有一个实例,重复注册会抛出异常
|
||||
|
||||
4. **内存管理**:容器持有的实例不会自动释放,需要注意内存泄漏问题
|
||||
|
||||
5. **注册顺序**:组件的依赖关系需要手动保证,先注册被依赖的组件
|
||||
|
||||
## 相关包
|
||||
|
||||
- [`architecture`](../architecture/README.md) - 使用 IoC 容器管理所有组件
|
||||
|
||||
@ -6,7 +6,7 @@ Logging 包提供了灵活的日志系统,支持多级别日志记录。默认
|
||||
|
||||
## 核心接口
|
||||
|
||||
### [`ILogger`](ILogger.cs)
|
||||
### [ILogger](ILogger.cs)
|
||||
|
||||
日志记录器接口,定义了日志记录的基本功能。
|
||||
|
||||
@ -62,7 +62,7 @@ void Fatal(string msg, Exception t);
|
||||
string Name();
|
||||
```
|
||||
|
||||
### [`ILoggerFactory`](ILoggerFactory.cs)
|
||||
### [ILoggerFactory](ILoggerFactory.cs)
|
||||
|
||||
日志工厂接口,用于创建日志记录器实例。
|
||||
|
||||
@ -72,7 +72,7 @@ string Name();
|
||||
ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info);
|
||||
```
|
||||
|
||||
### [`ILoggerFactoryProvider`](ILoggerFactory.cs)
|
||||
### [ILoggerFactoryProvider](ILoggerFactoryProvider.cs)
|
||||
|
||||
日志工厂提供程序接口,用于获取日志工厂。
|
||||
|
||||
@ -83,7 +83,7 @@ ILoggerFactory GetLoggerFactory();
|
||||
ILogger CreateLogger(string name);
|
||||
```
|
||||
|
||||
### [`LogLevel`](LogLevel.cs)
|
||||
### [LogLevel](LogLevel.cs)
|
||||
|
||||
日志级别枚举。
|
||||
|
||||
@ -101,7 +101,7 @@ public enum LogLevel
|
||||
|
||||
## 核心类
|
||||
|
||||
### [`AbstractLogger`](AbstractLogger.cs)
|
||||
### [AbstractLogger](AbstractLogger.cs)
|
||||
|
||||
抽象日志基类,封装了日志级别判断、格式化与异常处理逻辑。平台日志器只需实现 `Write` 方法即可。
|
||||
|
||||
@ -127,7 +127,7 @@ public class CustomLogger : AbstractLogger
|
||||
}
|
||||
```
|
||||
|
||||
### [`ConsoleLogger`](ConsoleLogger.cs)
|
||||
### [ConsoleLogger](ConsoleLogger.cs)
|
||||
|
||||
控制台日志记录器实现,支持彩色输出。
|
||||
|
||||
@ -162,7 +162,14 @@ logger.Fatal("致命错误");
|
||||
- **Error**: 红色
|
||||
- **Fatal**: 洋红色
|
||||
|
||||
### [`ConsoleLoggerFactory`](ConsoleLoggerFactory.cs)
|
||||
**构造函数参数:**
|
||||
|
||||
- `name`:日志器名称,默认为 "ROOT"
|
||||
- `minLevel`:最低日志级别,默认为 LogLevel.Info
|
||||
- `writer`:TextWriter 输出流,默认为 Console.Out
|
||||
- `useColors`:是否使用颜色,默认为 true(仅在输出到控制台时生效)
|
||||
|
||||
### [ConsoleLoggerFactory](ConsoleLoggerFactory.cs)
|
||||
|
||||
控制台日志工厂,用于创建控制台日志记录器实例。
|
||||
|
||||
@ -174,7 +181,7 @@ var logger = factory.GetLogger("MyModule", LogLevel.Debug);
|
||||
logger.Info("日志记录器创建成功");
|
||||
```
|
||||
|
||||
### [`ConsoleLoggerFactoryProvider`](ConsoleLoggerFactoryProvider.cs)
|
||||
### [ConsoleLoggerFactoryProvider](ConsoleLoggerFactoryProvider.cs)
|
||||
|
||||
控制台日志工厂提供程序实现。
|
||||
|
||||
@ -182,11 +189,12 @@ logger.Info("日志记录器创建成功");
|
||||
|
||||
```csharp
|
||||
var provider = new ConsoleLoggerFactoryProvider();
|
||||
var factory = provider.GetLoggerFactory();
|
||||
var logger = factory.GetLogger("MyApp", LogLevel.Info);
|
||||
provider.MinLevel = LogLevel.Debug; // 设置最低日志级别
|
||||
var logger = provider.CreateLogger("MyApp");
|
||||
logger.Info("应用程序启动");
|
||||
```
|
||||
|
||||
### [`LoggerFactoryResolver`](LoggerFactoryResolver.cs)
|
||||
### [LoggerFactoryResolver](LoggerFactoryResolver.cs)
|
||||
|
||||
日志工厂提供程序解析器,用于管理和提供日志工厂提供程序实例。
|
||||
|
||||
@ -270,9 +278,9 @@ public class DebugLogger : AbstractLogger
|
||||
// 只输出调试及更高级别的日志
|
||||
if (level >= LogLevel.Debug)
|
||||
{
|
||||
GD.Print($"[{level}] {message}");
|
||||
Console.WriteLine($"[{level}] {message}");
|
||||
if (exception != null)
|
||||
GD.Print(exception);
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,15 +349,16 @@ public class DebugLogger : AbstractLogger
|
||||
- 支持字符串格式化参数
|
||||
- 支持异常信息传递
|
||||
|
||||
3. **自定义比较器**:
|
||||
- 在 `BindableProperty` 中可以使用 `WithComparer` 设置自定义比较器
|
||||
3. **ConsoleLogger 的额外参数**:
|
||||
- ConsoleLogger 现在支持自定义TextWriter输出流
|
||||
- 支持禁用颜色输出的功能(useColors参数)
|
||||
|
||||
## 相关包
|
||||
|
||||
- [`architecture`](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
|
||||
- [`property`](../property/README.md) - 可绑定属性基于事件系统实现
|
||||
- [`extensions`](../extensions/README.md) - 提供便捷的扩展方法
|
||||
- [architecture](../architecture/README.md) - 架构核心,使用日志系统记录生命周期事件
|
||||
- [property](../property/README.md) - 可绑定属性基于事件系统实现
|
||||
- [extensions](../extensions/README.md) - 提供便捷的扩展方法
|
||||
|
||||
---
|
||||
|
||||
**许可证**: Apache 2.0
|
||||
**许可证**: Apache 2.0
|
||||
@ -13,14 +13,14 @@ Model 包定义了数据模型层的接口和基类。Model 是 MVC 架构中的
|
||||
|
||||
**继承的能力接口:**
|
||||
|
||||
- [`ICanSetArchitecture`](../rule/ICanSetArchitecture.cs) - 可设置架构引用
|
||||
- [`ICanGetUtility`](../utility/ICanGetUtility.cs) - 可获取工具类
|
||||
- [`ICanSendEvent`](../events/ICanSendEvent.cs) - 可发送事件
|
||||
- [`IContextAware`](../rule/IContextAware.cs) - 上下文感知接口
|
||||
- [`ILogAware`](../rule/ILogAware.cs) - 日志感知接口
|
||||
|
||||
**核心方法:**
|
||||
|
||||
```csharp
|
||||
void Init(); // 初始化模型
|
||||
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
|
||||
```
|
||||
|
||||
### [`ICanGetModel`](ICanGetModel.cs)
|
||||
@ -31,7 +31,8 @@ void Init(); // 初始化模型
|
||||
|
||||
### [`AbstractModel`](AbstractModel.cs)
|
||||
|
||||
抽象模型基类,提供模型的基础实现。
|
||||
抽象模型基类,实现IModel接口,提供模型的基础实现。该类继承自[
|
||||
`ContextAwareBase`](file:///d:/Project/Rider/GFramework/GFramework.Core/rule/ContextAwareBase.cs#L11-L37),提供了上下文感知能力。
|
||||
|
||||
**使用示例:**
|
||||
|
||||
@ -55,6 +56,20 @@ public class PlayerModel : AbstractModel
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnArchitecturePhase(ArchitecturePhase phase)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case ArchitecturePhase.Initializing:
|
||||
// 架构初始化阶段的处理
|
||||
break;
|
||||
case ArchitecturePhase.Ready:
|
||||
// 架构就绪阶段的处理
|
||||
break;
|
||||
// ... 其他阶段处理
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -66,6 +81,7 @@ public class PlayerModel : AbstractModel
|
||||
2. **提供数据访问接口**
|
||||
3. **监听自身属性变化并做出响应**
|
||||
4. **发送数据变化事件**
|
||||
5. **处理架构生命周期事件**
|
||||
|
||||
### ❌ 不应该做的事
|
||||
|
||||
@ -99,6 +115,19 @@ public class PlayerModel : AbstractModel
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnArchitecturePhase(ArchitecturePhase phase)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case ArchitecturePhase.Ready:
|
||||
// 模型准备好后的处理
|
||||
_log?.Log("PlayerModel is ready.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -123,6 +152,18 @@ public class GameModel : AbstractModel
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnArchitecturePhase(ArchitecturePhase phase)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case ArchitecturePhase.ShuttingDown:
|
||||
// 游戏模型清理工作
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -7,17 +7,7 @@ Query 包实现了 CQRS(命令查询职责分离)模式中的查询部分。
|
||||
|
||||
## 核心接口
|
||||
|
||||
### [`ICanSendQuery`](ICanSendQuery.cs)
|
||||
|
||||
标记接口,表示该类型可以发送查询。
|
||||
|
||||
**继承关系:**
|
||||
|
||||
```csharp
|
||||
public interface ICanSendQuery : IBelongToArchitecture
|
||||
```
|
||||
|
||||
### [`IQuery<TResult>`](IQuery.cs)
|
||||
### IQuery<TResult>
|
||||
|
||||
查询接口,定义了查询的基本契约。
|
||||
|
||||
@ -27,26 +17,50 @@ public interface ICanSendQuery : IBelongToArchitecture
|
||||
TResult Do(); // 执行查询并返回结果
|
||||
```
|
||||
|
||||
**继承的能力:**
|
||||
|
||||
- `ICanSetArchitecture` - 可设置架构
|
||||
- `ICanGetModel` - 可获取 Model
|
||||
- `ICanGetSystem` - 可获取 System
|
||||
- `ICanSendQuery` - 可发送其他 Query
|
||||
|
||||
## 核心类
|
||||
|
||||
### [`AbstractQuery<T>`](AbstractQuery.cs)
|
||||
### [`AbstractQuery<TInput, TResult>`](AbstractQuery.cs)
|
||||
|
||||
抽象查询基类,提供了查询的基础实现。
|
||||
抽象查询基类,提供了查询的基础实现。它接受一个泛型输入参数 TInput,该参数必须实现 IQueryInput 接口。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```csharp
|
||||
public abstract class AbstractQuery<T> : IQuery<T>
|
||||
public abstract class AbstractQuery<TInput, TResult>(TInput input) : ContextAwareBase, IQuery<TResult>
|
||||
where TInput : IQueryInput
|
||||
{
|
||||
public T Do() => OnDo();
|
||||
protected abstract T OnDo(); // 子类实现查询逻辑
|
||||
public TResult Do() => OnDo(input); // 执行查询,传入输入参数
|
||||
protected abstract TResult OnDo(TInput input); // 子类实现查询逻辑
|
||||
}
|
||||
```
|
||||
|
||||
### [`EmptyQueryInput`](EmptyQueryInput.cs)
|
||||
|
||||
空查询输入类,用于表示不需要任何输入参数的查询操作。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```csharp
|
||||
public sealed class EmptyQueryInput : IQueryInput
|
||||
{
|
||||
// 作为占位符使用,适用于那些不需要额外输入参数的查询场景
|
||||
}
|
||||
```
|
||||
|
||||
### [`QueryBus`](QueryBus.cs)
|
||||
|
||||
查询总线实现,负责执行查询并返回结果。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```csharp
|
||||
public sealed class QueryBus : IQueryBus
|
||||
{
|
||||
public TResult Send<TResult>(IQuery<TResult> query)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
return query.Do();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -54,41 +68,58 @@ public abstract class AbstractQuery<T> : IQuery<T>
|
||||
|
||||
### 1. 定义查询
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 定义查询输入参数
|
||||
public record GetPlayerGoldQueryInput : IQueryInput;
|
||||
|
||||
// 查询玩家金币数量
|
||||
public class GetPlayerGoldQuery : AbstractQuery<int>
|
||||
public class GetPlayerGoldQuery : AbstractQuery<GetPlayerGoldQueryInput, int>
|
||||
{
|
||||
protected override int OnDo()
|
||||
public GetPlayerGoldQuery() : base(new EmptyQueryInput())
|
||||
{
|
||||
}
|
||||
|
||||
protected override int OnDo(GetPlayerGoldQueryInput input)
|
||||
{
|
||||
return this.GetModel<PlayerModel>().Gold.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询玩家是否死亡
|
||||
public class IsPlayerDeadQuery : AbstractQuery<bool>
|
||||
public record IsPlayerDeadQueryInput : IQueryInput;
|
||||
|
||||
public class IsPlayerDeadQuery : AbstractQuery<IsPlayerDeadQueryInput, bool>
|
||||
{
|
||||
protected override bool OnDo()
|
||||
public IsPlayerDeadQuery() : base(new EmptyQueryInput())
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool OnDo(IsPlayerDeadQueryInput input)
|
||||
{
|
||||
return this.GetModel<PlayerModel>().Health.Value <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询背包中指定物品的数量
|
||||
public class GetItemCountQuery : AbstractQuery<int>
|
||||
public record GetItemCountQueryInput(string ItemId) : IQueryInput;
|
||||
|
||||
public class GetItemCountQuery : AbstractQuery<GetItemCountQueryInput, int>
|
||||
{
|
||||
public string ItemId { get; set; }
|
||||
|
||||
protected override int OnDo()
|
||||
public GetItemCountQuery(string itemId) : base(new GetItemCountQueryInput(itemId))
|
||||
{
|
||||
}
|
||||
|
||||
protected override int OnDo(GetItemCountQueryInput input)
|
||||
{
|
||||
var inventory = this.GetModel<InventoryModel>();
|
||||
return inventory.GetItemCount(ItemId);
|
||||
return inventory.GetItemCount(input.ItemId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 发送查询(在 Controller 中)
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
public partial class ShopUI : Control, IController
|
||||
{
|
||||
[Export] private Button _buyButton;
|
||||
@ -121,7 +152,7 @@ public partial class ShopUI : Control, IController
|
||||
|
||||
### 3. 在 System 中使用
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
public class CombatSystem : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
@ -148,7 +179,7 @@ public class CombatSystem : AbstractSystem
|
||||
|
||||
### 1. 带参数的复杂查询
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 查询指定范围内的敌人列表
|
||||
public class GetEnemiesInRangeQuery : AbstractQuery<List<Enemy>>
|
||||
{
|
||||
@ -172,7 +203,7 @@ var enemies = this.SendQuery(new GetEnemiesInRangeQuery
|
||||
|
||||
### 2. 组合查询
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 查询玩家是否可以使用技能
|
||||
public class CanUseSkillQuery : AbstractQuery<bool>
|
||||
{
|
||||
@ -215,7 +246,7 @@ public class IsSkillOnCooldownQuery : AbstractQuery<bool>
|
||||
|
||||
### 3. 聚合数据查询
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 查询玩家战斗力
|
||||
public class GetPlayerPowerQuery : AbstractQuery<int>
|
||||
{
|
||||
@ -254,7 +285,7 @@ public class GetPlayerInfoQuery : AbstractQuery<PlayerInfo>
|
||||
|
||||
### 4. 跨 System 查询
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 在 AI System 中查询玩家状态
|
||||
public class EnemyAISystem : AbstractSystem
|
||||
{
|
||||
@ -303,7 +334,7 @@ public class EnemyAISystem : AbstractSystem
|
||||
- **返回值**:有返回值
|
||||
- **示例**:获取金币数量、检查技能冷却、查询玩家位置
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// ❌ 错误:在 Query 中修改状态
|
||||
public class BadQuery : AbstractQuery<int>
|
||||
{
|
||||
@ -349,7 +380,7 @@ public class AddGoldCommand : AbstractCommand
|
||||
|
||||
### 1. 缓存查询结果
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 在 Model 中缓存复杂计算
|
||||
public class PlayerModel : AbstractModel
|
||||
{
|
||||
@ -379,7 +410,7 @@ public class PlayerModel : AbstractModel
|
||||
|
||||
### 2. 批量查询
|
||||
|
||||
```csharp
|
||||
``csharp
|
||||
// 一次查询多个数据,而不是多次单独查询
|
||||
public class GetMultipleItemCountsQuery : AbstractQuery<Dictionary<string, int>>
|
||||
{
|
||||
|
||||
@ -6,85 +6,84 @@ Rule 包定义了框架的核心规则接口,这些接口规定了框架各个
|
||||
|
||||
## 核心接口
|
||||
|
||||
### [`IBelongToArchitecture`](IBelongToArchitecture.cs)
|
||||
### IContextAware
|
||||
|
||||
标记接口,表示某个对象属于特定的架构体系。
|
||||
标记接口,表示该类型可以感知架构上下文。
|
||||
|
||||
**接口定义:**
|
||||
|
||||
```csharp
|
||||
public interface IBelongToArchitecture
|
||||
public interface IContextAware
|
||||
{
|
||||
IArchitecture GetArchitecture();
|
||||
void SetContext(IArchitectureContext context);
|
||||
IArchitectureContext GetContext();
|
||||
}
|
||||
```
|
||||
|
||||
**实现此接口的类型:**
|
||||
|
||||
- Controller
|
||||
- System
|
||||
- Query
|
||||
- Model
|
||||
- Command
|
||||
- Query
|
||||
- Event 处理器
|
||||
- 以及其他需要感知架构上下文的组件
|
||||
|
||||
**作用:**
|
||||
所有实现此接口的类型都能够获取其所属的架构实例,从而访问架构提供的各种能力。
|
||||
所有实现此接口的类型都能够获取其所属的架构上下文实例,从而访问架构提供的各种能力。
|
||||
|
||||
### [`ICanSetArchitecture`](ICanSetArchitecture.cs)
|
||||
## 核心类
|
||||
|
||||
定义可以设置架构实例的能力。
|
||||
### [`ContextAwareBase`](ContextAwareBase.cs)
|
||||
|
||||
**接口定义:**
|
||||
上下文感知基类,实现了 IContextAware 接口,为需要感知架构上下文的类提供基础实现。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```csharp
|
||||
public interface ICanSetArchitecture
|
||||
public abstract class ContextAwareBase : IContextAware
|
||||
{
|
||||
void SetArchitecture(IArchitecture architecture);
|
||||
protected IArchitectureContext? Context { get; set; }
|
||||
|
||||
void IContextAware.SetContext(IArchitectureContext context)
|
||||
{
|
||||
Context = context;
|
||||
OnContextReady(); // 上下文准备好后调用此方法
|
||||
}
|
||||
|
||||
IArchitectureContext IContextAware.GetContext()
|
||||
{
|
||||
Context ??= GameContext.GetFirstArchitectureContext();
|
||||
return Context;
|
||||
}
|
||||
|
||||
protected virtual void OnContextReady() // 子类可以重写此方法进行初始化
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**实现此接口的类型:**
|
||||
|
||||
- Command
|
||||
- Query
|
||||
|
||||
**作用:**
|
||||
在 Command 和 Query 执行前,框架会自动调用 `SetArchitecture` 方法注入架构实例,使其能够访问 Model、System 等组件。
|
||||
|
||||
## 接口关系图
|
||||
|
||||
```
|
||||
IBelongToArchitecture (属于架构)
|
||||
↓ 被继承
|
||||
├── ICanGetModel (可获取 Model)
|
||||
├── ICanGetSystem (可获取 System)
|
||||
├── ICanGetUtility (可获取 Utility)
|
||||
├── ICanSendCommand (可发送 Command)
|
||||
├── ICanSendEvent (可发送 Event)
|
||||
├── ICanSendQuery (可发送 Query)
|
||||
└── ICanRegisterEvent (可注册 Event)
|
||||
|
||||
ICanSetArchitecture (可设置架构)
|
||||
↓ 被继承
|
||||
├── ICommand (命令接口)
|
||||
└── IQuery<T> (查询接口)
|
||||
IContextAware (上下文感知接口)
|
||||
↓ 被继承于
|
||||
├── AbstractSystem (抽象系统基类)
|
||||
├── AbstractQuery<TInput, TResult> (抽象查询基类)
|
||||
├── AbstractModel (抽象模型基类)
|
||||
└── AbstractCommand (抽象命令基类)
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 1. Controller 实现 IBelongToArchitecture
|
||||
### 1. Component 继承 ContextAwareBase
|
||||
|
||||
```csharp
|
||||
// Controller 通过实现 IBelongToArchitecture 获得架构访问能力
|
||||
// 组件通过继承 ContextAwareBase 获得架构上下文访问能力
|
||||
public partial class PlayerController : Node, IController
|
||||
{
|
||||
// 实现 IBelongToArchitecture 接口
|
||||
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
|
||||
|
||||
// 不再需要手动实现 IContextAware,基类已处理
|
||||
// 可以直接使用扩展方法
|
||||
public override void _Ready()
|
||||
{
|
||||
// 因为实现了 IBelongToArchitecture,所以可以使用扩展方法
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
this.SendCommand(new InitPlayerCommand());
|
||||
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
|
||||
@ -97,17 +96,17 @@ public partial class PlayerController : Node, IController
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Command 实现 ICanSetArchitecture
|
||||
### 2. Command 继承 AbstractCommand (IContextAware)
|
||||
|
||||
```csharp
|
||||
// Command 实现 ICanSetArchitecture,框架会自动注入架构
|
||||
// Command 继承 AbstractCommand,自动成为 IContextAware
|
||||
public class BuyItemCommand : AbstractCommand
|
||||
{
|
||||
public string ItemId { get; set; }
|
||||
|
||||
protected override void OnExecute()
|
||||
{
|
||||
// 框架已经通过 SetArchitecture 注入了架构实例
|
||||
// 框架或上下文系统会自动注入 IArchitectureContext
|
||||
// 所以这里可以直接使用 this.GetModel
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
var shopModel = this.GetModel<ShopModel>();
|
||||
@ -125,21 +124,15 @@ public class BuyItemCommand : AbstractCommand
|
||||
### 3. 自定义组件遵循规则
|
||||
|
||||
```csharp
|
||||
// 自定义管理器遵循框架规则
|
||||
public class SaveManager : IBelongToArchitecture
|
||||
// 自定义管理器遵循框架规则,继承 ContextAwareBase
|
||||
public class SaveManager : ContextAwareBase
|
||||
{
|
||||
private IArchitecture _architecture;
|
||||
|
||||
public SaveManager(IArchitecture architecture)
|
||||
{
|
||||
_architecture = architecture;
|
||||
}
|
||||
|
||||
public IArchitecture GetArchitecture() => _architecture;
|
||||
// 不再需要手动构造函数传参,上下文由框架注入
|
||||
// protected override void OnContextReady() 可用于初始化
|
||||
|
||||
public void SaveGame()
|
||||
{
|
||||
// 因为实现了 IBelongToArchitecture,可以使用扩展方法
|
||||
// 因为继承了 ContextAwareBase,可以使用扩展方法
|
||||
var playerModel = this.GetModel<PlayerModel>();
|
||||
var saveData = new SaveData
|
||||
{
|
||||
@ -219,9 +212,10 @@ Rule 接口体现了依赖注入(DI)的思想:
|
||||
|
||||
```csharp
|
||||
// 接口定义了"需要什么"
|
||||
public interface IBelongToArchitecture
|
||||
public interface IContextAware
|
||||
{
|
||||
IArchitecture GetArchitecture();
|
||||
void SetContext(IArchitectureContext context);
|
||||
IArchitectureContext GetContext();
|
||||
}
|
||||
|
||||
// 框架负责"提供什么"
|
||||
@ -230,8 +224,8 @@ public static class CanSendExtensions
|
||||
public static void SendCommand<T>(this ICanSendCommand self, T command)
|
||||
where T : ICommand
|
||||
{
|
||||
// 自动注入架构依赖
|
||||
command.SetArchitecture(self.GetArchitecture());
|
||||
// 自动注入架构上下文依赖
|
||||
command.SetContext(self.GetContext());
|
||||
command.Execute();
|
||||
}
|
||||
}
|
||||
@ -245,8 +239,8 @@ Rule 接口遵循接口隔离原则,每个接口职责单一:
|
||||
// ❌ 不好的设计:一个大接口包含所有能力
|
||||
public interface IBigInterface
|
||||
{
|
||||
IArchitecture GetArchitecture();
|
||||
void SetArchitecture(IArchitecture architecture);
|
||||
void SetContext(IArchitectureContext context);
|
||||
IArchitectureContext GetContext();
|
||||
T GetModel<T>() where T : class, IModel;
|
||||
T GetSystem<T>() where T : class, ISystem;
|
||||
void SendCommand<T>(T command) where T : ICommand;
|
||||
@ -254,8 +248,7 @@ public interface IBigInterface
|
||||
}
|
||||
|
||||
// ✅ 好的设计:小接口组合
|
||||
public interface IBelongToArchitecture { ... } // 只负责获取架构
|
||||
public interface ICanSetArchitecture { ... } // 只负责设置架构
|
||||
public interface IContextAware { ... } // 只负责上下文的设置与获取
|
||||
public interface ICanGetModel { ... } // 只负责获取 Model
|
||||
public interface ICanSendCommand { ... } // 只负责发送 Command
|
||||
```
|
||||
@ -271,14 +264,14 @@ public interface IController : ICanGetModel, ICanGetSystem, ICanSendCommand,
|
||||
{
|
||||
}
|
||||
|
||||
// Command 需要设置架构和获取 Model/System
|
||||
public interface ICommand : ICanSetArchitecture, ICanGetModel, ICanGetSystem,
|
||||
// Command 需要上下文感知和获取 Model/System
|
||||
public interface ICommand : IContextAware, ICanGetModel, ICanGetSystem,
|
||||
ICanSendEvent, ICanSendQuery
|
||||
{
|
||||
}
|
||||
|
||||
// System 只需要获取其他组件
|
||||
public interface ISystem : ICanGetModel, ICanGetUtility, ICanGetSystem,
|
||||
public interface ISystem : IContextAware, ICanGetModel, ICanGetUtility, ICanGetSystem,
|
||||
ICanRegisterEvent, ICanSendEvent, ICanSendQuery
|
||||
{
|
||||
}
|
||||
@ -324,10 +317,10 @@ public class DatabaseCommand : AbstractCommand, ICanAccessDatabase
|
||||
|
||||
## 相关包
|
||||
|
||||
- [`architecture`](../architecture/README.md) - 定义 IArchitecture 接口
|
||||
- [`command`](../command/README.md) - Command 实现 ICanSetArchitecture
|
||||
- [`query`](../query/README.md) - Query 实现 ICanSetArchitecture
|
||||
- [`controller`](../controller/README.md) - Controller 实现 IBelongToArchitecture
|
||||
- [`system`](../system/README.md) - System 实现 IBelongToArchitecture
|
||||
- [`model`](../model/README.md) - Model 实现 IBelongToArchitecture
|
||||
- [`architecture`](../architecture/README.md) - 定义 IArchitectureContext 接口
|
||||
- [`command`](../command/README.md) - Command 继承 AbstractCommand (IContextAware)
|
||||
- [`query`](../query/README.md) - Query 继承 AbstractQuery (IContextAware)
|
||||
- [`controller`](../controller/README.md) - Controller 实现 ICanSendCommand 等接口
|
||||
- [`system`](../system/README.md) - System 继承 AbstractSystem (IContextAware)
|
||||
- [`model`](../model/README.md) - Model 继承 AbstractModel (IContextAware)
|
||||
- [`extensions`](../extensions/README.md) - 基于规则接口提供扩展方法
|
||||
@ -24,6 +24,8 @@ System 接口,定义了系统的基本行为。
|
||||
|
||||
```csharp
|
||||
void Init(); // 系统初始化方法
|
||||
void Destroy(); // 系统销毁方法
|
||||
void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
|
||||
```
|
||||
|
||||
**继承的能力:**
|
||||
@ -39,15 +41,18 @@ void Init(); // 系统初始化方法
|
||||
|
||||
### [`AbstractSystem`](AbstractSystem.cs)
|
||||
|
||||
抽象 System 基类,提供了 System 的基础实现。
|
||||
抽象 System 基类,提供了 System 的基础实现。继承自 ContextAwareBase,具有上下文感知能力。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```csharp
|
||||
public abstract class AbstractSystem : ISystem
|
||||
public abstract class AbstractSystem : ContextAwareBase, ISystem
|
||||
{
|
||||
void ISystem.Init() => OnInit();
|
||||
void ISystem.Init() => OnInit(); // 系统初始化,内部调用抽象方法 OnInit()
|
||||
void ISystem.Destroy() => OnDestroy(); // 系统销毁,内部调用 OnDestroy()
|
||||
protected abstract void OnInit(); // 子类实现初始化逻辑
|
||||
protected virtual void OnDestroy(); // 子类可选择重写销毁逻辑
|
||||
public virtual void OnArchitecturePhase(ArchitecturePhase phase); // 处理架构阶段事件
|
||||
}
|
||||
```
|
||||
|
||||
@ -99,6 +104,12 @@ public class CombatSystem : AbstractSystem
|
||||
{
|
||||
return Math.Max(1, attackPower - defense / 2);
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
// 清理资源
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -7,17 +7,7 @@ Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如
|
||||
|
||||
## 核心接口
|
||||
|
||||
### [`ICanGetUtility`](ICanGetUtility.cs)
|
||||
|
||||
标记接口,表示该类型可以获取 Utility。
|
||||
|
||||
**继承关系:**
|
||||
|
||||
```csharp
|
||||
public interface ICanGetUtility : IBelongToArchitecture
|
||||
```
|
||||
|
||||
### [`IUtility`](IUtility.cs)
|
||||
### IUtility
|
||||
|
||||
Utility 标记接口,所有工具类都应实现此接口。
|
||||
|
||||
@ -30,64 +20,98 @@ public interface IUtility
|
||||
}
|
||||
```
|
||||
|
||||
### IContextUtility
|
||||
|
||||
上下文工具接口,扩展了IUtility接口,为需要感知架构上下文的工具类提供基础能力。
|
||||
|
||||
**接口定义:**
|
||||
|
||||
```csharp
|
||||
public interface IContextUtility : IUtility
|
||||
{
|
||||
void Init(); // 初始化上下文工具
|
||||
}
|
||||
```
|
||||
|
||||
## 核心类
|
||||
|
||||
### [`AbstractContextUtility`](AbstractContextUtility.cs)
|
||||
|
||||
抽象上下文工具类,提供上下文相关的通用功能实现。继承自 ContextAwareBase 并实现 IContextUtility 接口。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```csharp
|
||||
public abstract class AbstractContextUtility : ContextAwareBase, IContextUtility
|
||||
{
|
||||
protected ILogger Logger = null!;
|
||||
|
||||
void IContextUtility.Init()
|
||||
{
|
||||
var name = GetType().Name;
|
||||
Logger = LoggerFactoryResolver.Provider.CreateLogger(name);
|
||||
Logger.Debug($"Initializing Context Utility: {name}");
|
||||
|
||||
OnInit(); // 子类实现初始化逻辑
|
||||
|
||||
Logger.Info($"Context Utility initialized: {name}");
|
||||
}
|
||||
|
||||
protected abstract void OnInit(); // 子类实现具体的初始化逻辑
|
||||
}
|
||||
```
|
||||
|
||||
## 基本使用
|
||||
|
||||
### 1. 定义 Utility
|
||||
|
||||
```csharp
|
||||
// 存储工具类
|
||||
public class StorageUtility : IUtility
|
||||
// 存储工具类,继承自AbstractContextUtility
|
||||
public class StorageUtility : AbstractContextUtility
|
||||
{
|
||||
private const string SavePath = "user://save_data.json";
|
||||
|
||||
public void Save<T>(T data)
|
||||
protected override void OnInit()
|
||||
{
|
||||
string json = Json.Stringify(data);
|
||||
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
|
||||
file.StoreString(json);
|
||||
Logger.Info("StorageUtility initialized");
|
||||
}
|
||||
|
||||
public T Load<T>() where T : new()
|
||||
public void Save<T>(T data)
|
||||
{
|
||||
if (!FileAccess.FileExists(SavePath))
|
||||
return new T();
|
||||
string json = JsonSerializer.Serialize(data);
|
||||
// 实际保存逻辑
|
||||
File.WriteAllText(SavePath, json);
|
||||
}
|
||||
|
||||
public T Load<T>()
|
||||
{
|
||||
if (!File.Exists(SavePath))
|
||||
return default(T);
|
||||
|
||||
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
|
||||
string json = file.GetAsText();
|
||||
return Json.Parse<T>(json);
|
||||
string json = File.ReadAllText(SavePath);
|
||||
return JsonSerializer.Deserialize<T>(json);
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (FileAccess.FileExists(SavePath))
|
||||
if (File.Exists(SavePath))
|
||||
{
|
||||
DirAccess.RemoveAbsolute(SavePath);
|
||||
File.Delete(SavePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 数学工具类
|
||||
// 数学工具类,作为普通Utility
|
||||
public class MathUtility : IUtility
|
||||
{
|
||||
public float Lerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b - a) * Mathf.Clamp(t, 0f, 1f);
|
||||
return a + (b - a) * Math.Clamp(t, 0f, 1f);
|
||||
}
|
||||
|
||||
public Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, float t)
|
||||
public bool IsInRange(float value, float min, float max)
|
||||
{
|
||||
float u = 1 - t;
|
||||
return u * u * p0 + 2 * u * t * p1 + t * t * p2;
|
||||
}
|
||||
|
||||
public bool IsInRange(Vector3 point, Vector3 center, float radius)
|
||||
{
|
||||
return point.DistanceTo(center) <= radius;
|
||||
}
|
||||
|
||||
public int RollDice(int sides)
|
||||
{
|
||||
return GD.RandRange(1, sides);
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user