Manuel Wildauers blog

State Pattern in PHP

Das State Pattern ist hervorragend dazu geeignet das Verhalten eines Objektes abhängig von seinem Zustand zu machen.

In meinem Beispiel ist es ein Fahrstuhl der folgende Zustände haben kann:

Daraus lässt sich folgendes Interface ableiten

<?php

namespace Elevator;

interface ElevatorStateInterface
{
    public function open();
    public function close();
    public function move();
    public function stop();
}

Nun gibt es eine Klasse ElevatorState die das Interface implementiert.

<?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();
    }
}

Standardmäßig werfen alle Methoden eine Exception. In meinem Fall ist es eine IllegalStateTransitionException die von LogicException erbt.

Jetzt können wir die einzelnen Zustände implementieren. In diesem Beispiel der Status “Move”.

<?php

namespace Elevator\State;

class Move extends \Elevator\ElevatorState
{
    public function move()
    {
        return new Move();
    }

    public function stop()
    {
        return new Stop();
    }
}

Wie man sieht sind nicht alle Methoden aus ElevatorState implementiert. Nämlich genau dies, die für den aktuellen Zustand nicht erlaubt sind. Es gibt bestimmt Fahrstühle die während der Fahrt die Tür öffnen, sollten sie aber nicht.

Hier die eigentliche Klasse 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());
    }
}

Wenn diese Klasse mit

$elevator = new Elevator\Elevator();

instanziiert wird, bekommt sie durch den Konstruktor den Zustand Stop.

Das bedeutet wir können aus diesem Zustand in den Zustand Open wechseln.

$elevator->open();

Wenn ich jetzt aus diesem Zustand folgendes versuche

$elevator->move();

würde folgendes passieren

PHP Fatal error: Uncaught Elevator\Exception\IllegalStateTransitionException

da die Tür erst geschlossen werden muss.

$elevator->close();

Das Objekt Elevator ist also abhängig von seinem Zustand.

Den kompletten Source zum State Pattern in PHP gibt es auf GitHub.