PHP设计模式之命令模式示例详解

前言

命令模式,也称为动作或者事务模式,很多教材会用饭馆来举例。作为顾客的我们是命令的下达者,服务员是这个命令的接收者,菜单是这个实际的命令,而厨师是这个命令的执行者。那么,这个模式解决了什么呢?当你要修改菜单的时候,只需要和服务员说就好了,她会转达给厨师,也就是说,我们实现了顾客和厨师的解耦。也就是调用者与实现者的解耦。当然,很多设计模式可以做到这一点,但是命令模式能够做到的是让一个命令接收者实现多个命令(服务员下单、拿酒水、上菜),或者把一条命令转达给多个实现者(热菜厨师、凉菜厨师、主食师傅)。这才是命令模式真正发挥的地方!!

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做为实现者拥有更多的话语权

到此这篇关于PHP设计模式之命令模式的文章就介绍到这了,更多相关PHP设计模式之命令模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-12-20

老生常谈PHP面向对象之命令模式(必看篇)

这个模式主要由 命令类.用户请求数据类.业务逻辑类.命令类工厂类及调用类构成,各个类的作用概括如下: 1.命令类:调用用户请求数据类和业务逻辑类: 2.用户请求数据类:获取用户请求数据及保存后台处理后返回的结果: 3.业务逻辑类:如以下的示例中验证用户登陆信息是否正确的功能等: 4.命令工厂类(我自己取的名字,哈哈):生成命令类的实例,这个类第一次看的时候我觉得有点屌,当然看了几遍了还是觉得很屌 :): 5.调用类:调用命令类,生成视图: 直接看代码: //命令类 abstract class

PHP设计模式之命令模式的深入解析

命令模式(Command),命令模式是封装一个通用操作的机制. 如果你熟悉C或PHP,你可能已经遇到过Command,它相当于程序中的:回调(callback).回调通常使用一个函数指针或数据结构如PHP中的字符串和数组实现,Command是在一个方法调用之上的抽象,它吸收了所有面向对象的好处:合成.继承和处理. 例如,<设计模式>一书推荐使用Command存储用户行为链,以支持撤销和重做操作. 注意PHP 5.3函数编程能力(闭包)可以被当做Command模式的一个本地实现,但为每一个命令层

php设计模式之命令模式的应用详解

命令模式:将一个请求封装为一个对象,从而你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销操作. 命令类:1.命令角色:声明了一个给所有具体命令类的抽象接口.这是一个抽象角色.2.具体命令角色:定义一个接受者和行为之间的弱耦合:实现execute方法,负责调用接受的相应操作.execute()方法通常叫做执行方法3.客户角色:创建一个具体命令对象并确定其接受者.4.请求者角色:负责调用命令对象执行请求,相关的方法叫做行动方法.5.接受者角色:负责具体实施和执行一个请求.作

php设计模式之命令模式使用示例

命令类:1.命令角色:声明了一个给所有具体命令类的抽象接口.这是一个抽象角色.2.具体命令角色:定义一个接受者和行为之间的弱耦合:实现execute方法,负责调用接受的相应操作.execute()方法通常叫做执行方法3.客户角色:创建一个具体命令对象并确定其接受者.4.请求者角色:负责调用命令对象执行请求,相关的方法叫做行动方法.5.接受者角色:负责具体实施和执行一个请求.作用:1.抽象出待执行的动作以参数化对象.2.在不同的时刻指定.排列和执行请求.3.支持取消操作4.支持修改日志 复制代码

php设计模式 Command(命令模式)

<?php /** * 命令模式 * * 将一个请求封装为一个对象从而使你可用不同的请求对客户进行参数化,对请求排除或记录请求日志,以及支持可取消的操作 */ interface Command { public function execute(); } class Invoker { private $_command = array(); public function setCommand($command) { $this->_command[] = $command; } publ

学习php设计模式 php实现命令模式(command)

一.意图 将一个请求封装为一个对象,从而使用你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤消的操作. 可变的方面是:何时,怎样满足一个请求 命令模式是对命令的封装.命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象. 请求的一方发出请求要求执行一个操作:接收的一方收到请求,并执行操作.命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行.何时被执行,以及是怎么被执行的. 二.

JavaScript设计模式经典之命令模式

一.命令模式概念 命令模式(Command)的定义是:用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行.也就是说该模式旨在将函数的调用.请求和操作封装成一个单一的对象,然后对这个对象进行一些列的处理.他也可以用来消除调用操作的对象和实现操作的对象之间的耦合.这为各种具体的类的更换带来了极大的灵活性. 二.命令模式的作用和注意事项 模式作用: 1.将函数的封装.请求.调用结合为一体 2.调用具体的函数解耦命令对象与接收对象 3.提高程序模块化的灵活性 注意事项:

学习php设计模式 php实现备忘录模式(Memento)

一.意图 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样可以在以后把该对象的状态恢复到之前保存的状态. 二.备忘录模式结构图 三.备忘录模式中主要角色 1.备忘录(Memento)角色: 存储发起人(Originator)对象的内部状态,而发起人根据需要决定备忘录存储发起人的哪些内部状态. 备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取. 2.发起人(Originator)角色: 创建一个含有当前的内部状态的备忘录对象 使用备忘录

解析Java设计模式编程中命令模式的使用

定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 类型:行为类模式 类图: 命令模式的结构         顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构: Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令. ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现. Client类:最终的客户

快速学习C# 设计模式之职责链模式

职责链模式简介及UML 职责链也叫责任链,他是一种行为型模式,它为请求创建了一个接收请求者对象的链,并将请求沿着这条链传递到目标对象去处理. 该模式最简单的实现方式就是运用里氏替换原则,对每个职责所持有的对象进行抽象,并使得每个职责对象都拥有共同的父类,通过对外提供出具有一般意义的接口. 范例 该范例,是我在对微服务中,服务发现的容错性进行处理的一种处理方案,考虑到服务发现过程中,如果注册中心宕机,那么可以使用本地文件存放的临时性信息,如果本地文件不存在,那么就直接用内容中存放的信息.在整个流程

详解Java设计模式编程中命令模式的项目结构实现

正论: 命令模式把一个请求或者操作封装到一个对象中.命令模式运行系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 通俗: 其实很好理解.命令模式,关心的就是命令(或者称为操作).打个比方.在一个公司里面,整个运作就像一个系统.某个boss发布了一个命令,中层领导接到这个命令,然后指派给具体负责这个员工.整个流程很清晰吧.有一个需求,如何将这个流程固定下来,形成一个系统.我们只要抓住了重点:命令.将它抽取出来,其他的都迎刃而解了.抽取出命令,封装成一个独

实例讲解设计模式中的命令模式在iOS App开发中的运用

命令模式封装一个请求或行为作为一个对象.封装的请求比原的更加灵活,可以在对象之间传递,储存,动态修改,或放入一个队列. 那么让我们简要的说一下命令模式的特点. 它能比较容易地设计一个命令队列: 在需要的情况下,可以较容易地将命令记入日志: 允许接收请求地一方决定是否要否决请求: 可以容易地实现对请求地撤销和重做: 由于加进新地具体命令类不影响其他的类,因此增加新的具体命令类很容易: 把请求一个操作的对象与知道怎么执行一个操作的对象分隔开. 下面给出基本的类结构图: 上面这张图是命令模式的类结构的

C#命令模式(Command Pattern)实例教程

本文以实例形式讲述了C#命令模式的实现方法,分享给大家供大家参考.具体实现方法如下: 现假设想让遥控器控制电灯的开关.电视机的开关和切换,该如何做? 所有的开.关.切换都是遥控器发出的指令,把这些指令统一抽象成一个接口. public interface IControl { void Execute(); } 把电灯.电视机抽象成类. public class Tv { public void TurnOn() { Console.WriteLine("电视机打开了"); } pub

学习JavaScript设计模式之装饰者模式

有时我们不希望某个类天生就非常庞大,一次性包含许多职责.那么我们就可以使用装饰着模式. 装饰着模式可以动态地给某个对象添加一些额外的职责,从而不影响这个类中派生的其他对象. 装饰着模式将一个对象嵌入另一个对象之中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链. 一.不改动原函数的情况下,给该函数添加些额外的功能 1. 保存原引用 window.onload = function() { console.log(1); }; var _onload = window.onload ||

学习JavaScript设计模式之责任链模式

一.定义 责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 二.示例 假设这么一个场景: 我们负责一个售卖手机的电商网站,经过分别缴纳500元定金和200元定金的两轮预定后,到了正式购买阶段.针对预定用户实行优惠,支付过500元定金的用户会收到100元的商城优惠券,支付过200元定金的用户会收到50元的商城优惠券,没有支付定金的用户归为普通购买,且在库存有限的情况下不一定保证买到. /*