封裝調用與解耦
我們有一個調用器和一個接受器。此模式使用一個【Command】來委托接收器的方法調用,并呈現相同的方法【execute】。調用器只知道調用【execute】來處理客戶機的【Command】。從而實現了接收器和調用者的解耦。
此模式的另一個方面是undo(),它取消execute()方法。命令行也可以通過聚合來組合更復雜的命令,使用最小的復制-粘貼,并依賴組合而不是繼承。
Command.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command; interface Command { /** * this is the most important method in the Command pattern, * The Receiver goes in the constructor. */ public function execute(); }
UndoableCommand.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command; interface UndoableCommand extends Command { /** * This method is used to undo change made by command execution */ public function undo(); }
HelloCommand.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command; /** * This concrete command calls "print" on the Receiver, but an external * invoker just knows that it can call "execute" */ class HelloCommand implements Command { /** * Each concrete command is built with different receivers. * There can be one, many or completely no receivers, but there can be other commands in the parameters */ public function __construct(private Receiver $output) { } /** * execute and output "Hello World". */ public function execute() { // sometimes, there is no receiver and this is the command which does all the work $this->output->write('Hello World'); } }
AddMessageDateCommand.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command; /** * This concrete command tweaks receiver to add current date to messages * invoker just knows that it can call "execute" */ class AddMessageDateCommand implements UndoableCommand { /** * Each concrete command is built with different receivers. * There can be one, many or completely no receivers, but there can be other commands in the parameters. */ public function __construct(private Receiver $output) { } /** * Execute and make receiver to enable displaying messages date. */ public function execute() { // sometimes, there is no receiver and this is the command which // does all the work $this->output->enableDate(); } /** * Undo the command and make receiver to disable displaying messages date. */ public function undo() { // sometimes, there is no receiver and this is the command which // does all the work $this->output->disableDate(); } }
Receiver.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command; /** * Receiver is a specific service with its own contract and can be only concrete. */ class Receiver { private bool $enableDate = false; /** * @var string[] */ private array $output = []; public function write(string $str) { if ($this->enableDate) { $str .= ' [' . date('Y-m-d') . ']'; } $this->output[] = $str; } public function getOutput(): string { return join("\n", $this->output); } /** * Enable receiver to display message date */ public function enableDate() { $this->enableDate = true; } /** * Disable receiver to display message date */ public function disableDate() { $this->enableDate = false; } }
Invoker.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command; /** * Invoker is using the command given to it. * Example : an Application in SF2. */ class Invoker { private Command $command; /** * in the invoker we find this kind of method for subscribing the command * There can be also a stack, a list, a fixed set ... */ public function setCommand(Command $cmd) { $this->command = $cmd; } /** * executes the command; the invoker is the same whatever is the command */ public function run() { $this->command->execute(); } }
Tests/CommandTest.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command\Tests; use DesignPatterns\Behavioral\Command\HelloCommand; use DesignPatterns\Behavioral\Command\Invoker; use DesignPatterns\Behavioral\Command\Receiver; use PHPUnit\Framework\TestCase; class CommandTest extends TestCase { public function testInvocation() { $invoker = new Invoker(); $receiver = new Receiver(); $invoker->setCommand(new HelloCommand($receiver)); $invoker->run(); $this->assertSame('Hello World', $receiver->getOutput()); } }
Tests/UndoableCommandTest.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Command\Tests; use DesignPatterns\Behavioral\Command\AddMessageDateCommand; use DesignPatterns\Behavioral\Command\HelloCommand; use DesignPatterns\Behavioral\Command\Invoker; use DesignPatterns\Behavioral\Command\Receiver; use PHPUnit\Framework\TestCase; class UndoableCommandTest extends TestCase { public function testInvocation() { $invoker = new Invoker(); $receiver = new Receiver(); $invoker->setCommand(new HelloCommand($receiver)); $invoker->run(); $this->assertSame('Hello World', $receiver->getOutput()); $messageDateCommand = new AddMessageDateCommand($receiver); $messageDateCommand->execute(); $invoker->run(); $this->assertSame("Hello World\nHello World [" . date('Y-m-d') . ']', $receiver->getOutput()); $messageDateCommand->undo(); $invoker->run(); $this->assertSame("Hello World\nHello World [" . date('Y-m-d') . "]\nHello World", $receiver->getOutput()); } }
更多建議: