Design Pattern: State in PHP

Next on from the Head First Design Patterns is the “State Pattern”. The State Pattern allows an object to alter its behavior when its internal state changes. By altering its behavior, the object can perform a different set of operations. To truly understand this pattern, let’s take a look at an example. A sales order can have many different states such as “New Order”, “Invoicing”, “Processing”, “Shipped”. Before the sales order can be moved forward to a different state, there are strict rules that need to be obeyed.

Here’s the code if you would like to play with it.

interface State {
    public function insertQuarter();
    public function ejectQuarter();
    public function turnCrank();
    public function dispense();
}
class HasQuarterState implements State {
	private $random;
    private $gumballMachine;

    public function __construct(GumballMachine $gumballMachine){
        $this->gumballMachine = $gumballMachine;
    }

    public function insertQuarter() {
       echo("You can't insert another quarter<br />");
    }

    public function ejectQuarter() {
        echo("Quarter returned<br />");
        $this->gumballMachine->setState($this->gumballMachine->getNoQuarterState());
    }

    public function turnCrank() {
        echo("You turned ...");
		$winner = rand(0, 9);
		if(($winner == 0) && ($this->gumballMachine->getCount() > 1)){
			$this->gumballMachine->setState($this->gumballMachine->getWinnerState());
		}
		else{
			$this->gumballMachine->setState($this->gumballMachine->getSoldState());
		}
    }

    public function dispense() {
    	echo("No gumball dispensed<br />");
    }

	public function __toString(){
		return "waiting for turn of crank<br />";
	}
}
include_once('state.php');

class NoQuarterState implements State {
    private $gumballMachine;

    public function __construct(GumballMachine $gumballMachine){
        $this->gumballMachine = $gumballMachine;
    }

    public function insertQuarter() {
        echo("You inserted a quarter<br />");
        $this->gumballMachine->setState($this->gumballMachine->getHasQuarterState());
    }

    public function ejectQuarter() {
        echo("You haven't inserted a quarter<br />");

    }

    public function turnCrank() {
        echo("You turned, but there is no quarter<br />");
    }

    public function dispense() {
        echo("You need to pay first<br />");
    }

	public function __toString(){
		return "waiting for quarter<br />";
	}
}
include_once('state.php');

class SoldOutState implements State {
	private $gumballMachine;

	public function __construct(GumballMachine $gumballMachine){
		$this->gumballMachine = $gumballMachine;
	}

    public function insertQuarter() {
    	echo("You can't insert a quarter, the machine is sold out<br />");
    }

    public function ejectQuarter() {
    	echo("You can't eject, you haven't inserted a quarter yet<br />");
    }

    public function turnCrank() {
    	echo("You turned, but there are no gumballs<br />");
    }

    public function dispense() {
    	echo("No gumball dispensed<br />");
    }

	public function __toString(){
		return "sold out<br />";
	}
}
include_once('state.php');

class SoldState implements State {
    private $gumballMachine;

	public function __construct(GumballMachine $gumballMachine){
		$this->gumballMachine = $gumballMachine;
	}

    public function insertQuarter() {
    	echo("Please wait, we're already giving you a gumball<br />");
    }

    public function ejectQuarter() {
    	echo("Sorry, you already turned the crank<br />");
    }

    public function turnCrank() {
    	echo("Turning twice doesn't get you another gumball!<br />");
    }

    public function dispense() {
    	$this->gumballMachine->releaseBall();
		if($this->gumballMachine->getCount() > 0){
			$this->gumballMachine->setState($this->gumballMachine->getNoQuarterState());
		}
		else{
			echo("Oops, out of gumballs!<br />");
			$this->gumballMachine->setState($this->gumballMachine->getSoldOutState());
		}
    }

	public function __toString(){
		return "dispensing a gumball<br />";
	}
}
include_once ('state.php');

class WinnerState implements State {
	private $gumballMachine;

	public function __construct(GumballMachine $gumballMachine) {
		$this -> gumballMachine = $gumballMachine;
	}

	public function insertQuarter() {
		echo("Please wait, we're already giving you a gumball<br />");
	}

	public function ejectQuarter() {
		echo("Please wait, we're already giving you a Gumball<br />");
	}

	public function turnCrank() {
		echo("Turning again doesn't get you another gumball!<br />");
	}

	public function dispense() {
		echo("YOU'RE A WINNER! You get two gumballs for your quarter<br />");
		$this -> gumballMachine -> releaseBall();
		if ($this -> gumballMachine -> getCount() == 0) {
			$this -> gumballMachine -> setState($this -> gumballMachine -> getSoldOutState());
		}
		else {
			$this -> gumballMachine -> releaseBall();

			if ($this -> gumballMachine -> getCount() > 0) {
				$this -> gumballMachine -> setState($this -> gumballMachine -> getNoQuarterState());
			}
			else {
				echo("Oops, out of gumballs!<br />");
				$this -> gumballMachine -> setState($this -> gumballMachine -> getSoldOutState());
			}
		}
	}

	public function __toString() {
		return "dispensing two gumballs for your quarter, because YOU'RE A WINNER!<br />";
	}

}
include_once('state.php');
include_once('soldState.php');
include_once('noQuarterState.php');
include_once('hasQuarterState.php');
include_once('soldOutState.php');
include_once('winnerState.php');

class GumballMachine {

    private $soldOutState;
    private $noQuarterState;
    private $hasQuarterState;
    private $soldState;
	private $winnerState;
    private $state;
    private $count = 0;

    public function __construct($numberGumballs) {
        $this->state = $this->soldOutState;
        $this->soldOutState = new SoldOutState($this);
        $this->noQuarterState = new NoQuarterState($this);
        $this->hasQuarterState = new HasQuarterState($this);
        $this->soldState = new SoldState($this);
		$this->winnerState = new WinnerState($this);

        $this->count = $numberGumballs;
        if ($numberGumballs > 0) {
            $this->state = $this->noQuarterState;
        }
    }

    public function insertQuarter() {
        $this->state->insertQuarter();
    }

    public function ejectQuarter() {
        $this->state->ejectQuarter();
    }

    public function turnCrank() {
        $this->state->turnCrank();
        $this->state->dispense();
    }

    public function setState(State $state) {
        $this->state = $state;
    }

    public function releaseBall() {
        echo("A gumball comes rolling out the slot...<br />");
        if ($this->count != 0) {
            $this->count = $this->count - 1;
        }
    }

    public function getCount() {
        return $this->count;
    }

    private function refill($count) {
        $this->count = $count;
        $this->state = $this->noQuarterState;
    }

    public function getState() {
        return $this->state;
    }

    public function getSoldOutState() {
        return $this->soldOutState;
    }

    public function getNoQuarterState() {
        return $this->noQuarterState;
    }

    public function getHasQuarterState() {
        return $this->hasQuarterState;
    }

    public function getSoldState() {
        return $this->soldState;
    }

	public function getWinnerState(){
		return $this->winnerState;
	}

    public function __toString() {
        $result = "";
        $result = $result . "Mighty Gumball, Inc.<br />";
        $result = $result . "Java-enabled Standing Gumball Model #2004<br />";
        $result = $result . "Inventory: " . $this->count . " gumball";
        if ($this->count != 1) {
            $result = $result . "s<br />";
        }
        $result = $result . "Machine is " . $this->state;
        return $result;
    }
}
include_once 'gumballMachine.php';

class GumballMachineTestDrive{
	public static function main(){
		$gumballMachine = new GumballMachine(5);
		echo($gumballMachine);

		$gumballMachine->insertQuarter();
		$gumballMachine->turnCrank();

		echo($gumballMachine);

		$gumballMachine->insertQuarter();
		$gumballMachine->turnCrank();
		$gumballMachine->insertQuarter();
		$gumballMachine->turnCrank();

		echo($gumballMachine);
	}

}

GumballMachineTestDrive::main();

Leave a Reply




You may use the tags listed below in your comments:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>