定义
将“请求”封装成命令对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操做。
适用场景
当需要将发出请求的对象和执行请求的对象解耦的时候,使用命令模式。比如我们有一个遥控器,需要控制客厅灯的点亮和关闭,电视的打开和关闭,风扇的关闭等,这是需要遥控器判断当前的对象类型,并执行不同的操作,此时就可以使用命令模式将遥控器和电灯,电视,风扇解耦
举个例子
假设有一下三个类,分别表示电灯,电风扇和电视:
/**
* Class Light
*/
class Light
{
public function on()
{
}
public function off()
{
}
}
/**
* Class ElectricFan
*/
class ElectricFan
{
public function on()
{
}
public function off()
{
}
public function speedHigh()
{
}
public function speedLow()
{
}
}
/**
* Class Television
*/
class Television
{
public function on()
{
}
public function off()
{
}
public function voiceHigh()
{
}
public function voiceLow()
{
}
}
按照一般的写法,当我们需要在实现遥控器类的时候可能会这样写
/**
* Class RemoteControl
*/
class RemoteControl
{
private $light;
private $electricFan;
private $television;
public function __construct(Light $light, ElectricFan $electricFan, Television $television)
{
$this->light = $light;
$this->electricFan = $electricFan;
$this->television = $television;
}
public function lightOn()
{
$this->light->on();
}
public function lightOff()
{
$this->light->Off();
}
// other function
...
}
可以看出来,当需要让遥控器遥控另一个电器,则还需要修改遥控器类的实现,是不是很麻烦。
使用命令模式实现遥控器类
既然上面的写法比较麻烦,每次都需要修改控制器的代码,那么换个思路把每一个命令作为一个对象来实现。仍然使用上面的三个被遥控的电器类,但这次我们把没一个命令作为一个对象
interface Command
{
public function execute();
}
/**
* 开灯
* Class LightOnCommand
* @package App
*/
class LightOnCommand implements Command
{
private $light;
public function __construct(Light $light)
{
$this->light = $light;
}
public function execute()
{
$this->light->on();
}
}
/**
* 关灯
* Class LightOffCommand
* @package App
*/
class LightOffCommand implements Command
{
private $light;
public function __construct(Light $light)
{
$this->light = $light;
}
public function execute()
{
$this->light->off();
}
}
/**
* 开风扇
* Class ElectricFanOnCommand
* @package App
*/
class ElectricFanOnCommand implements Command
{
private $electricFan;
public function __construct(ElectricFan $electricFan)
{
$this->electricFan = $electricFan;
}
public function execute()
{
$this->electricFan->on();
}
}
/**
* 关风扇
* Class ElectricFanOffCommand
* @package App
*/
class ElectricFanOffCommand implements Command
{
private $electricFan;
public function __construct(ElectricFan $electricFan)
{
$this->electricFan = $electricFan;
}
public function execute()
{
$this->electricFan->off();
}
}
/**
* 开电视
* Class ElectricFanOnCommand
* @package App
*/
class TelevisionOnCommand implements Command
{
private $television;
public function __construct(Television $television)
{
$this->electricFan = $television;
}
public function execute()
{
$this->television->on();
}
}
/**
* 关电视
* Class ElectricFanOffCommand
* @package App
*/
class TelevisionOffCommand implements Command
{
private $television;
public function __construct(Television $television)
{
$this->electricFan = $television;
}
public function execute()
{
$this->television->off();
}
}
// 其他的命令类似
这样我们就可以这样实现遥控器类
class RemoteControl
{
private $onCommand = [];
private $offCommand =[];
/**
* RemoteControl constructor.
* @param array $onCommand Command 数组
* @param array $offCommand Command 数组
*/
public function __construct()
{
}
public function setCommand(int $position, Command $onCommand, Command $offCommand)
{
$this->onCommand[$position] = $onCommand;
$this->offCommand[$position] = $offCommand;
}
/**
* 打开
*/
public function pressOn()
{
foreach ($this->onCommand as $command) {
$command->execute();
}
}
/**
* 关闭
*/
public function pressOff()
{
foreach ($this->offCommand as $command) {
$command->execute();
}
}
}
现在来实现一下打开和关闭电灯
$light = new Light();
$lightOnCommand = new LightOnCommand($light);
$lightOffCommand = new LightOffCommand($light);
$remoteControl = new RemoteControl();
$remoteControl->setCommand(0, $lightOnCommand, $lightOffCommand);
$remoteControl->pressOn();
$remoteControl->pressOff();
当需要遥控器控制电视的时候:
$remoteControl = new RemoteControl();
// 电灯
$light = new Light();
$lightOnCommand = new LightOnCommand($light);
$lightOffCommand = new LightOffCommand($light);
$remoteControl->setCommand(0, $lightOnCommand, $lightOffCommand);
// 电视
$television = new Television();
$televisionOnCommand = new TelevisionOnCommand($television);
$televisionOffCommand = new TelevisionOffCommand($television);
$remoteControl->setCommand(1, $televisionOnCommand, $televisionOffCommand);
// 打开
$remoteControl->pressOn();
// 关闭
$remoteControl->pressOff();
这样遥控器的代码不用做任何修改,就可以添加控制更多的电器,降低了两者的耦合。
用途
命令模式可以用于 队列请求, 比如有一个工作队列,在一端添加命令,另一端取出命令调用它的execute()方法,执行完成后再去取下一个命令
总结
- 命令模式将发出请求的对象和执行请求的对象解耦
- 在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者的一个或一组动作
- 调用者可以接受命令当作参数,甚至在运行时动态的进行