mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 将 CommandBus 重命名为 CommandExecutor 并更新相关接口 - 将 QueryBus 重命名为 QueryExecutor 并更新相关接口 - 将 AsyncQueryBus 重命名为 AsyncQueryExecutor 并更新相关接口 - 更新 ArchitectureContext 中的服务引用和错误消息 - 修改 ArchitectureServices 中的私有字段和公共属性名称 - 更新所有测试文件中的实例变量和服务引用 - 修改测试类名称以匹配新的执行器命名 - 更新状态机系统测试中的容器注册项
Command 包使用说明
概述
Command 包实现了命令模式(Command Pattern),用于封装用户操作和业务逻辑。通过命令模式,可以将请求封装为对象,实现操作的参数化、队列化、日志记录、撤销等功能。
核心接口
1. ICommand
无返回值命令接口,定义了命令的基本契约。
核心方法:
void Execute(); // 执行命令
2. ICommand<TResult>
带返回值的命令接口,用于需要返回执行结果的命令。
核心方法:
TResult Execute(); // 执行命令并返回结果
核心类
1. AbstractCommand<TInput>
无返回值命令的抽象基类,提供了命令的基础实现。它继承自 ContextAwareBase ,具有上下文感知能力。
使用示例:
// 定义一个命令输入参数
public struct StartGameCommandInput : ICommandInput
{
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.PlayerName.Value = input.PlayerName;
gameModel.CurrentLevel.Value = input.LevelId;
gameModel.GameState.Value = GameState.Playing;
// 发送事件通知其他模块
this.SendEvent(new GameStartedEvent());
}
}
// 使用命令
public class GameController : IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void OnStartButtonClicked()
{
// 发送命令实例
this.SendCommand(new StartGameCommand(new StartGameCommandInput
{
LevelId = 1,
PlayerName = "Player1"
}));
}
}
2. AbstractCommand<TInput, TResult>
带返回值命令的抽象基类,同样继承自 ContextAwareBase。
使用示例:
// 定义一个计算伤害的命令输入
public struct CalculateDamageCommandInput : ICommandInput
{
public int AttackerAttackPower { get; set; }
public int DefenderDefense { get; set; }
}
// 定义一个计算伤害的命令
public class CalculateDamageCommand : AbstractCommand<CalculateDamageCommandInput, int>
{
public CalculateDamageCommand(CalculateDamageCommandInput input) : base(input)
{
}
protected override int OnExecute(CalculateDamageCommandInput input)
{
// 获取游戏配置
var config = this.GetModel<GameConfigModel>();
// 计算最终伤害
var baseDamage = input.AttackerAttackPower - input.DefenderDefense;
var finalDamage = Math.Max(1, baseDamage * config.DamageMultiplier);
return (int)finalDamage;
}
}
// 使用带返回值的命令
public class CombatSystem : AbstractSystem
{
protected override void OnInit() { }
public void Attack(Character attacker, Character defender)
{
// 发送命令并获取返回值
var damage = this.SendCommand(new CalculateDamageCommand(new CalculateDamageCommandInput
{
AttackerAttackPower = attacker.AttackPower,
DefenderDefense = defender.Defense
}));
// 应用伤害
defender.Health -= damage;
// 发送伤害事件
this.SendEvent(new DamageDealtEvent(attacker, defender, damage));
}
}
命令的生命周期
- 创建命令:实例化命令对象,传入必要的参数
- 执行命令:调用
Execute()方法,内部委托给OnExecute() - 返回结果:对于带返回值的命令,返回执行结果
- 命令销毁:命令执行完毕后可以被垃圾回收
CommandBus - 命令总线
功能说明
CommandBus 是命令执行的核心组件,负责发送和执行命令。
主要方法:
void Send(ICommand command); // 发送无返回值命令
TResult Send<TResult>(ICommand<TResult> command); // 发送带返回值命令
使用示例
var commandBus = new CommandBus();
// 发送无返回值命令
commandBus.Send(new StartGameCommand(new StartGameCommandInput()));
// 发送带返回值命令
var result = commandBus.Send(new CalculateDamageCommand(new CalculateDamageCommandInput()));
EmptyCommandInput - 空命令输入
当命令不需要输入参数时,可以使用 EmptyCommandInput 类:
public class SimpleActionCommand : AbstractCommand<EmptyCommandInput>
{
public SimpleActionCommand(EmptyCommandInput input) : base(input)
{
}
protected override void OnExecute(EmptyCommandInput input)
{
// 执行简单操作,无需额外参数
this.SendEvent(new SimpleActionEvent());
}
}
使用场景
1. 用户交互操作
public struct SaveGameCommandInput : ICommandInput
{
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, input.SaveSlot);
this.SendEvent(new GameSavedEvent(input.SaveSlot));
}
}
2. 业务流程控制
public struct LoadLevelCommandInput : ICommandInput
{
public int LevelId { get; set; }
}
public class LoadLevelCommand : AbstractCommand<LoadLevelCommandInput>
{
public LoadLevelCommand(LoadLevelCommandInput input) : base(input)
{
}
protected override void OnExecute(LoadLevelCommandInput input)
{
var levelSystem = this.GetSystem<LevelSystem>();
var uiSystem = this.GetSystem<UISystem>();
// 显示加载界面
uiSystem.ShowLoadingScreen();
// 加载关卡
levelSystem.LoadLevel(input.LevelId);
// 发送事件
this.SendEvent(new LevelLoadedEvent(input.LevelId));
}
}
最佳实践
- 保持命令原子性:一个命令应该完成一个完整的业务操作
- 命令无状态:命令不应该保存长期状态,执行完即可丢弃
- 参数通过构造函数传递:命令需要的参数应在创建时传入
- 避免命令嵌套:命令内部尽量不要发送其他命令,使用事件通信
- 合理使用返回值:只在确实需要返回结果时使用
AbstractCommand<TInput, TResult> - 命令命名规范:使用动词+名词形式,如
StartGameCommand、SavePlayerCommand - 输入参数结构化:使用
ICommandInput接口的实现类来组织命令参数
扩展功能
命令撤销/重做(可扩展)
public struct MoveCommandInput : ICommandInput
{
public Vector3 NewPosition { get; set; }
}
// 实现可撤销命令
public class MoveCommand : AbstractCommand<MoveCommandInput>, IUndoableCommand
{
private Vector3 _oldPosition;
private Vector3 _newPosition;
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 = input.NewPosition;
}
public void Undo()
{
var player = this.GetModel<PlayerModel>();
player.Position = _oldPosition;
}
}
相关包
architecture- 架构核心,负责命令的分发和执行extensions- 提供SendCommand()扩展方法query- 查询模式,用于数据查询events- 事件系统,命令执行后的通知机制system- 业务系统,命令的主要执行者model- 数据模型,命令操作的数据
许可证: Apache 2.0