【问题标题】:Command Pattern: Where to create the Command items?命令模式:在哪里创建命令项?
【发布时间】:2015-10-26 18:58:42
【问题描述】:

我已经非常广泛地使用了命令模式,而且效果很好。但是,通常不讨论的是 在哪里创建了 Commands 的实例。

以下示例说明了这个问题:Document 具有设置文本的函数 setText()

class Document {
public:
    void setText(const std::string text) {
        if (commandManager()->isActive()) {
            // called by SetTextCommand
            m_text = text;
        } else {
            // called somewhere in the application
            commandManager()->addAndExecute(new SetTextCommand(this, text));
        }
    }
    std::string text() const { return m_text; }

    CommandManager * commandManager() const { return m_commandManager; }
private:
    std::string m_text;
    CommandManager * m_commandManager;
}

这里,SetTextCommand 会像这样执行document->setText(text)

class SetTextCommand : public Command {
public:
    SetTextCommand(Document * doc, const std::string & text)
        : Command(), m_doc(doc), m_oldText(doc->text()), m_text(text)
    {}
    void redo() override {
        m_doc->setText(m_text);
    }
    void undo() override {
        m_doc->setText(m_oldText, false);
    }
}

SetTextCommandCommandManager 处理,如下所示:

CommandManager::addAndExecute(Command * command) {
    m_doc->commandManager()->setActive(true); // THIS IS THE TRICK
    command->redo();
    m_doc->commandManager()->setActive(false);  // THIS IS THE TRICK
    m_stack->push_back(command);
}

这里的技巧是,当运行redo() 时,CommandManager::isActive() 设置为 true。因此,Document::setText() 将设置为 m_text

显然,所有 Document setter 函数都必须遵循 if (commandManager()->isActive()) { ... } else { ... } 范式。这是因为命令本身是在 setter 函数中创建的。

现在的问题是:这是实现命令模式的好方法吗?还是有更简洁的解决方案来创建命令,同时拥有一个不错的 API?

请详细回答。

【问题讨论】:

  • new SetTextCommand(this, text) 有内存泄漏的味道
  • 不,我省略的是根据命令堆栈在内部跟踪这些项目的 CommandManager。这根本不是问题:)
  • 添加并执行?您是否一次添加并执行命令?那有什么意义呢?我希望有一种撤销命令的 lifo 队列。这些只会在撤消时执行。
  • 我更新了代码以更多地反映我的意思。查看 SetTextCommand 类以及它如何调用 m_doc->setText()。现在问题更清楚了吗?
  • 为什么 setText 创建对象?这不是违反 SRP(单一职责原则)吗?

标签: c++ command-pattern


【解决方案1】:

我认为必须在任何地方复制if (commandManager()->isActive()) 可能会非常难看...让setText 始终执行SetTextCommand 路径并创建一个新的setTextImmediate 方法可能更好SetTextCommand可以用。

【讨论】:

  • 是的,这可能会更好,它将工作/实现隐藏在私有函数后面。但是,由于您没有批评该方法或指出实现此方法的更好方法,因此您基本上是在说:“是的,这就是要走的路。”对吗?
  • 他说创建命令没问题,但你应该避免使用 Active 标志。
  • 我现在已经接受了这个答案,尽管我确信还有更好的方法可以做到这一点......好吧,我会睁大眼睛。 :-)
猜你喜欢
  • 1970-01-01
  • 2019-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-06
  • 2021-11-04
  • 2011-02-04
相关资源
最近更新 更多