You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity项目中Command模式应用是否正确?无Invoker实现疑问

你的实现是否符合Command模式规范?

你的实现已经符合Command模式的核心规范,但没有引入Invoker(调用者)确实会丢失这个模式的部分扩展能力,下面具体拆解:

符合规范的核心点

  • 你定义了抽象的iCommand接口,约定了Execute()方法,这是Command模式的核心契约,保证所有命令都遵循统一的执行标准。
  • GenerateCommand作为具体命令类,完整封装了生成操作的逻辑(销毁旧地图、设置尺寸、生成、构建),同时持有执行所需的接收者(Generator)和依赖控件(Slider),完全符合“将请求封装为对象”的模式核心思想。
  • 调用端(AppController的Generate()方法)通过命令对象间接执行操作,实现了调用者(按钮触发逻辑)和接收者(Generator)的解耦——按钮不需要知道Generator的具体方法,只需要调用命令的Execute()。

缺少Invoker的影响

目前你的AppController实际上承担了部分Invoker的职责,但没有将命令管理的逻辑抽离出来:

  • 无法方便地扩展命令的撤销/重做功能:如果后续需要支持撤销生成操作,你得在AppController里维护命令历史,会让控制器逻辑变得臃肿。
  • 难以实现批量命令执行、命令队列等功能:比如要同时执行多个命令(生成+初始化玩家位置),直接在控制器里写会破坏单一职责。
  • 命令的执行逻辑和控制器耦合:每次修改命令执行的规则(比如加日志、加延迟),都要改动AppController。

优化建议:添加Invoker

可以创建一个简单的CommandInvoker类来专门管理命令的执行和历史:

public class CommandInvoker
{
    private Stack<iCommand> _commandHistory = new Stack<iCommand>();

    public void ExecuteCommand(iCommand command)
    {
        command.Execute();
        _commandHistory.Push(command);
    }

    // 扩展撤销功能
    public void UndoLastCommand()
    {
        if (_commandHistory.Count > 0)
        {
            // 这里需要你的Command接口添加Undo方法
            var lastCommand = _commandHistory.Pop();
            if (lastCommand is IUndoableCommand undoable)
            {
                undoable.Undo();
            }
        }
    }
}

// 扩展支持撤销的命令接口
public interface IUndoableCommand : iCommand
{
    void Undo();
}

// 修改GenerateCommand实现Undo
public class GenerateCommand : IUndoableCommand
{
    private Generator _generator;
    private Slider _sliderWidth, _sliderHeight;
    // 记录撤销所需的旧状态
    private int _oldWidth;
    private int _oldHeight;

    public GenerateCommand(Generator generator, Slider sliderWidth, Slider sliderHeight)
    {
        _generator = generator;
        _sliderWidth = sliderWidth;
        _sliderHeight = sliderHeight;
        // 保存旧状态用于撤销
        _oldWidth = generator.GetWidth();
        _oldHeight = generator.GetHeight();
    }

    public void Execute()
    {
        _generator.Destroy();
        _generator.SetWidth((int)_sliderWidth.value);
        _generator.SetHeight((int)_sliderHeight.value);
        _generator.Generate();
        _generator.Build();
    }

    public void Undo()
    {
        _generator.Destroy();
        _generator.SetWidth(_oldWidth);
        _generator.SetHeight(_oldHeight);
        _generator.Generate();
        _generator.Build();
    }
}

然后修改AppController,依赖CommandInvoker:

public class AppController : MonoBehaviour
{
    public Generator generator;
    public Slider sliderWidth, sliderHeight;
    public GameObject generatorController, playerController, UI, app, room, spawnLocation;

    private CommandInvoker _invoker;

    void Start()
    {
        _invoker = new CommandInvoker();
        generator = new DepthFirstSearchGenerator(room: room, spawnLocation: spawnLocation);
        Generate();
    }

    public void Generate()
    {
        var command = new GenerateCommand(generator, sliderWidth, sliderHeight);
        _invoker.ExecuteCommand(command);
    }

    // 可以添加按钮调用撤销方法
    public void UndoGenerate()
    {
        _invoker.UndoLastCommand();
    }
}

这样既保留了Command模式的解耦优势,又通过Invoker获得了扩展命令管理能力的空间。

内容的提问来源于stack exchange,提问作者Lucas Luque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 01:15:27