mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 19:03:29 +08:00
- 移除 controller.md 文件,将控制器相关内容整合到其他文档中 - 重构 command.md 文档,更新命令基类的类型参数设计 - 添加新的命令基类 AbstractCommand<TInput> 和 AbstractCommand<TInput, TResult> - 更新命令使用示例,采用输入参数对象替代构造函数参数 - 优化事件注册相关代码示例,移除 Godot 特定的生命周期方法 - 更新依赖注入容器文档,明确 Register 方法的泛型特性 - 添加模型异步初始化功能说明和相关接口介绍 - 重构查询系统文档,统一采用输入参数对象的设计模式 - 更新架构生命周期枚举值,使用更准确的阶段名称 - 添加 GitHub Actions 工作流配置,集成 LLM 准备的文档索引功能
10 KiB
10 KiB
最佳实践
本文档总结了使用 GFramework 的最佳实践和设计模式。
架构设计
1. 清晰的职责分离
原则:每一层都有明确的职责,不要混淆。
// ✅ 正确的职责分离
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. 事件驱动设计
原则:使用事件解耦组件,避免直接调用。
// ❌ 紧耦合
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)。
// ✅ 正确的 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. 命名规范
// 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. 正确的注销管理
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. 生命周期管理
public class GameManager
{
private GameArchitecture _architecture;
public void StartGame()
{
_architecture = new GameArchitecture();
_architecture.Initialize();
}
public void EndGame()
{
// 销毁架构,自动清理所有组件
_architecture.Destroy();
_architecture = null;
}
}
性能优化
1. 缓存组件引用
// ❌ 低效:每次都查询
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. 避免频繁的事件创建
// ❌ 低效:每帧创建新事件
public void Update()
{
this.SendEvent(new UpdateEvent()); // 频繁分配内存
}
// ✅ 高效:复用事件或使用对象池
private UpdateEvent _updateEvent = new UpdateEvent();
public void Update()
{
this.SendEvent(_updateEvent);
}
3. 异步处理重操作
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. 单元测试
[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. 集成测试
[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. 代码注释
/// <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 中包含业务逻辑
// ❌ 错误
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. 忘记注销监听器
// ❌ 错误:可能导致内存泄漏
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. 直接调用其他系统
// ❌ 错误:紧耦合
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());
}
}
遵循这些最佳实践将帮助你构建可维护、高效、可扩展的应用程序。