我曾经有一个项目来创建一个 3D 建模应用程序,并且我曾经有相同的要求。据我在处理它时所了解的,无论如何,操作都应该始终知道它做了什么,因此应该知道如何撤消它。所以我为每个操作创建了一个基类,它的操作状态如下所示。
class OperationState
{
protected:
Operation& mParent;
OperationState(Operation& parent);
public:
virtual ~OperationState();
Operation& getParent();
};
class Operation
{
private:
const std::string mName;
public:
Operation(const std::string& name);
virtual ~Operation();
const std::string& getName() const{return mName;}
virtual OperationState* operator ()() = 0;
virtual bool undo(OperationState* state) = 0;
virtual bool redo(OperationState* state) = 0;
};
创建一个函数及其状态如下:
class MoveState : public OperationState
{
public:
struct ObjectPos
{
Object* object;
Vector3 prevPosition;
};
MoveState(MoveOperation& parent):OperationState(parent){}
typedef std::list<ObjectPos> PrevPositions;
PrevPositions prevPositions;
};
class MoveOperation : public Operation
{
public:
MoveOperation():Operation("Move"){}
~MoveOperation();
// Implement the function and return the previous
// previous states of the objects this function
// changed.
virtual OperationState* operator ()();
// Implement the undo function
virtual bool undo(OperationState* state);
// Implement the redo function
virtual bool redo(OperationState* state);
};
曾经有一个类叫做 OperationManager。这注册了不同的函数并在其中创建了它们的实例,例如:
OperationManager& opMgr = OperationManager::GetInstance();
opMgr.register<MoveOperation>();
注册功能是这样的:
template <typename T>
void OperationManager::register()
{
T* op = new T();
const std::string& op_name = op->getName();
if(mOperations.count(op_name))
{
delete op;
}else{
mOperations[op_name] = op;
}
}
每当要执行一个函数时,它将基于当前选择的对象或它需要处理的任何内容。注意:在我的例子中,我不需要发送每个对象应该移动多少的详细信息,因为一旦它被设置为活动功能,它是由输入设备的 MoveOperation 计算的。
在 OperationManager 中,执行一个函数就像:
void OperationManager::execute(const std::string& operation_name)
{
if(mOperations.count(operation_name))
{
Operation& op = *mOperations[operation_name];
OperationState* opState = op();
if(opState)
{
mUndoStack.push(opState);
}
}
}
当需要撤消时,您可以从 OperationManager 中执行如下操作: OperationManager
OperationManager::GetInstance().undo();
的撤消功能如下所示:
void OperationManager::undo()
{
if(!mUndoStack.empty())
{
OperationState* state = mUndoStack.pop();
if(state->getParent().undo(state))
{
mRedoStack.push(state);
}else{
// Throw an exception or warn the user.
}
}
}
这使得 OperationManager 不知道每个函数需要什么参数,因此很容易管理不同的函数。