3.2. Command

3.2.1. Purpose

To encapsulate invocation and decoupling.

We have an Invoker and a Receiver. This pattern uses a “Command” todelegate the method call against the Receiver and presents the samemethod “execute”. Therefore, the Invoker just knows to call “execute” toprocess the Command of the client. The Receiver is decoupled from theInvoker.

The second aspect of this pattern is the undo(), which undoes the methodexecute(). Command can also be aggregated to combine more complexcommands with minimum copy-paste and relying on composition overinheritance.

3.2.2. Examples

  • A text editor : all events are Command which can be undone, stackedand saved.
  • Symfony2: SF2 Commands that can be run from the CLI are built withjust the Command pattern in mind
  • big CLI tools use subcommands to distribute various tasks and packthem in “modules”, each of these can be implemented with the Commandpattern (e.g. vagrant)

3.2.3. UML Diagram

Alt Command UML Diagram

3.2.4. Code

You can also find this code on GitHub

CommandInterface.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Command;
  4.  
  5. interface CommandInterface
  6. {
  7. /**
  8. * this is the most important method in the Command pattern,
  9. * The Receiver goes in the constructor.
  10. */
  11. public function execute();
  12. }

HelloCommand.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Command;
  4.  
  5. /**
  6. * This concrete command calls "print" on the Receiver, but an external
  7. * invoker just knows that it can call "execute"
  8. */
  9. class HelloCommand implements CommandInterface
  10. {
  11. /**
  12. * @var Receiver
  13. */
  14. private $output;
  15.  
  16. /**
  17. * Each concrete command is built with different receivers.
  18. * There can be one, many or completely no receivers, but there can be other commands in the parameters
  19. *
  20. * @param Receiver $console
  21. */
  22. public function __construct(Receiver $console)
  23. {
  24. $this->output = $console;
  25. }
  26.  
  27. /**
  28. * execute and output "Hello World".
  29. */
  30. public function execute()
  31. {
  32. // sometimes, there is no receiver and this is the command which does all the work
  33. $this->output->write('Hello World');
  34. }
  35. }

Receiver.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Command;
  4.  
  5. /**
  6. * Receiver is specific service with its own contract and can be only concrete.
  7. */
  8. class Receiver
  9. {
  10. /**
  11. * @var bool
  12. */
  13. private $enableDate = false;
  14.  
  15. /**
  16. * @var string[]
  17. */
  18. private $output = [];
  19.  
  20. /**
  21. * @param string $str
  22. */
  23. public function write(string $str)
  24. {
  25. if ($this->enableDate) {
  26. $str .= ' ['.date('Y-m-d').']';
  27. }
  28.  
  29. $this->output[] = $str;
  30. }
  31.  
  32. public function getOutput(): string
  33. {
  34. return join("\n", $this->output);
  35. }
  36.  
  37. /**
  38. * Enable receiver to display message date
  39. */
  40. public function enableDate()
  41. {
  42. $this->enableDate = true;
  43. }
  44.  
  45. /**
  46. * Disable receiver to display message date
  47. */
  48. public function disableDate()
  49. {
  50. $this->enableDate = false;
  51. }
  52. }

Invoker.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Command;
  4.  
  5. /**
  6. * Invoker is using the command given to it.
  7. * Example : an Application in SF2.
  8. */
  9. class Invoker
  10. {
  11. /**
  12. * @var CommandInterface
  13. */
  14. private $command;
  15.  
  16. /**
  17. * in the invoker we find this kind of method for subscribing the command
  18. * There can be also a stack, a list, a fixed set ...
  19. *
  20. * @param CommandInterface $cmd
  21. */
  22. public function setCommand(CommandInterface $cmd)
  23. {
  24. $this->command = $cmd;
  25. }
  26.  
  27. /**
  28. * executes the command; the invoker is the same whatever is the command
  29. */
  30. public function run()
  31. {
  32. $this->command->execute();
  33. }
  34. }

3.2.5. Test

Tests/CommandTest.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\Command\Tests;
  4.  
  5. use DesignPatterns\Behavioral\Command\HelloCommand;
  6. use DesignPatterns\Behavioral\Command\Invoker;
  7. use DesignPatterns\Behavioral\Command\Receiver;
  8. use PHPUnit\Framework\TestCase;
  9.  
  10. class CommandTest extends TestCase
  11. {
  12. public function testInvocation()
  13. {
  14. $invoker = new Invoker();
  15. $receiver = new Receiver();
  16.  
  17. $invoker->setCommand(new HelloCommand($receiver));
  18. $invoker->run();
  19. $this->assertEquals('Hello World', $receiver->getOutput());
  20. }
  21. }