Thursday, March 18, 2010

Practical Php Patterns: State

This post is part of the Practical Php Pattern series.

The pattern of today is the State one: its intent is allowing an object to change its behavior when its state change, while hiding the state-related informations.
The main role in this pattern is carried out by a State object that encapsulates the state-related behavior beyond a segregated interface.
Instead of executing the same structure of conditionals in many methods of the Context object, it delegates part of its job to State. Switch constructs are often candidates for replacement by a State collaborator.
The effective State is changed by replacing the State object with a new instance, possibly from a different class (State implementations may be Flyweights.)
Usually factory methods on the State object create other istances in response to events raised by the Context calls, so that the transition is dictated by the internal State code. You can implement a Finite State Machine with this pattern, ensuring that the transitions cannot be violated because the next State is decided by a ConcreteState method.
Moreover, the State methods may accept parameters from the Context to fulfill their responsibility, so that there is no bidirectional relationship to maintain in field references.
There are two explicit concepts enforced and named by the use of this pattern: the stateful part of an object (encapsulated in the State collaborator and separated from the immutable behavior of Context) and the state transitions (abstracted in the assignment of a new State object to a Context field reference, which on the right side has a call to an handler method on State implementations.)

Participants
  • Context: defines an interface for Clients and maintains a State object internally.
  • State: defines an interface for the state-related behavior and often for transitions.
  • ConcreteState (more than one class): implements a particular behavior and set of valid transitions.
Note that ConcreteStates are usually ValueObjects, so they are immutable and shared. Thus a new instance of one of the ConcreteStates is created when there is a transition.

The code sample implements a Finite State Machine for the parsing and validation of a binary string, which is considered valid if it has a parity bit (the number of 1 bits is even). The state and their relationships are encapsulated by the set of State implementations, and can change without affecting the code of Context.
<?php
interface State
{
    /**
     * @param string $input     '1' or '0'
     * @return State            next State
     */
    public function parse($input);

    /**
     * @return boolean          whether a binary string that
     *                          brought to this State is valid
     */
    public function valid();
}

/**
 * A ConcreteState. The machine is in this state
 * when the number of 1 read is even (valid string).
 */
class EvenOnesState implements State
{
    public function parse($input) {
        if ($input == 1) {
            return new OddOnesState;
        } else {
            return $this;
        }
    }

    public function valid()
    {
        return true;
    }
}

/**
 * A ConcreteState. The machine is in this state
 * when the number of 1 read is odd (invalid string).
 */
class OddOnesState implements State
{
    public function parse($input) {
        if ($input == 1) {
            return new EvenOnesState;
        } else {
            return $this;
        }
    }

    public function valid()
    {
        return false;
    }
}

class ParityBitValidator
{
    protected $_state;

    /**
     * @param State $initialState   the state at the reset
     */
    public function __construct(State $initialState)
    {
        $this->_state = $initialState;
    }

    /**
     * @param string $bits
     * @return boolean
     */
    public function isValid($bits)
    {
        for ($i = 0; $i < strlen($bits); $i++) {
            $bit = $bits{$i};
            $this->_state = $this->_state->parse($bit);
        }
        return $this->_state->valid();
    }
}

$validator = new ParityBitValidator(new EvenOnesState);
var_dump($validator->isValid('10101001101'));    // true
var_dump($validator->isValid('101010011011'));   // false

11 comments:

Anonymous said...

$bit = $bits{$i}; this is deprecated in php 5.3. You should use $bit = $bits[$i] instead.

http://www.php.net/manual/en/language.types.string.php#language.types.string.substr

Thank you for all the great posts!

Anonymous said...

I appreciate post, where people share knowledge to others. Thanks for that.
However, you call your tutorial "Practical PHP Patterns" and it looks for me more like more theoretical.

I think, if some guy (like me) who never or little used Design Patterns read your post, he has very little chance to understand for what it is good. The same could be read in all books about design patterns.

Sorry for critical opinion, but title of the post is irritating

e.s.t said...

Well, actually you do have got some practical examples, don't you ? In my opinion it can't get any more practical than this.

Giorgio said...

I really don't know what could be more "practical" than including a full implementation of the pattern, with running code and docblocks that explains the correspondence between classes and roles. If you have some constructive suggestions...

Anonymous said...

Is ParityBitValidator used in PHP scripts? Does any PHP developer do parity bit checking? I suppose most do not.

PHP practical means for me, that I can use pattern for PHP related problems. Sure, if one invest some time, then use of State could be found for PHP, but this is what I expect if I see the title "PHP practical patterns". It should be both PHP and practical.

Ughh... Never mind. Looks like no one share my opinion :)

Giorgio said...

The problem with what you're suggesting is that the example problem gets in the way of showing the pattern. I could implement the same state machine for the parsing of some structure like docblocks or a subset of Sql, then no one would understand what part of the code is the pattern-related infrastructure and what part contains business logic specific to the problem at hand. :)

Anonymous said...

believe me or not, I'd find it great :)

I wouldn't expect full implementation of parser for docblocks or whatever. Its ok, if it has only dummy methods there.

I know many developers out there, that knew about DP, but don't get, why they would use them, because they don't understand really benefit of using it.

The same problem is with Unit Testing. There are many tutorials how it works, but only a few which take time to explain why would one use it. I don't mean statements like "your software is robust then" or "you have less problems if you change your code later". Usually there is calculator example and first thought is why in the hell I would test that 1+1 = 2.

What I mean, important aspect for me is WHY would I use the solution and not only HOW.

I still appreciate tutorials, and of course yours too ;)

php development services said...

PHP practical means for me, that I can use pattern for PHP related problems.

designing said...

Nice blog.There are many tutorials how it works, but only a few which take time to explain why would one use it.

Anonymous said...

State Machines scare people. They're so complex and hard at college that people actually run away from them. But actually, more than half of current web apps are state machines. If you have a status, published_at, or any timestamp field that can be null you're most likely using a state machine and you're not noticing it. This is a good explanation about this Pattern and with a PHP example. That's really uncommon. Thank you very much for this.

On a side note: There really very few PHP related problems. You have problems that can be solved with PHP (or any other language). PHP related is something that you want to built in PHP and you can't because the language doesn't offer a way to do it. Everything else are just problems. :)

Thank you again for the post

Rolf Vreijdenberger said...

Thanks for your article. Your Context example has actually been implemented in my opensource php statemachine project 'izzum': https://github.com/rolfvreijdenberger/izzum-statemachine, which is a lot more elaborate than what is presented here, but with the same formal approach taken.
cheers,
Rolf Vreijdenberger

ShareThis