Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fear Not the Machine of State!

Fear Not the Machine of State!

Do your application’s objects have properties like ‘status’ or ‘state’, boolean properties like ‘active’, ‘paid’ or ‘published’, nullable timestamp properties like ‘paid_on’ or ‘published_at’? These are all good indicators that your project may benefit from the introduction of a State Machine. Proper use of a State Machine can mean less bugs, less undefined behavior, and a more clearly defined API to your object’s internal state, yet many developers are reluctant to use them in their projects, fearing unnecessary complexity, difficult integration or a steep learning curve. The good news is that these concerns are largely unfounded! Join us as we explore the State Machine’s theoretical underpinnings, examine its practical application including examples of its use in popular open source projects, and review available PHP resources for easily leveraging the power of the State Machine in your own projects.

willroth

July 23, 2015
Tweet

More Decks by willroth

Other Decks in Programming

Transcript

  1. State Pattern • Behavioral Pattern • Allows an object to

    alter its behavior when its internal state changes, giving the appearance of changing class.
  2. Context: maintains an instance of the concrete state subclass that

    defines current behavior. Abstract State: defines an interface for encapsulating all state behavior. Concrete State: implements the behavior associated with a state of the context.
  3. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  4. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  5. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  6. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  7. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  8. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  9. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  10. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  11. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  12. class Turnstyle { const LOCKED_STATE = 'locked'; const UNLOCKED_STATE =

    'unlocked'; private $status; public function __construct($status = null) { $this->status = is_null($status) ? self::LOCKED_STATE : $status; } public function getStatus() { return $this->status; } public function pass() { $this->status = self::LOCKED_STATE; } public function coin() { $this->status = self::UNLOCKED_STATE; } }
  13. // truncated ... public function pass() { if ($this->status ==

    self::LOCKED_STATE) { $this->alarm->sound(); } $this->status = self::LOCKED_STATE; } public function coin() { if ($this->status == self::UNLOCKED_STATE) { $this->coinVendor->refund(); } $this->status = self::UNLOCKED_STATE; } // truncated ...
  14. // truncated ... public function pass() { if ($this->status ==

    self::LOCKED_STATE) { $this->alarm->sound(); } $this->status = self::LOCKED_STATE; } public function coin() { if ($this->status == self::UNLOCKED_STATE) { $this->coinVendor->refund(); } $this->status = self::UNLOCKED_STATE; } // truncated ...
  15. // truncated ... public function pass() { if ($this->status ==

    self::LOCKED_STATE) { $this->alarm->sound(); } $this->status = self::LOCKED_STATE; } public function coin() { if ($this->status == self::UNLOCKED_STATE) { $this->coinVendor->refund(); } $this->status = self::UNLOCKED_STATE; } // truncated ...
  16. // truncated ... public function pass() { if ($this->status ==

    self::LOCKED_STATE) { $this->alarm->sound(); } $this->status = self::LOCKED_STATE; } public function coin() { if ($this->status == self::UNLOCKED_STATE) { $this->coinVendor->refund(); } $this->status = self::UNLOCKED_STATE; } // truncated ...
  17. // truncated ... public function pass() { if ($this->status ==

    self::LOCKED_STATE) { $this->alarm->sound(); } $this->status = self::LOCKED_STATE; } public function coin() { if ($this->status == self::UNLOCKED_STATE) { $this->coinVendor->refund(); } $this->status = self::UNLOCKED_STATE; } // truncated ...
  18. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Ready ResetAlarm Lock Coin Refund
  19. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Ready ResetAlarm Lock Coin Refund
  20. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Ready ResetAlarm Lock Coin Refund Pass
  21. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Ready ResetAlarm Lock Coin Refund Pass
  22. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset Ready ResetAlarm Lock Coin Refund Pass
  23. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  24. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  25. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  26. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  27. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock NORMAL MODE Coin Refund Pass
  28. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock NORMAL MODE Coin Refund Pass
  29. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock NORMAL MODE DIAGNOSTIC MODE Coin Refund Pass
  30. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock NORMAL MODE DIAGNOSTIC MODE H Coin Refund Pass
  31. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock NORMAL MODE DIAGNOSTIC MODE H Coin Refund Pass
  32. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  33. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  34. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  35. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  36. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  37. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  38. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  39. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  40. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  41. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  42. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  43. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  44. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } }
  45. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } }
  46. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } }
  47. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } }
  48. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } }
  49. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } }
  50. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  51. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  52. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  53. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  54. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  55. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  56. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  57. class LockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  58. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  59. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  60. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  61. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  62. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  63. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  64. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  65. class unlockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->setState($turnstile->lockedState); } public function coin(Turnstile $turnstile) { $turnstile->coinVendor->refund(); } }
  66. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  67. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  68. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  69. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  70. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  71. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  72. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  73. class Turnstile { private $state; public $lockedState; public $unlockedState; public

    $violationState; public function __construct(TurnstileState $state = null) { $this->lockedState = new LockedTurnstileState(); $this->unlockedState = new UnlockedTurnstileState(); $this->violationState = new ViolationTurnstileState(); $this->state = is_null($state) ? $this->lockedState : $state; } public function getState() { return $this->state; } public function setState(TurnstileState $state) { $this->state = $state; } public function pass() { $this->state->pass($this); } public function coin() { $this->state->coin($this); } public function reset() { $this->state->reset($this); } public function ready() { $this->state->ready($this); } }
  74. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } public function reset(Turnstile $turnstile) { //empty implementation } public function ready(Turnstile $turnstile) { //empty implementation } }
  75. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } public function reset(Turnstile $turnstile) { //empty implementation } public function ready(Turnstile $turnstile) { //empty implementation } }
  76. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } public function reset(Turnstile $turnstile) { //empty implementation } public function ready(Turnstile $turnstile) { //empty implementation } }
  77. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

    implementation } public function coin(Turnstile $turnstile) { //empty implementation } public function reset(Turnstile $turnstile) { //empty implementation } public function ready(Turnstile $turnstile) { //empty implementation } }
  78. class violationTurnstileState implements TurnstileState { public function coin(Turnstile $turnstile) {

    turnstile->coinVendor->refund(); } public function reset(Turnstile $turnstile) { $turnstile->alarm->reset(); } public function ready(Turnstile $turnstile) { $turnstile->alarm->reset(); $turnstile->setState($turnstile->unlockedTurnstileState); } }
  79. class violationTurnstileState implements TurnstileState { public function coin(Turnstile $turnstile) {

    turnstile->coinVendor->refund(); } public function reset(Turnstile $turnstile) { $turnstile->alarm->reset(); } public function ready(Turnstile $turnstile) { $turnstile->alarm->reset(); $turnstile->setState($turnstile->unlockedTurnstileState); } }
  80. class violationTurnstileState implements TurnstileState { public function coin(Turnstile $turnstile) {

    turnstile->coinVendor->refund(); } public function reset(Turnstile $turnstile) { $turnstile->alarm->reset(); } public function ready(Turnstile $turnstile) { $turnstile->alarm->reset(); $turnstile->setState($turnstile->unlockedTurnstileState); } }
  81. class violationTurnstileState implements TurnstileState { public function coin(Turnstile $turnstile) {

    turnstile->coinVendor->refund(); } public function reset(Turnstile $turnstile) { $turnstile->alarm->reset(); } public function ready(Turnstile $turnstile) { $turnstile->alarm->reset(); $turnstile->setState($turnstile->unlockedTurnstileState); } }
  82. class violationTurnstileState implements TurnstileState { public function coin(Turnstile $turnstile) {

    turnstile->coinVendor->refund(); } public function reset(Turnstile $turnstile) { $turnstile->alarm->reset(); } public function ready(Turnstile $turnstile) { $turnstile->alarm->reset(); $turnstile->setState($turnstile->unlockedTurnstileState); } }
  83. class violationTurnstileState implements TurnstileState { public function coin(Turnstile $turnstile) {

    turnstile->coinVendor->refund(); } public function reset(Turnstile $turnstile) { $turnstile->alarm->reset(); } public function ready(Turnstile $turnstile) { $turnstile->alarm->reset(); $turnstile->setState($turnstile->unlockedTurnstileState); } }
  84. class lockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); $turnstile->setState($turnstile->violationState); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  85. class lockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); $turnstile->setState($turnstile->violationState); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  86. class lockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); $turnstile->setState($turnstile->violationState); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  87. class lockedTurnstileState extends TurnstileState { public function pass(Turnstile $turnstile) {

    $turnstile->alarm->sound(); $turnstile->setState($turnstile->violationState); } public function coin(Turnstile $turnstile) { $turnstile->setState($turnstile->unlockedState); } }
  88. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  89. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock NORMAL MODE DIAGNOSTIC MODE H Coin Refund Pass
  90. Extend the public API of the “context” & Abstract “state”

    classes To include new event triggers Create a concrete subclass of the abstract “state class” to encapsulate the new state logic
  91. Extend the public API of the “context” & Abstract “state”

    classes To include new event triggers Create a concrete subclass of the abstract “state class” to encapsulate the new state logic ADD an instance of the new concrete “state” class as an instance variable on the “context” object
  92. • State Variables • $status • $state • Boolean Driven

    Development • $active, $published • isActive(), isPublished()
  93. • State Variables • $status • $state • Boolean Driven

    Development • $active, $published • isActive(), isPublished() • Nullable Timestamps
  94. • State Variables • $status • $state • Boolean Driven

    Development • $active, $published • isActive(), isPublished() • Nullable Timestamps • Multipart Conditionals or Switches
  95. <?xml version="1.0" encoding="UTF-8"?> <specification> <configuration> <class name="Door"/> <interface name="DoorState"/> <abstractClass

    name="AbstractDoorState"/> </configuration> <states> <state name="OpenDoorState" query="isOpen"/> <state name="ClosedDoorState" query="isClosed"/> <state name="LockedDoorState" query="isLocked"/> </states> <transitions> <transition from="ClosedDoorState" to="OpenDoorState" operation="open"/> <transition from="OpenDoorState" to="ClosedDoorState" operation="close"/> <transition from="ClosedDoorState" to="LockedDoorState" operation="lock"/> <transition from="LockedDoorState" to="ClosedDoorState" operation="unlock"/> </transitions> <operations> <operation name="open" allowed="canBeOpened" disallowed="cannotBeOpened"/> <operation name="close" allowed="canBeClosed" disallowed="cannotBeClosed"/> <operation name="lock" allowed="canBeLocked" disallowed="cannotBeLocked"/> <operation name="unlock" allowed="canBeUnlocked" disallowed="cannotBeUnlocked"/> </operations> </specification>
  96. OpenDoor [x] Can be closed [x] Cannot be opened [x]

    Cannot be locked [x] Cannot be unlocked ClosedDoor [x] Cannot be closed [x] Can be opened [x] Can be locked [x] Cannot be unlocked LockedDoor [x] Cannot be closed [x] Cannot be opened [x] Cannot be locked [x] Can be unlocked
  97. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  98. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  99. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  100. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  101. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  102. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  103. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  104. class Door { private $state; public function __construct(DoorState $state) {

    $this->setState($state); } public function open() { $this->setState($this->state->open()); } public function close() { $this->setState($this->state->close()); } public function lock() { $this->setState($this->state->lock()); } public function unlock() { $this->setState($this->state->unlock()); } private function setState(DoorState $state) { $this->state = $state; } }
  105. class ClosedDoorState extends AbstractDoorState { public function open() { return

    new OpenDoorState; } public function lock() { return new LockedDoorState; } }
  106. class ClosedDoorState extends AbstractDoorState { public function open() { return

    new OpenDoorState; } public function lock() { return new LockedDoorState; } }
  107. class ClosedDoorState extends AbstractDoorState { public function open() { return

    new OpenDoorState; } public function lock() { return new LockedDoorState; } }
  108. class ClosedDoorState extends AbstractDoorState { public function open() { return

    new OpenDoorState; } public function lock() { return new LockedDoorState; } }
  109. class ClosedDoorState extends AbstractDoorState { public function open() { return

    new OpenDoorState; } public function lock() { return new LockedDoorState; } }
  110. class ClosedDoorState extends AbstractDoorState { public function open() { return

    new OpenDoorState; } public function lock() { return new LockedDoorState; } }
  111. • Context Object maintains a reference to the Stateful Object.

    • States & Transitions are mapped and maintained by the Context Object.
  112. • Context Object maintains a reference to the Stateful Object.

    • States & Transitions are mapped and maintained by the Context Object. • State Transitions are introduced externally rather than in response to Triggers.
  113. • Context Object maintains a reference to the Stateful Object.

    • States & Transitions are mapped and maintained by the Context Object. • State Transitions are introduced externally rather than in response to Triggers. • The Context Object determines if Transitions are allowed.
  114. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  115. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  116. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  117. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  118. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  119. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  120. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  121. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  122. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  123. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  124. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  125. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  126. $stateMap = [ 'class' => 'Document', 'states' => [ 'draft'

    => ['type' => 'initial', 'properties' => []], 'proposed' => ['type' => 'normal', 'properties' => []], 'accepted' => ['type' => 'final', 'properties' => []], 'refused' => ['type' => 'final', 'properties' => []], ], 'transitions' => [ 'propose' => ['from' => ['draft'], 'to' => 'proposed'], 'accept' => ['from' => ['proposed'], 'to' => 'accepted'], 'refuse' => ['from' => ['proposed'], 'to' => 'refused'], ] ]; $stateMachine = new Finite\StateMachine\StateMachine; $loader->load(new Finite\Loader\ArrayLoader($stateMap)); $stateMachine->setObject(new Document); $stateMachine->initialize();
  127. echo $stateMachine->getCurrentState(); // => "draft“ var_dump($stateMachine->can('accept')); // => bool(false) var_dump($stateMachine->can('propose'));

    // => bool(true) $stateMachine->apply('propose'); echo $stateMachine->getCurrentState(); // => "proposed"
  128. echo $stateMachine->getCurrentState(); // => "draft“ var_dump($stateMachine->can('accept')); // => bool(false) var_dump($stateMachine->can('propose'));

    // => bool(true) $stateMachine->apply('propose'); echo $stateMachine->getCurrentState(); // => "proposed"
  129. echo $stateMachine->getCurrentState(); // => "draft“ var_dump($stateMachine->can('accept')); // => bool(false) var_dump($stateMachine->can('propose'));

    // => bool(true) $stateMachine->apply('propose'); echo $stateMachine->getCurrentState(); // => "proposed"
  130. echo $stateMachine->getCurrentState(); // => "draft“ var_dump($stateMachine->can('accept')); // => bool(false) var_dump($stateMachine->can('propose'));

    // => bool(true) $stateMachine->apply('propose'); echo $stateMachine->getCurrentState(); // => "proposed"
  131. echo $stateMachine->getCurrentState(); // => "draft“ var_dump($stateMachine->can('accept')); // => bool(false) var_dump($stateMachine->can('propose'));

    // => bool(true) $stateMachine->apply('propose'); echo $stateMachine->getCurrentState(); // => "proposed"
  132. • exceptionally complex? • subject to frequent change? • externally

    configurable? Is Your State & Transition Graph
  133. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems • Allow object to alter behavior when internal state changes
  134. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems • Allow object to alter behavior when internal state changes Provide the Same Benefits
  135. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems • Allow object to alter behavior when internal state changes Provide the Same Benefits • Increased Flexibility
  136. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems • Allow object to alter behavior when internal state changes Provide the Same Benefits • Increased Flexibility • Increased Control
  137. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems • Allow object to alter behavior when internal state changes Provide the Same Benefits • Increased Flexibility • Increased Control • Increased Visibility