命令模式,也称为动作或者事务模式,很多教材会用饭馆来举例。作为顾客的咱们是命令的下达者,服务员是这个命令的接收者,菜单是这个理论的命令,而厨师是这个命令的执行者。那么,这个模式解决了什么呢?当你要批改菜单的时候,只须要和服务员说就好了,她会转达给厨师,也就是说,咱们实现了顾客和厨师的解耦。也就是调用者与实现者的解耦。当然,很多设计模式能够做到这一点,然而命令模式可能做到的是让一个命令接收者实现多个命令(服务员下单、拿酒水、上菜),或者把一条命令转达给多个实现者(热菜厨师、凉菜厨师、主食徒弟)。这才是命令模式真正施展的中央!!
Gof 类图及解释
GoF 定义:将一个申请封装为一个对象,从而使你可用不同的申请对客户进行参数化;对申请排队或记录申请日志,以及反对可吊销的操作
GoF 类图
代码实现
class Invoker
{
public $command;
public function __construct($command)
{$this->command = $command;}
public function exec()
{$this->command->execute();
}
}
首先咱们定义一个命令的接收者,或者说是命令的请求者更失当。类图中的英文定义这个单词是“期求者”。也就是由它来发动和操作命令。
abstract class Command
{
protected $receiver;
public function __construct(Receiver $receiver)
{$this->receiver = $receiver;}
abstract public function execute();}
class ConcreteCommand extends Command
{public function execute()
{$this->receiver->action();
}
}
接下来是命令,也就是咱们的“菜单”。这个命令的作用是为了定义真正的执行者是谁。
class Receiver
{
public $name;
public function __construct($name)
{$this->name = $name;}
public function action()
{echo $this->name . '命令执行了!', PHP_EOL;}
}
接管者,也就是执行者,真正去执行命令的人。
// 筹备执行者
$receiverA = new Receiver('A');
// 筹备命令
$command = new ConcreteCommand($receiverA);
// 请求者
$invoker = new Invoker($command);
$invoker->exec();
客户端的调用,咱们要分割好执行者也就是挑有好厨子的饭馆(Receiver),而后筹备好命令也就是菜单(Command),最初交给服务员(Invoker)。
- 其实这个饭店的例子曾经十分清晰了,对于命令模式真是完满的解析
- 那说好的能够下多份订单或者给多个厨师呢?别急,上面的代码帮忙咱们解决这个问题
残缺代码:https://github.com/zhangyue0503/designpatterns-php/blob/master/09.command/source/command.php
<?php
class Invoker
{private $command = [];
public function setCommand(Command $command)
{$this->command[] = $command;
}
public function exec()
{if(count($this->command) > 0){foreach ($this->command as $command) {$command->execute();
}
}
}
public function undo()
{if(count($this->command) > 0){foreach ($this->command as $command) {$command->undo();
}
}
}
}
abstract class Command
{
protected $receiver;
protected $state;
protected $name;
public function __construct(Receiver $receiver, $name)
{
$this->receiver = $receiver;
$this->name = $name;
}
abstract public function execute();}
class ConcreteCommand extends Command
{public function execute()
{if (!$this->state || $this->state == 2) {$this->receiver->action();
$this->state = 1;
} else {echo $this->name . '命令正在执行,无奈再次执行了!', PHP_EOL;}
}
public function undo()
{if ($this->state == 1) {$this->receiver->undo();
$this->state = 2;
} else {echo $this->name . '命令未执行,无奈撤销了!', PHP_EOL;}
}
}
class Receiver
{
public $name;
public function __construct($name)
{$this->name = $name;}
public function action()
{echo $this->name . '命令执行了!', PHP_EOL;}
public function undo()
{echo $this->name . '命令撤销了!', PHP_EOL;}
}
// 筹备执行者
$receiverA = new Receiver('A');
$receiverB = new Receiver('B');
$receiverC = new Receiver('C');
// 筹备命令
$commandOne = new ConcreteCommand($receiverA, 'A');
$commandTwo = new ConcreteCommand($receiverA, 'B');
$commandThree = new ConcreteCommand($receiverA, 'C');
// 请求者
$invoker = new Invoker();
$invoker->setCommand($commandOne);
$invoker->setCommand($commandTwo);
$invoker->setCommand($commandThree);
$invoker->exec();
$invoker->undo();
// 新加一个独自的执行者,只执行一个命令
$invokerA = new Invoker();
$invokerA->setCommand($commandOne);
$invokerA->exec();
// 命令 A 曾经执行了,再次执行全副的命令执行者,A 命令的 state 判断无奈失效
$invoker->exec();
- 这一次咱们一次性解决了多个订单、多位厨师的问题,并且还顺便解决了如果下错命令了,进行撤销的问题
- 能够看进去,命令模式将调用操作的对象与晓得如何实现该操作的对象实现理解耦
- 这种多命令多执行者的实现,有点像 组合模式 的实现
- 在这种状况下,减少新的命令,即不会影响执行者,也不会影响客户。当有新的客户须要新的命令时,只须要减少命令和请求者即可。即便有批改的需要,也只是批改请求者。
- Laravel 框架的事件调度机制中,除了观察者模式外,也很显著的能看出命令模式的影子
咱们的手机工厂和餐厅其实并没有什么两样,当咱们须要代工厂来制作手机时,也是先下订单,这个订单就可以看做是命令。在这个订单中,咱们会规定好须要用到的配件,什么型号的 CPU,什么型号的内存,预装什么零碎之类的。而后代工厂的工人们就会依据这个订单来进行生产。在这个过程中,我不必关怀是某一个工人还是一群工人来执行这个订单,我只须要将这个订单交给和咱们对接的人就能够了,而后只管等着手机生产进去进行验收咯!!
残缺代码:https://github.com/zhangyue0503/designpatterns-php/blob/master/09.command/source/command-up.php
实例
短信性能又回来了,咱们发现除了工厂模式外,命令模式貌似也是一种不错的实现形式哦。在这里,咱们仍然是应用那几个短信和推送的接口,话不多说,咱们用命令模式再来实现一个吧。当然,有趣味的敌人能够接着实现咱们的短信撤回性能哈,想想下面的命令勾销是怎么实现的。
短信发送类图
残缺源码:https://github.com/zhangyue0503/designpatterns-php/blob/master/09.command/source/command-message.php
<?php
class SendMsg
{private $command = [];
public function setCommand(Command $command)
{$this->command[] = $command;
}
public function send($msg)
{foreach ($this->command as $command) {$command->execute($msg);
}
}
}
abstract class Command
{protected $receiver = [];
public function setReceiver($receiver)
{$this->receiver[] = $receiver;
}
abstract public function execute($msg);
}
class SendAliYun extends Command
{public function execute($msg)
{foreach ($this->receiver as $receiver) {$receiver->action($msg);
}
}
}
class SendJiGuang extends Command
{public function execute($msg)
{foreach ($this->receiver as $receiver) {$receiver->action($msg);
}
}
}
class SendAliYunMsg
{public function action($msg)
{echo '【阿 X 云短信】发送:' . $msg, PHP_EOL;}
}
class SendAliYunPush
{public function action($msg)
{echo '【阿 X 云推送】发送:' . $msg, PHP_EOL;}
}
class SendJiGuangMsg
{public function action($msg)
{echo '【极 X 短信】发送:' . $msg, PHP_EOL;}
}
class SendJiGuangPush
{public function action($msg)
{echo '【极 X 推送】发送:' . $msg, PHP_EOL;}
}
$aliMsg = new SendAliYunMsg();
$aliPush = new SendAliYunPush();
$jgMsg = new SendJiGuangMsg();
$jgPush = new SendJiGuangPush();
$sendAliYun = new SendAliYun();
$sendAliYun->setReceiver($aliMsg);
$sendAliYun->setReceiver($aliPush);
$sendJiGuang = new SendJiGuang();
$sendAliYun->setReceiver($jgMsg);
$sendAliYun->setReceiver($jgPush);
$sendMsg = new SendMsg();
$sendMsg->setCommand($sendAliYun);
$sendMsg->setCommand($sendJiGuang);
$sendMsg->send('这次要搞个大流动,快来注册吧!!');
阐明
- 在这个例子中,仍然是多命令多执行者的模式
- 能够将这个例子与形象工厂进行比照,同样的性能应用不同的设计模式来实现,然而要留神的是,形象工厂更多的是为了生产对象返回对象,而命令模式则是一种行为的抉择
- 咱们能够看出命令模式非常适合造成命令队列,多命令让命令能够一条一条执行上来
- 它容许接管的一方决定是否要否决申请,Receiver 做为实现者领有更多的话语权
下期看点
命令模式说了很多,不过的确是很好玩的一个模式,下一场咱们劳动劳动,来一个比较简单的模式,甚至是比咱们的 简略工厂 还要简略的一个模式,那就是 策略模式
各自媒体平台均可搜寻【硬核项目经理】