Однажды у меня был проект по созданию приложения для 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::GetInstance().undo();
И функция отмены OperationManager выглядит следующим образом:
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 не знал, какие аргументы нужны каждой функции, и поэтому управлять разными функциями было легко.
person
Vite Falcon
schedule
24.06.2010
Commandдолжны быть публичными? - person CB Bailey   schedule 24.06.2010