最近在做功能时,有个这样的场景:用户修改一部分的数据,需要支持用户对修改数据的撤销与恢复。
上述的业务场景实际可以抽象为对用户数据的撤销与恢复。类似问题可以用命令模式处理。
实现这一功能的关键是将一个命令看作一个对象并记录详细内容。具体的来说,需要记录一个命令影响了系统中的那些对象,该命令修改了这些对象的那些数据成员,这些数据成员在执行命令前后的取值是什么。一旦记录了这些信息,可以撤销或重做这条命令。目前Qt Undo Framework利用了命令设计模式处理该问题。
类图说明
核心类
QUndoCommand:这个类相当于Command模式中的那个抽象基类Command,所有这些命令都被保存到undo栈中,在其派生类中实现undo和redo函数。
QUndoStack:这个相当于命令历史记录,其中保存了Command对象的列表。
QUndoGroup:是一个undo stack的组合。
QUndoView:是显示undo堆栈中内容的一个列表组件,在这个视图中点击命令的名称也可以实现与Undo/Redo按钮相同的作用。
场景测试
为简化场景,用如下的场景代替。Map(oRuleIDMap)中存放规则ID与规则名称的对应关系,数据如下:
oRuleIDMap.insert(std::make_pair(0, "ModifyEdoName")); //0-修改图元名称 oRuleIDMap.insert(std::make_pair(1, "ModifyEdoVisible")); //1-修改图元可见性 oRuleIDMap.insert(std::make_pair(2, "ModifyEntName")); //2-修改构件名称 oRuleIDMap.insert(std::make_pair(3, "ModifyEntVisible")); //3-修改构件名称 |
执行操作:修改规则的所有名称,后缀添加”_Default”。修改后数据如下(相当于用户修改了规则参数并且点击确定按钮)
std::make_pair(0, "ModifyEdoName_Default") //0-修改图元名称_Default std::make_pair(1, "ModifyEdoVisible_Default") //1-修改图元可见性_Default std::make_pair(2, "ModifyEntName_Default") //2-修改构件名称_Default std::make_pair(3, "ModifyEntVisible_Default") //3-修改构件名称_Default |
执行操作:撤销上部的所有修改记录,即后缀去掉”_Default”。修改后数据如下(相当于用户修改了规则参数并且点击取消按钮)
std::make_pair(0, "ModifyEdoName") //0-修改图元名称 std::make_pair(1, "ModifyEdoVisible") //1-修改图元可见性 std::make_pair(2, "ModifyEntName") //2-修改构件名称 std::make_pair(3, "ModifyEntVisible") //3-修改构件名称 |
代码示例
<p>#include <QUndoStack></p><p>#include <QUndoCommand></p><p>#include <QDebug></p>
typedef std::map<int, QString> MyRuleDataMap;
class MyModifyCommand: public QUndoCommand
{
public:
MyModifyCommand(MyRuleDataMap* oRuleDataMap, int nRuleID, const QString& sOldValue, const QString& sNewValue);
virtual void undo();
virtual void redo();
private:
int m_nRuleID;
QString m_sOldValue, m_sNewValue;
MyRuleDataMap* m_pRuleDataMap;
};
MyModifyCommand::MyModifyCommand(MyRuleDataMap* oRuleDataMap, int nRuleID,
const QString& sOldValue, const QString& sNewValue)
:QUndoCommand(nullptr), m_pRuleDataMap(oRuleDataMap), m_nRuleID(nRuleID), m_sOldValue(sOldValue), m_sNewValue(sNewValue)
{
}
void MyModifyCommand::undo()
{
auto pIter = m_pRuleDataMap->find(m_nRuleID);
if (pIter != m_pRuleDataMap->end())
{
pIter->second = m_sOldValue;
}
}
void MyModifyCommand::redo()
{
auto pIter = m_pRuleDataMap->find(m_nRuleID);
if (pIter != m_pRuleDataMap->end())
{
pIter->second = m_sNewValue;
}
}
void outputAllRuleData(MyRuleDataMap& oRuleMap)
{
for(auto it = oRuleMap.begin(); it != oRuleMap.end(); ++it)
{
int nRuleID = it->first;
QString sRuleData = it->second;
qDebug() << "RuleID:" << nRuleID << " RuleData:" << sRuleData;
}
}
int main(int argc, char *argv[])
{
std::shared_ptr<MyRuleDataMap> pRuleDataMap(new MyRuleDataMap);
pRuleDataMap->insert(std::make_pair(0, "ModifyEdoName")); //0-修改图元名称
pRuleDataMap->insert(std::make_pair(1, "ModifyEdoVisible")); //1-修改图元可见性
pRuleDataMap->insert(std::make_pair(2, "ModifyEntName")); //2-修改构件名称
pRuleDataMap->insert(std::make_pair(3, "ModifyEntVisible")); //3-修改构件名称
qDebug() << QStringLiteral("修改前原始数据:");
outputAllRuleData(*pRuleDataMap.get());
qDebug() << "===================================================";
std::shared_ptr<QUndoStack> pUndoStack(new QUndoStack);
MyModifyCommand* pModifyRuleID0 = new MyModifyCommand(pRuleDataMap.get(), 0, "ModifyEdoName", "ModifyEdoName_Default");
pUndoStack->push(pModifyRuleID0);
MyModifyCommand* pModifyRuleID1 = new MyModifyCommand(pRuleDataMap.get(), 1, "ModifyEdoVisible", "ModifyEdoVisible_Default");
pUndoStack->push(pModifyRuleID1);
MyModifyCommand* pModifyRuleID2 = new MyModifyCommand(pRuleDataMap.get(), 2, "ModifyEntName", "ModifyEntName_Default");
pUndoStack->push(pModifyRuleID2);
MyModifyCommand* pModifyRuleID3 = new MyModifyCommand(pRuleDataMap.get(), 3, "ModifyEntVisible", "ModifyEntVisible_Default");
pUndoStack->push(pModifyRuleID3);
qDebug() << QStringLiteral("修改原始数据:");
outputAllRuleData(*pRuleDataMap.get());
qDebug() << "===================================================";
pUndoStack->undo();
pUndoStack->undo();
pUndoStack->undo();
pUndoStack->undo();
qDebug() << QStringLiteral("撤销修改数据:");
outputAllRuleData(*pRuleDataMap.get());
qDebug() << "===================================================";
return 1;
}
测试结果
"修改前原始数据:" RuleID: 0 RuleData: "ModifyEdoName" RuleID: 1 RuleData: "ModifyEdoVisible" RuleID: 2 RuleData: "ModifyEntName" RuleID: 3 RuleData: "ModifyEntVisible" =================================================== "修改原始数据:" RuleID: 0 RuleData: "ModifyEdoName_Default" RuleID: 1 RuleData: "ModifyEdoVisible_Default" RuleID: 2 RuleData: "ModifyEntName_Default" RuleID: 3 RuleData: "ModifyEntVisible_Default" =================================================== "撤销修改数据:" RuleID: 0 RuleData: "ModifyEdoName" RuleID: 1 RuleData: "ModifyEdoVisible" RuleID: 2 RuleData: "ModifyEntName" RuleID: 3 RuleData: "ModifyEntVisible" =================================================== |