PHP设计模式的命令模式,也称为动作或事务模式,很多教科书都会以饭店为例。我们顾客是下单的人,服务员是下单的人,菜单是实际点的菜,厨师是下单的执行者。那么,这个模式解决了什么问题呢?当你想修改菜单时,只需要告诉服务员,她就会转达给厨师。也就是说,我们实现了顾客和厨师的解耦。即调用者和实现者的解耦。当然,很多设计模式都可以做到这一点,但是命令模式可以做到的是让一个命令接收者执行多个命令(服务员下单、拿饮料、上菜),或者将一个命令传达给多个执行者(热菜厨师)、凉菜师傅、主食师傅)。这就是命令模式真正发挥作用的地方!!Gof类图及解释GoF定义:将一个请求封装成一个对象,这样就可以对不同请求的客户进行参数化;队列请求或记录请求日志,并支持可撤销操作。GoF类图代码实现classInvoker{public$command;公共函数__construct($command){$this->command=$command;}publicfunctionexec(){$this->command->execute();}}首先我们定义一个命令接收者,或者更恰当地说,命令的请求者。类图中单词的英文定义是“invoker”。即发起和运行命令。抽象类Command{protected$receiver;公共函数__construct(Receiver$receiver){$this->receiver=$receiver;}abstractpublicfunctionexecute();}classConcreteCommandextendsCommand{publicfunctionexecute(){$this->receiver->action();}}接下来是命令,这是我们的“菜单”。这个命令的作用是定义真正的执行者是谁。类接收器{公共$name;公共函数__construct($name){$this->name=$name;}publicfunctionaction(){echo$this->name.'命令执行!',PHP_EOL;}}Taker,即执行人,是真正执行订单的人。//准备执行器$receiverA=newReceiver('A');//准备命令$command=newConcreteCommand($receiverA);//请求者$invoker=newInvoker($command);$调用者->exec();对于client的调用,我们需要联系executor,也就是挑一个有好的厨师的餐厅(Receiver),然后prepareorder,也就是menu(Command),最后交给服务员(调用者)。其实这个餐厅的例子就很清楚了。是对命令模式的完美解析。如何下多个订单或给多个厨师?别急,下面的代码帮我们解决了这个问题完整代码:https://github.com/zhangyue0503/designpatterns-php/blob/master/09.command/source/command.phpcommand[]=$command;}publicfunctionexec(){if(count($this->command)>0){foreach($this->commandas$command){$command->execute();}}}publicfunctionundo(){if(count($this->command)>0){foreach($this->commandas$command){$command->undo();}}}}抽象类命令{protected$receiver;受保护的$状态;受保护的$名称;公共函数__construct(Receiver$receiver,$name){$this->receiver=$receiver;$this->name=$name;}抽象公共函数execute();}classConcreteCommandextendsCommand{publicfunctionexecute(){if(!$this->state||$this->state==2){$this->receiver->action();$this->state=1;}else{echo$this->name.'命令正在执行中,不能再执行!',PHP_EOL;}}publicfunctionundo(){if($this->state==1){$this->receiver->undo();$this->state=2;}else{echo$this->name.'命令尚未执行,无法撤消!',PHP_EOL;}}}classReceiver{public$name;公共函数__construct($name){$this->name=$name;}publicfunctionaction(){echo$this->name.'命令执行!',PHP_EOL;}publicfunctionundo(){echo$this->name.'命令撤销!',PHP_EOL;}}//准备执行器$receiverA=newReceiver('A');$receiverB=新接收者('B');$receiverC=newReceiver('C');//准备命令$commandOne=newConcreteCommand($receiverA,'A');$commandTwo=newConcreteCommand($receiverA,'B');$commandThree=newConcreteCommand($receiverA,'C');//请求者$invoker=newInvoker();$invoker->setCommand($commandOne);$invoker->setCommand($commandTwo);$invoker->setCommand($commandThree);$invoker->exec();$invoker->undo();//添加单个执行器只执行一个命令$invokerA=newInvoker();$invokerA->setCommand($commandOne);$invokerA->exec();//命令A已经执行完毕,重新执行所有的命令执行器,A命令的状态判断不能生效$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.phpcommand[]=$command;}publicfunctionsend($msg){foreach($this->commandas$command){$command->execute($msg);}}}抽象类命令{protected$receiver=[];公共功能setReceiver($receiver){$this->receiver[]=$receiver;}abstractpublicfunctionexecute($msg);}classSendAliYunextendsCommand{publicfunctionexecute($msg){foreach($this->receiveras$receiver){$receiver->action($msg);}}}}classSendJiGuangextendsCommand{publicfunctionexecute($msg){foreach($this->receiveras$receiver){$receiver->action($msg);}}}}classSendAliYunMsg{publicfunctionaction($msg){echo'【阿里云短信】发送:'.$味精,PHP_EOL;}}classSendAliYunPush{publicfunctionaction($msg){echo'【阿里云推送】发送:'.$味精,PHP_EOL;}}classSendJiGuangMsg{publicfunctionaction($msg){echo'【极X短信】发送:'.$味精,PHP_EOL;}}classSendJiGuangPush{publicfunctionaction($msg){echo'【极速推送】发送:'.$味精,PHP_EOL;}}$aliMsg=newSendAliYunMsg();$aliPush=newSendAliYunPush();$jgMsg=newSendJiGuangMsg();$jgPush=newSendJiGuangPush();$sendAliYun=newSendAliYun();$sendAliYun->setReceiver($aliMsg);$sendAliYun->setReceiver($aliPush);$sendJiGuang=newSendJiGuang();$sendAliYun->setReceiver($jgMsg);$sendAliYun->setReceiver($jgPush);$sendMsg=newSendMsg();$sendMsg->setCommand($sendAliYun);$sendMsg->setCommand($sendJiGuang);$sendMsg->send('这次有大活动,快来报名');说明在本例中,依然是多命令多执行器模式。您可以将此示例与抽象工厂进行比较。同样的功能使用不同的设计模式来实现,但是需要注意的是,抽象工厂更多的是返回对象来生产对象,命令模式是一种行为选择。我们可以看到命令模式非常适合组成一个命令队列。多个命令允许一条一条地执行命令。它允许接收方决定是否否决该请求。Receiver作为一个实现者,有更多关于命令模式的内容我在下期讲了很多,不过确实是一个很好玩的模式。在接下来的比赛中,我们会休息一下,来一个更简单的模式,甚至比我们的简单工厂更简单的模式。即攻略模式在各媒体平台都可以搜索【硬核项目经理】
