30.03.2010
The State pattern allows an object to change its behavior when its internal state changes. Basically the states are kept in a separate object that encapsulates the state-related behavior. The switch constructions are often candidates for this pattern.
|
Participants:
The State pattern does not specify where the state transitions will be defined. The choices are two: the context object, or each individual State derived class. The advantage of the later option is ease of adding new State derived classes. The disadvantage is each State derived class has knowledge of (coupling to) its siblings, which introduces dependencies between subclasses. |
We get the example of a Printer with 4 identified states: ready, print starting, printing (in progress), print ending.
Each state class will have an action() method and an (optionally) display_state() method (used to better debug the operations).
States classes will look as:
// State interface here interface PrinterState { public function display_state(); public function action(); } // Concrete States classes follow class StateReady implements PrinterState { public function action() { echo "Printer is ready.\n"; return new StatePrintStart; } public function display_state() { echo get_class($this)."->"; } } class StatePrintStart implements PrinterState { public function action() { echo "The print is starting.\n"; return new StatePrinting; } public function display_state() { echo get_class($this)."->"; } } class StatePrinting implements PrinterState { public function action() { echo "Printer is printing nicely.\n"; return new StatePrintEnd; } public function display_state() { echo get_class($this)."->"; } } class StatePrintEnd implements PrinterState { public function action() { echo "Printer has finish its job.\n"; return new StateReady; } public function display_state() { echo get_class($this)."->"; } }
After this, the interface class:
class Printer { protected $_printerstate; /** * @param State $initialState the state at the start */ public function __construct(PrinterState $initialState) { $this->_printerstate = $initialState; } public function run() { $this->_printerstate->display_state(); // just for displaying purposes $this->_printerstate = $this->_printerstate->action(); // each action returns next possible state } public function cancel() { // simplified example // this will call the end state no matter what and after end action, prepare again the printer $endstate = new StatePrintEnd; $this->_printerstate = $endstate->action(); } }
Then run the code:
$printer = new Printer(new StateReady); echo "---------------------- \n"; $printer->run(); // the printer becomes ready echo "---------------------- \n"; $printer->cancel(); // here we cancel echo "---------------------- \n"; $printer->run(); // return and retry again echo "---------------------- \n"; $printer->run(); echo "---------------------- \n"; $printer->run(); echo "---------------------- \n"; $printer->run(); echo "---------------------- \n";
Result for above code is:
...$> php state_pattern.php ---------------------- StateReady->Printer is ready. ---------------------- Printer has finish its job. ---------------------- StateReady->Printer is ready. ---------------------- StatePrintStart->The print is starting. ---------------------- StatePrinting->Printer is printing nicely. ---------------------- StatePrintEnd->Printer has finish its job. ----------------------
References:
Robot example
Giorgio Sironi example
Page about State Pattern