GeWuYou 93b25a19f7 docs(core): 更新命令系统文档并移除控制器独立文档
- 移除 controller.md 文件,将控制器相关内容整合到其他文档中
- 重构 command.md 文档,更新命令基类的类型参数设计
- 添加新的命令基类 AbstractCommand<TInput> 和 AbstractCommand<TInput, TResult>
- 更新命令使用示例,采用输入参数对象替代构造函数参数
- 优化事件注册相关代码示例,移除 Godot 特定的生命周期方法
- 更新依赖注入容器文档,明确 Register 方法的泛型特性
- 添加模型异步初始化功能说明和相关接口介绍
- 重构查询系统文档,统一采用输入参数对象的设计模式
- 更新架构生命周期枚举值,使用更准确的阶段名称
- 添加 GitHub Actions 工作流配置,集成 LLM 准备的文档索引功能
2026-02-12 13:13:07 +08:00

484 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 最佳实践
本文档总结了使用 GFramework 的最佳实践和设计模式。
## 架构设计
### 1. 清晰的职责分离
**原则**:每一层都有明确的职责,不要混淆。
```csharp
// ✅ 正确的职责分离
public class PlayerModel : AbstractModel
{
// Model只存储数据
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Score { get; } = new(0);
}
public class CombatSystem : AbstractSystem
{
// System处理业务逻辑
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var player = this.GetModel<PlayerModel>();
player.Health.Value -= e.Damage;
}
}
public class PlayerController : IController
{
// Controller连接 UI 和逻辑
public void Initialize()
{
var player = _architecture.GetModel<PlayerModel>();
player.Health.RegisterWithInitValue(OnHealthChanged);
}
private void OnHealthChanged(int health)
{
UpdateHealthDisplay(health);
}
}
```
### 2. 事件驱动设计
**原则**:使用事件解耦组件,避免直接调用。
```csharp
// ❌ 紧耦合
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
var systemB = this.GetSystem<SystemB>();
systemB.DoSomething(); // 直接调用
}
}
// ✅ 松耦合
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
this.SendEvent(new EventB()); // 发送事件
}
}
public class SystemB : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<EventB>(OnEventB);
}
}
```
### 3. 命令查询分离
**原则**明确区分修改状态Command和查询状态Query
```csharp
// ✅ 正确的 CQRS
public class MovePlayerCommand : AbstractCommand
{
public Vector2 Direction { get; set; }
protected override void OnDo()
{
// 修改状态
this.SendEvent(new PlayerMovedEvent { Direction = Direction });
}
}
public class GetPlayerPositionQuery : AbstractQuery<Vector2>
{
protected override Vector2 OnDo()
{
// 只查询,不修改
return this.GetModel<PlayerModel>().Position.Value;
}
}
```
## 代码组织
### 1. 项目结构
```
GameProject/
├── Models/
│ ├── PlayerModel.cs
│ ├── GameStateModel.cs
│ └── InventoryModel.cs
├── Systems/
│ ├── CombatSystem.cs
│ ├── InventorySystem.cs
│ └── GameLogicSystem.cs
├── Commands/
│ ├── AttackCommand.cs
│ ├── MoveCommand.cs
│ └── UseItemCommand.cs
├── Queries/
│ ├── GetPlayerHealthQuery.cs
│ └── GetInventoryItemsQuery.cs
├── Events/
│ ├── PlayerDiedEvent.cs
│ ├── ItemUsedEvent.cs
│ └── EnemyDamagedEvent.cs
├── Controllers/
│ ├── PlayerController.cs
│ └── UIController.cs
├── Utilities/
│ ├── StorageUtility.cs
│ └── MathUtility.cs
└── GameArchitecture.cs
```
### 2. 命名规范
```csharp
// Models使用 Model 后缀
public class PlayerModel : AbstractModel { }
public class GameStateModel : AbstractModel { }
// Systems使用 System 后缀
public class CombatSystem : AbstractSystem { }
public class InventorySystem : AbstractSystem { }
// Commands使用 Command 后缀
public class AttackCommand : AbstractCommand { }
public class MoveCommand : AbstractCommand { }
// Queries使用 Query 后缀
public class GetPlayerHealthQuery : AbstractQuery<int> { }
public class GetInventoryItemsQuery : AbstractQuery<List<Item>> { }
// Events使用 Event 后缀
public class PlayerDiedEvent : IEvent { }
public class ItemUsedEvent : IEvent { }
// Controllers使用 Controller 后缀
public class PlayerController : IController { }
// Utilities使用 Utility 后缀
public class StorageUtility : IUtility { }
```
## 内存管理
### 1. 正确的注销管理
```csharp
public class MyController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize()
{
var model = _architecture.GetModel<PlayerModel>();
// 注册事件并添加到注销列表
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
.AddToUnregisterList(_unregisterList);
// 注册属性监听并添加到注销列表
model.Health.Register(OnHealthChanged)
.AddToUnregisterList(_unregisterList);
}
public void Cleanup()
{
// 统一注销所有监听器
_unregisterList.UnRegisterAll();
}
private void OnPlayerDied(PlayerDiedEvent e) { }
private void OnHealthChanged(int health) { }
}
```
### 2. 生命周期管理
```csharp
public class GameManager
{
private GameArchitecture _architecture;
public void StartGame()
{
_architecture = new GameArchitecture();
_architecture.Initialize();
}
public void EndGame()
{
// 销毁架构,自动清理所有组件
_architecture.Destroy();
_architecture = null;
}
}
```
## 性能优化
### 1. 缓存组件引用
```csharp
// ❌ 低效:每次都查询
public void Update()
{
var model = _architecture.GetModel<PlayerModel>();
model.Health.Value -= 1;
}
// ✅ 高效:缓存引用
private PlayerModel _playerModel;
public void Initialize()
{
_playerModel = _architecture.GetModel<PlayerModel>();
}
public void Update()
{
_playerModel.Health.Value -= 1;
}
```
### 2. 避免频繁的事件创建
```csharp
// ❌ 低效:每帧创建新事件
public void Update()
{
this.SendEvent(new UpdateEvent()); // 频繁分配内存
}
// ✅ 高效:复用事件或使用对象池
private UpdateEvent _updateEvent = new UpdateEvent();
public void Update()
{
this.SendEvent(_updateEvent);
}
```
### 3. 异步处理重操作
```csharp
public class LoadDataCommand : AbstractCommand
{
protected override async void OnDo()
{
// 异步加载数据,不阻塞主线程
var data = await LoadDataAsync();
this.SendEvent(new DataLoadedEvent { Data = data });
}
private async Task<Data> LoadDataAsync()
{
return await Task.Run(() =>
{
// 耗时操作
return new Data();
});
}
}
```
## 测试
### 1. 单元测试
```csharp
[TestFixture]
public class CombatSystemTests
{
private GameArchitecture _architecture;
private PlayerModel _playerModel;
[SetUp]
public void Setup()
{
_architecture = new TestArchitecture();
_architecture.Initialize();
_playerModel = _architecture.GetModel<PlayerModel>();
}
[TearDown]
public void Teardown()
{
_architecture.Destroy();
}
[Test]
public void PlayerTakeDamage_ReducesHealth()
{
_playerModel.Health.Value = 100;
_architecture.SendEvent(new DamageEvent { Amount = 10 });
Assert.AreEqual(90, _playerModel.Health.Value);
}
[Test]
public void PlayerDies_WhenHealthReachesZero()
{
_playerModel.Health.Value = 10;
_architecture.SendEvent(new DamageEvent { Amount = 10 });
Assert.AreEqual(0, _playerModel.Health.Value);
}
}
```
### 2. 集成测试
```csharp
[TestFixture]
public class GameFlowTests
{
private GameArchitecture _architecture;
[SetUp]
public void Setup()
{
_architecture = new GameArchitecture();
_architecture.Initialize();
}
[Test]
public void CompleteGameFlow()
{
// 初始化
var player = _architecture.GetModel<PlayerModel>();
Assert.AreEqual(100, player.Health.Value);
// 执行操作
_architecture.SendCommand(new AttackCommand { Damage = 20 });
// 验证结果
Assert.AreEqual(80, player.Health.Value);
}
}
```
## 文档
### 1. 代码注释
```csharp
/// <summary>
/// 玩家模型,存储玩家的所有状态数据
/// </summary>
public class PlayerModel : AbstractModel
{
/// <summary>
/// 玩家的生命值,使用 BindableProperty 实现响应式更新
/// </summary>
public BindableProperty<int> Health { get; } = new(100);
protected override void OnInit()
{
// 监听生命值变化,当生命值为 0 时发送死亡事件
Health.Register(hp =>
{
if (hp <= 0)
this.SendEvent(new PlayerDiedEvent());
});
}
}
```
### 2. 架构文档
为你的项目编写架构文档,说明:
- 主要的 Model、System、Command、Query
- 关键事件流
- 组件间的通信方式
- 扩展点和插件机制
## 常见陷阱
### 1. 在 Model 中包含业务逻辑
```csharp
// ❌ 错误
public class PlayerModel : AbstractModel
{
public void TakeDamage(int damage)
{
Health.Value -= damage;
if (Health.Value <= 0)
Die();
}
}
// ✅ 正确
public class CombatSystem : AbstractSystem
{
private void OnDamage(DamageEvent e)
{
var player = this.GetModel<PlayerModel>();
player.Health.Value -= e.Amount;
}
}
```
### 2. 忘记注销监听器
```csharp
// ❌ 错误:可能导致内存泄漏
public void Initialize()
{
this.RegisterEvent<Event1>(OnEvent1); // 未注销
}
// ✅ 正确
private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize()
{
this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
}
public void Cleanup()
{
_unregisterList.UnRegisterAll();
}
```
### 3. 直接调用其他系统
```csharp
// ❌ 错误:紧耦合
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
var systemB = this.GetSystem<SystemB>();
systemB.DoSomething();
}
}
// ✅ 正确:使用事件解耦
public class SystemA : AbstractSystem
{
private void OnEvent(EventA e)
{
this.SendEvent(new EventB());
}
}
```
---
遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。