命令模式
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作。
- 命令是具现化(实例化,对象化的方法调用
- 命令模式是一种回调的面向对象实现
- 命令模式的优点有:对类间解耦、可扩展性强、易于命令的组合维护、易于与其他模式结合,而缺点是会导致类的膨胀
- 命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开
- 命令模式的优点有:对类间解耦、可扩展性强、易于命令的组合维护、易于与其他模式结合,而缺点是会导致类的膨胀
- 把控制角色的命令变为第一公民对象,去除直接方法调用中严厉的束缚。 将其视为命令队列,或者是命令流.一些代码(输入控制器或者AI)产生一系列命令放入流中。 另一些代码(调度器或者角色自身)调用并消耗命令。 通过在中间加入队列,我们解耦了消费者和生产者
代码
根据用户输入做表现的简单实现
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();
}
为了允许玩家配置按键的功能
class Command
{
public:
virtual ~Command() {}
virtual void execute() = 0;
};
为不同的游戏行为定义相应的子类:
class JumpCommand : public Command
{
public:
virtual void execute() { jump(); }
};
class FireCommand : public Command
{
public:
virtual void execute() { fireGun(); }
};
在代码的输入处理部分,为每个按键存储一个指向命令的指针。
class InputHandler
{
public:
void handleInput();
// 绑定命令的方法……
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();
}
上述就是命令模式的简短介绍,但有局限性,限制了只能玩家角色跳跃,修改为
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_;
// 没有按下任何按键,就什么也不做
return NULL;
}
Command* command = inputHandler.handleInput();
if (command)
{
command->execute(actor);
}
这里不能立即执行,因为还不知道哪个角色会传进来。 这里我们享受了命令是具体调用的好处——延迟到调用执行时再知道。
使用场合
- 命令模式很适合实现诸如撤消,重做,回放,时间倒流之类的功能。
- 基于命令模式实现录像与回放等功能,也就是执行并解析一系列经过预录制的序列化后的各玩家操作的有序命令集合