将请求封装为对象,从而使你可以将具有不同请求,队列或日志等请求的其他对象参数化,并支持可撤消的操作。
战斗是为了赢,没有战斗能够在没有一个好的计划或策略来赢得。多数失败的战斗是指挥官在没有任何计划的情况下命令其士兵进入战场的战斗,或者更糟的是,在战斗中没有能力指挥其士兵的战斗。结果是每个人都在为自己而战,而不是团体为团体的互相保护而遭受破坏。精心策划的计划是完美的战斗计划,每个人都知道自己的位置和在正确的时间做什么。您是否想过如何制定战斗计划?如果我们在代码中模拟了它,你认为它会变得复杂还是有趣?事实证明,这很有趣,如果在战斗中使用编程,那么赢得一场又一场的战斗将是最简单的事情。
在本章中,我们将了解如何使用“命令模式(Command Pattern
)”进行编程来制定战斗计划。
在典型的命令模式(Command Pattern
)中,包含有五个元素。它们分别是命令接口(command interface
),客户端(client
),接收者(receiver
),调用者(invoker
)和具体命令(concrete command
)。
我们将在接下来详细解释这5个元素。
但首先,让我们完成战斗计划的代码:
interface Command
{
public function execute();
}
class Gunner
{
public function fire()
{
echo 'Fire in the hole';
}
}
class GunnerFireCommand implements Command
{
private $_gunner = null;
public function _construct(Gunner $gunner)
{
$this->_gunner = $gunner;
}
public function execute()
{
$this->_gunner->fire();
}
}
class BattlePlan
{
public $commandsWithCodeNames = array();
public function setCommand($codeName, Command $command)
{
$this->commandsWithCodeNames[$codeName] = $command;
}
public function executeCommand($codeName)
{
$command = $this->commandsWithCodeNames[$codeName];
$command->execute();
}
}
// Commander's code
$peter = new Gunner();
$gunnerFireCommand = new GunnerFireCommand($peter);
$planA = new BattlePlan();
$planA->setCommand('planA', $gunnerFireCommand);
现在我们来解释下上面的例子,我们是怎么使用命令行模式中的四个元素:
让我们重新编写代码。
Command interface
):它定义了一个通用方法,因此所有具体的命令类都必须实现它。Client
):它负责创建具体的命令对象和接收者对象。在我们的这个战斗示例中,客户端就是指挥官。Invoker
):它包含具体的命令对象,并在某个时间点调用execute()
方法。请注意,它通过其接口(Command接口)保存具体的命令对象,它实际上并不知道其保存的对象的确切类型(从编程到抽象),因此仅向其公开具体命令对象的execute()
方法。在我们的战场示例中,战斗计划是调用方。Receiver
):它执行特定的任务。在我们的战斗示例中,枪手是接收者。Concrete Command
):此类实现Command
接口并实现execute()
方法。其目的是将请求封装为一个对象。为此,它将一组接收者的动作绑定在一起,并将execute()
方法暴露给外部。在我们的战场示例中,GunnerFireCommand
是一个具体的命令。在我们的战场示例中,通过使用命令模式,我们将一个请求(Gunner
类的fire()
方法)封装为一个对象(GunnerFireCommand
)。它使我们可以对具有不同请求,队列或日志请求的其他对象进行参数化(指挥官可以使用不同的命令制定不同的作战计划,例如,他可以发出命令来请求狙击手和炮手一起进攻)。