**备忘录模式(Memento Pattern)**是一种行为型设计模式,主要用于保存一个对象当前的状态,并在需要时恢复该状态。它常应用于以下场景:
- 撤销操作:如文本编辑器撤销、软件开发中的版本控制等,用户可以返回到之前的状态。
- 备份和恢复:系统故障时,使用备份数据恢复到之前的状态,减少数据丢失风险。
- 系统快照:在执行高风险操作之前,创建系统状态的快照,方便后续进行回滚操作。
备忘录模式主要有三个参与者:发起人(Originator)、备忘录(Memento)和负责人(Caretaker)。发起人负责创建备忘录和恢复状态;备忘录负责存储发起人的状态信息;负责人管理多个备忘录。
QT示例:
Memento:增加一个Memento类去记录QTextEdit的字体和颜色;
MyCommand类:对应备忘录模式的Originator(发起人);
QUndoStack类:对应 Caretaker(负责人),负责管理撤销和重做操作,维护命令的历史记录。
#include <QApplication>
#include <QTextEdit>
#include <QUndoStack>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUndoCommand>
class Memento {
public:
Memento(const QString &text, const QFont &font, const QColor &color)
: m_text(text), m_font(font), m_color(color) {}
const QString& getText() const { return m_text; }
const QFont& getFont() const { return m_font; }
const QColor& getColor() const { return m_color; }
private:
QString m_text;
QFont m_font;
QColor m_color;
};
class QUndoCommand
{
public:
virtual ~QUndoCommand() {}
virtual void undo() = 0;
virtual void redo() = 0;
};
class MyCommand : public QUndoCommand {
public:
MyCommand(QTextEdit *editor, const QString &text, const QFont &font,
const QColor &color, QUndoCommand *parent = nullptr)
: QUndoCommand(parent), m_editor(editor),
m_newMemento(std::make_shared<Memento>(text, font, color)) {
m_oldMemento = std::make_shared<Memento>(editor->toPlainText(),
editor->font(), editor->textColor());
}
void undo() override {
m_editor->setPlainText(m_oldMemento->getText());
m_editor->setFont(m_oldMemento->getFont());
m_editor->setTextColor(m_oldMemento->getColor());
}
void redo() override {
m_editor->setPlainText(m_newMemento->getText());
m_editor->setFont(m_newMemento->getFont());
m_editor->setTextColor(m_newMemento->getColor());
}
private:
QTextEdit *m_editor;
std::shared_ptr<Memento> m_newMemento;
std::shared_ptr<Memento> m_oldMemento;
};
class QUndoStack
{
public:
void push(QUndoCommand *cmd)
{
m_stack.push(cmd);
cmd->redo();
}
void undo()
{
if (!m_stack.isEmpty()) {
QUndoCommand *cmd = m_stack.pop();
cmd->undo();
m_undoStack.push(cmd);
}
}
void redo()
{
if (!m_undoStack.isEmpty()) {
QUndoCommand *cmd = m_undoStack.pop();
cmd->redo();
m_stack.push(cmd);
}
}
private:
QStack<QUndoCommand*> m_stack;
QStack<QUndoCommand*> m_undoStack;
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
QUndoStack stack;
QTextEdit editor;
QPushButton undoButton("Undo");
QPushButton redoButton("Redo");
QObject::connect(&undoButton, &QPushButton::clicked, &stack, &QUndoStack::undo);
QObject::connect(&redoButton, &QPushButton::clicked, &stack, &QUndoStack::redo);
// 假设用户通过一些方式,可以更改QTextEdit的字体和颜色
QObject::connect(&editor, &QTextEdit::cursorPositionChanged, [&]() {
QFont font = editor->font();
QColor color = editor->textColor();
stack.push(new MyCommand(&editor, editor.toPlainText(), font, color));
});
QVBoxLayout layout;
layout.addWidget(&editor);
layout.addWidget(&undoButton);
layout.addWidget(&redoButton);
QWidget window;
window.setLayout(&layout);
window.show();
return app.exec();
}
在这个示例中,我们假设用户可以通过一些方式,可以更改QTextEdit
的字体和颜色,并且这也需要做到撤销和重做。我们如果结合使用备忘录模式,增加一个Memento
类去记录QTextEdit
的字体和颜色,那么这个过程就简单的多了。否则,MyCommand
类需要存储所有的状态,如果状态相关的数据对象越来越多,可能会导致MyCommand
类的职责过重。
在这里,MyCommand
类兼任了备忘录模式的Originator类和命令模式的Receiver类,QUndoStack
类兼任了备忘录模式的Caretaker类和命令模式的Invoker类。所以实际上,命令模式和备忘录模式,可以简单理解为在命令模式的基础上增加一个Memento
类就可以了。