The State pattern is excellent for making the behavior of an object dependent on its state. In my example, it is an elevator that can have the following states:
- Open (Open)
- Closed (Close)
- In Motion (Move)
- Stand (Stop)
From this, the following interface can be derived
<?php
namespace Elevator;
interface ElevatorStateInterface
{
public function open();
public function close();
public function move();
public function stop();
}
Now we need a class ElevatorState that implements the interface
<?php
namespace Elevator;
class ElevatorState implements ElevatorStateInterface
{
public function close()
{
throw new \Elevator\Exception\IllegalStateTransitionException();
}
public function move()
{
throw new \Elevator\Exception\IllegalStateTransitionException();
}
public function open()
{
throw new \Elevator\Exception\IllegalStateTransitionException();
}
public function stop()
{
throw new \Elevator\Exception\IllegalStateTransitionException();
}
}
By default, all methods throw an exception. In my case it is an IllegalStateTransitionException which inherits from LogicException.
Now we can implement the individual states. In this example, the Move state.
<?php
namespace Elevator\State;
class Move extends \Elevator\ElevatorState
{
public function move()
{
return new Move();
}
public function stop()
{
return new Stop();
}
}
As you can see, not all methods are implemented from ElevatorState.
Exactly this, which are not allowed for the current state.
The class Elevator
<?php
namespace Elevator;
class Elevator
{
private $state;
function getState()
{
return $this->state;
}
function setState(ElevatorStateInterface $state)
{
$this->state = $state;
print "set state to : " . get_class($state) . PHP_EOL;
}
public function __construct()
{
$this->setState(new \Elevator\State\Stop());
}
public function isOpen()
{
return $this->state instanceof \Elevator\State\Open;
}
public function open()
{
$this->setState($this->state->open());
}
public function close()
{
$this->setState($this->state->close());
}
public function move()
{
$this->setState($this->state->move());
}
public function stop()
{
$this->setState($this->state->stop());
}
}
If you call the class with
<?php
$elevator = new Elevator\Elevator();
it gets the state Stop by the constructor. This means we can switch from this state to the Open state.
<?php
$elevator->open();
Now, I try this
<?php
$elevator->move();
An error will happen
PHP Fatal error: Uncaught Elevator\Exception\IllegalStateTransitionException
because the door must first be closed.
<?php
$elevator->close();
The complete source of this example is available on GitHub.
Top comments (4)
Thanks for this
How great I liked logic
I don't get it. Where did you implement the logic which says that the door must be closed before the move state can be set?
„Open“ doesn’t implement the method „move“ (github.com/mnlwldr/State-Pattern/b...).
That means when you call the method „move“ it throws an exception because in this state, move is not allowed.