1. 模式定义
命令模式(Command)将请求封装成对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
示例说明:
我们有一个调用者类 Invoker
和一个接收请求执行的类 Receiver
,在两者之间我们使用实现CommandInterface
接口的 HelloCommand
命令类的execute()
方法来托管请求调用方法(HelloCommand
类的构造方法依赖注入了接受类Receiver
,execute()
方法中调用了接受执行类的执行方法)。
这样,调用者 Invoker
只知道调用命令类的 execute()
方法来处理客户端请求(Invoker
中的run()
方法直接调用 HelloCommand
类中的execute()
方法),从而实现接收者 Receiver 与调用者 Invoker 的解耦。
Laravel 中的 Artisan 命令就使用了命令模式。
2. UML类图
3. 示例代码
CommandInterface.php
<?php
namespace DesignPattern\Behavioral\Command;
/**
* CommandInterface
*/
interface CommandInterface
{
/**
* 在命令模式中这是最重要的方法,
* Receiver在构造函数中传入.
*/
public function execute();
}
HelloCommand.php
<?php
namespace DesignPattern\Behavioral\Command;
/**
* 这是一个调用Receiver的print方法的命令实现类,
* 但是对于调用者而言,只知道调用命令类的execute方法
*/
class HelloCommand implements CommandInterface
{
/**
* @var Receiver
*/
protected $output;
/**
* 每一个具体的命令基于不同的Receiver
* 它们可以是一个、多个,甚至完全没有Receiver
*
* @param Receiver $console
*/
public function __construct(Receiver $console)
{
$this->output = $console;
}
/**
* 执行命令(并输出 Hello World)
*/
public function execute()
{
// 没有Receiver的时候完全通过命令类来实现功能
$this->output->write('Hello World'); //不同的命令实例,传入的参数可不同
}
}
调用者类 Invoker.php
<?php
namespace DesignPattern\Behavioral\Command;
/**
* Invoker类
*/
class Invoker
{
/**
* @var CommandInterface
*/
protected $command;
/**
* 在调用者中我们通常可以找到这种订阅命令的方法
*
* @param CommandInterface $cmd
*/
public function setCommand(CommandInterface $cmd)
{
$this->command = $cmd;
}
/**
* 执行命令
*/
public function run()
{
$this->command->execute();
}
}
接受执行类 Receiver.php
<?php
namespace DesignPattern\Behavioral\Command;
/**
* Receiver 接受调用请求类
*/
class Receiver
{
/**
* @param string $str
*/
public function write($str)
{
echo $str;
}
}
单元测试
<?php
namespace DesignPattern\Tests;
use DesignPattern\Behavioral\Command\HelloCommand;
use DesignPattern\Behavioral\Command\Invoker;
use DesignPattern\Behavioral\Command\Receiver;
use PHPUnit\Framework\TestCase;
/**
* 命令配器模式
* Class CommandTest
* @package Creational\Singleton\Tests
*/
class CommandTest extends TestCase
{
/**
* @var Invoker
*/
protected $invoker;
/**
* @var Receiver
*/
protected $receiver;
protected function setUp(): void
{
$this->invoker = new Invoker();
$this->receiver = new Receiver();
}
public function testInvocation()
{
$this->invoker->setCommand(new HelloCommand($this->receiver));
$this->expectOutputString('Hello World');
$this->invoker->run();
}
}
参考文档:https://laravelacademy.org/post/2871
教程源码:https://github.com/SylviaYuan1995/DesignPatternDemo