3.9. State

3.9.1. Purpose

Encapsulate varying behavior for the same routine based on an object’sstate. This can be a cleaner way for an object to change its behavior atruntime without resorting to large monolithic conditional statements.

3.9.2. UML Diagram

Alt State UML Diagram

3.9.3. Code

You can also find this code on GitHub

OrderContext.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\State;
  4.  
  5. class OrderContext
  6. {
  7. /**
  8. * @var State
  9. */
  10. private $state;
  11.  
  12. public static function create(): OrderContext
  13. {
  14. $order = new self();
  15. $order->state = new StateCreated();
  16.  
  17. return $order;
  18. }
  19.  
  20. public function setState(State $state)
  21. {
  22. $this->state = $state;
  23. }
  24.  
  25. public function proceedToNext()
  26. {
  27. $this->state->proceedToNext($this);
  28. }
  29.  
  30. public function toString()
  31. {
  32. return $this->state->toString();
  33. }
  34. }

State.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\State;
  4.  
  5. interface State
  6. {
  7. public function proceedToNext(OrderContext $context);
  8.  
  9. public function toString(): string;
  10. }

StateCreated.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\State;
  4.  
  5. class StateCreated implements State
  6. {
  7. public function proceedToNext(OrderContext $context)
  8. {
  9. $context->setState(new StateShipped());
  10. }
  11.  
  12. public function toString(): string
  13. {
  14. return 'created';
  15. }
  16. }

StateShipped.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\State;
  4.  
  5. class StateShipped implements State
  6. {
  7. public function proceedToNext(OrderContext $context)
  8. {
  9. $context->setState(new StateDone());
  10. }
  11.  
  12. public function toString(): string
  13. {
  14. return 'shipped';
  15. }
  16. }

StateDone.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\State;
  4.  
  5. class StateDone implements State
  6. {
  7. public function proceedToNext(OrderContext $context)
  8. {
  9. // there is nothing more to do
  10. }
  11.  
  12. public function toString(): string
  13. {
  14. return 'done';
  15. }
  16. }

3.9.4. Test

Tests/StateTest.php

  1. <?php
  2.  
  3. namespace DesignPatterns\Behavioral\State\Tests;
  4.  
  5. use DesignPatterns\Behavioral\State\OrderContext;
  6. use PHPUnit\Framework\TestCase;
  7.  
  8. class StateTest extends TestCase
  9. {
  10. public function testIsCreatedWithStateCreated()
  11. {
  12. $orderContext = OrderContext::create();
  13.  
  14. $this->assertEquals('created', $orderContext->toString());
  15. }
  16.  
  17. public function testCanProceedToStateShipped()
  18. {
  19. $contextOrder = OrderContext::create();
  20. $contextOrder->proceedToNext();
  21.  
  22. $this->assertEquals('shipped', $contextOrder->toString());
  23. }
  24.  
  25. public function testCanProceedToStateDone()
  26. {
  27. $contextOrder = OrderContext::create();
  28. $contextOrder->proceedToNext();
  29. $contextOrder->proceedToNext();
  30.  
  31. $this->assertEquals('done', $contextOrder->toString());
  32. }
  33.  
  34. public function testStateDoneIsTheLastPossibleState()
  35. {
  36. $contextOrder = OrderContext::create();
  37. $contextOrder->proceedToNext();
  38. $contextOrder->proceedToNext();
  39. $contextOrder->proceedToNext();
  40.  
  41. $this->assertEquals('done', $contextOrder->toString());
  42. }
  43. }