3.10. State
3.10.1. Purpose
Encapsulate varying behavior for the same routine based on an object’s state. This can be a cleaner way for an object to change its behavior at runtime without resorting to large monolithic conditional statements.
3.10.2. UML Diagram
3.10.3. Code
You can also find this code on GitHub
ContextOrder.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class ContextOrder
8{
9 private StateOrder $state;
10
11 public static function create(): ContextOrder
12 {
13 $order = new self();
14 $order->state = new CreateOrder();
15
16 return $order;
17 }
18
19 public function setState(StateOrder $state): void
20 {
21 $this->state = $state;
22 }
23
24 public function proceedToNext(): void
25 {
26 $this->state->proceedToNext($this);
27 }
28
29 public function toString(): string
30 {
31 return $this->state->toString();
32 }
33}
StateOrder.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7interface StateOrder
8{
9 public function proceedToNext(ContextOrder $context): void;
10
11 public function toString(): string;
12}
CreateOrder.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class CreateOrder implements StateOrder
8{
9 public function proceedToNext(ContextOrder $context): void
10 {
11 $context->setState(new ShippingOrder());
12 }
13
14 public function toString(): string
15 {
16 return 'created';
17 }
18}
ShippingOrder.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class ShippingOrder implements StateOrder
8{
9 public function proceedToNext(ContextOrder $context): void
10 {
11 $context->setState(new OrderDone());
12 }
13
14 public function toString(): string
15 {
16 return 'shipped';
17 }
18}
OrderDone.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State;
6
7class OrderDone implements StateOrder
8{
9 public function proceedToNext(ContextOrder $context): void
10 {
11 // there is nothing more to do
12 }
13
14 public function toString(): string
15 {
16 return 'done';
17 }
18}
3.10.4. Test
Tests/StateTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\State\Tests;
6
7use DesignPatterns\Behavioral\State\ContextOrder;
8use PHPUnit\Framework\TestCase;
9
10class StateTest extends TestCase
11{
12 public function testIsCreatedWithStateCreated(): void
13 {
14 $orderContext = ContextOrder::create();
15
16 $this->assertSame('created', $orderContext->toString());
17 }
18
19 public function testCanProceedToStateShipped(): void
20 {
21 $contextOrder = ContextOrder::create();
22 $contextOrder->proceedToNext();
23
24 $this->assertSame('shipped', $contextOrder->toString());
25 }
26
27 public function testCanProceedToStateDone(): void
28 {
29 $contextOrder = ContextOrder::create();
30 $contextOrder->proceedToNext();
31 $contextOrder->proceedToNext();
32
33 $this->assertSame('done', $contextOrder->toString());
34 }
35
36 public function testStateDoneIsTheLastPossibleState(): void
37 {
38 $contextOrder = ContextOrder::create();
39 $contextOrder->proceedToNext();
40 $contextOrder->proceedToNext();
41 $contextOrder->proceedToNext();
42
43 $this->assertSame('done', $contextOrder->toString());
44 }
45}