Development Blog
Collection of development articles I've put together...
State Pattern
Encapsulate varying behaviour for the same routine based on an object's state. This can be a cleaner way for an object to change its behaviour at runtime without resorting to large monolithic conditional statements.
0
Likes
Stuart Todd
•
2 years ago
•
Design Patterns: Behavioural
Purpose
The purpose of the design pattern
Encapsulate varying behaviour for the same routine based on an object's state. This can be a cleaner way for an object to change its behaviour at runtime without resorting to large monolithic conditional statements.
Examples
Examples of how the design pattern can be used
UML
UML design pattern diagram
Code
Code snippets
State
State interface defining method signature for proceedToNext and toString.
namespace DesignPatterns\Behavioral\State;
interface State
{
public function proceedToNext(OrderContext $context);
public function toString(): string;
}
StateCreated
StateCreated implements State. The proceedToNext method changes the state to StateShipped. The toString method outputs ‘created’.
namespace DesignPatterns\Behavioral\State;
class StateCreated implements State
{
public function proceedToNext(OrderContext $context)
{
$context->setState(new StateShipped());
}
public function toString(): string
{
return 'created';
}
}
StateShipped
StateShipped implements State. The proceedToNext method changes the state to StateDone. The toString method outputs ‘shipped’.
namespace DesignPatterns\Behavioral\State;
class StateShipped implements State
{
public function proceedToNext(OrderContext $context)
{
$context->setState(new StateDone());
}
public function toString(): string
{
return 'shipped';
}
}
StateDone
StateDone implements State. The proceedToNext method doesn’t do anything, as there’s nothing left to do. The toString method outputs ‘done’.
namespace DesignPatterns\Behavioral\State;
class StateDone implements State
{
public function proceedToNext(OrderContext $context)
{
// there is nothing more to do
}
public function toString(): string
{
return 'done';
}
}
OrderContext
OrderContext manages and holds a state. When OrderContent is created the state is set to StateCreated.
namespace DesignPatterns\Behavioral\State;
class OrderContext
{
private State $state;
public static function create(): OrderContext
{
$order = new self();
$order->state = new StateCreated();
return $order;
}
public function setState(State $state)
{
$this->state = $state;
}
public function proceedToNext()
{
$this->state->proceedToNext($this);
}
public function toString()
{
return $this->state->toString();
}
}
StateTest
Quick unit test showing the design pattern in action. Shows how the order state can be changed via a method call to create initially, then proceedToNext.
namespace DesignPatterns\Behavioral\State\Tests;
use DesignPatterns\Behavioral\State\OrderContext;
use PHPUnit\Framework\TestCase;
class StateTest extends TestCase
{
public function testIsCreatedWithStateCreated()
{
$orderContext = OrderContext::create();
$this->assertSame('created', $orderContext->toString());
}
public function testCanProceedToStateShipped()
{
$contextOrder = OrderContext::create();
$contextOrder->proceedToNext();
$this->assertSame('shipped', $contextOrder->toString());
}
public function testCanProceedToStateDone()
{
$contextOrder = OrderContext::create();
$contextOrder->proceedToNext();
$contextOrder->proceedToNext();
$this->assertSame('done', $contextOrder->toString());
}
public function testStateDoneIsTheLastPossibleState()
{
$contextOrder = OrderContext::create();
$contextOrder->proceedToNext();
$contextOrder->proceedToNext();
$contextOrder->proceedToNext();
$this->assertSame('done', $contextOrder->toString());
}
}