命令模式
Commands are an object-oriented replacement for callbacks.
1.一个简单的实现
void InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) jump();
else if (isPressed(BUTTON_Y)) fireGun();
else if (isPressed(BUTTON_A)) swapWeapon();
else if (isPressed(BUTTON_B)) lurchIneffectively();
}
这通常是在我们希望按钮输入和动作为硬编码的情况下,但通常在游戏中,允许用户自己配置按钮对应的动作。我们通过实现一个命令基类,以及相应的子类,在输入与动作之间加一层,代码如下:
2.又一个实现
class Command //基类
{
public:
virtual ~Command() {}
virtual void execute() = 0;
Actor *actor;
};
class JumpCommand : public Command //子类
{
public:
virtual void execute() { actor->jump(); }
};
class FireCommand : public Command
{
public:
virtual void execute() { actor->fireGun(); }
};
// ...
class InputHandler //输入处理类
{
public:
void handleInput();
// Methods to bind commands...
private:
Command* buttonX_;
Command* buttonY_;
Command* buttonA_;
Command* buttonB_;
};
void InputHandler::handleInput() //通过命令类来间接调用相应的动作
{
if (isPressed(BUTTON_X)) buttonX_->execute();
else if (isPressed(BUTTON_Y)) buttonY_->execute();
else if (isPressed(BUTTON_A)) buttonA_->execute();
else if (isPressed(BUTTON_B)) buttonB_->execute();
}
这种方式可以实现动作的动态绑定,但是动作对应的实体则被限制(命令包含实体的指针),我们可以通过指定发生动作的实体来进一步进行解耦。
3.指定动作发生者
class Command
{
public:
virtual ~Command() {}
virtual void execute(GameActor& actor) = 0;
};
class JumpCommand : public Command
{
public:
virtual void execute(GameActor& actor)
{
actor.jump();
}
};
Command* InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) return buttonX_;
if (isPressed(BUTTON_Y)) return buttonY_;
if (isPressed(BUTTON_A)) return buttonA_;
if (isPressed(BUTTON_B)) return buttonB_;
// Nothing pressed, so do nothing.
return NULL;
}
Command* command = inputHandler.handleInput();
if (command)
{
command->execute(actor);
}
通过这种方式我们可以将命令与动作实施对象解耦,使得命令得以流式处理。
4.重做与撤销
class Command
{
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
class MoveUnitCommand : public Command
{
public:
MoveUnitCommand(Unit* unit, int x, int y)
: unit_(unit),
xBefore_(0),
yBefore_(0),
x_(x),
y_(y)
{}
virtual void execute()
{
// Remember the unit's position before the move
// so we can restore it.
xBefore_ = unit_->x();
yBefore_ = unit_->y();
unit_->moveTo(x_, y_);
}
virtual void undo()
{
unit_->moveTo(xBefore_, yBefore_);
}
private:
Unit* unit_;
int xBefore_, yBefore_; //记录移动之前的位置
int x_, y_;
};
Command* handleInput()
{
Unit* unit = getSelectedUnit();
if (isPressed(BUTTON_UP)) {
// Move the unit up one.
int destY = unit->y() - 1;
return new MoveUnitCommand(unit, unit->x(), destY);
}
if (isPressed(BUTTON_DOWN)) {
// Move the unit down one.
int destY = unit->y() + 1;
return new MoveUnitCommand(unit, unit->x(), destY);
}
// Other moves...
return NULL;
}
可以建立一个链表来记录命令,指定current指针指向当前命令,通过左右移动来实现“Redo”和“Undo”。
命令模式的优点
- 降低系统的耦合度。
- 新的命令可以很容易地加入到系统中。
- 可以比较容易地设计一个命令队列和宏命令(组合命令)。
- 可以方便地实现对请求的Undo和Redo。
命令模式的缺点
- 使用命令模式可能会导致某些系统有过多的具体命令类。