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.

737023cf85d780c0b07bc0b5d97d6d6a?s=128

willroth

July 23, 2015
Tweet

Transcript

  1. Fear Not the Machine of State!

  2. None
  3. Yitzchok Willroth

  4. Yitzchok Willroth

  5. Yitzchok Willroth @coderabbi

  6. Yitzchok Willroth @coderabbi

  7. Yitzchok Willroth @coderabbi

  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. State Pattern

  20. State Pattern • Behavioral Pattern

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

    alter its behavior when its internal state changes, giving the appearance of changing class.
  22. None
  23. None
  24. None
  25. None
  26. None
  27. None
  28. None
  29. None
  30. None
  31. 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.
  32. None
  33. None
  34. None
  35. IDENTIFY STATES

  36. None
  37. Locked Coin Unlock Pass Lock Unlocked

  38. Locked Coin Unlock Pass Lock Unlocked

  39. None
  40. IDENTIFY TRANSITIONS

  41. None
  42. Locked Coin Unlock Pass Lock Unlocked

  43. Locked Coin Unlock Pass Lock Unlocked

  44. Locked Coin Unlock Pass Lock Unlocked

  45. Locked Coin Unlock Pass Lock Unlocked

  46. Locked Coin Unlock Pass Lock Unlocked

  47. Locked Coin Unlock Pass Lock Unlocked

  48. Locked Coin Unlock Pass Lock Unlocked

  49. Locked Coin Unlock Pass Lock Unlocked

  50. None
  51. None
  52. 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; } }
  53. 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; } }
  54. 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; } }
  55. 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; } }
  56. 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; } }
  57. 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; } }
  58. 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; } }
  59. 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; } }
  60. 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; } }
  61. 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; } }
  62. None
  63. None
  64. Locked Coin Unlock Pass Lock Unlocked

  65. Locked Coin Unlock Pass Lock Unlocked Pass

  66. Locked Coin Unlock Pass Lock Unlocked Pass Alarm

  67. Locked Coin Unlock Pass Lock Unlocked Pass Alarm

  68. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin

  69. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

  70. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

  71. None
  72. None
  73. // 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 ...
  74. // 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 ...
  75. // 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 ...
  76. // 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 ...
  77. // 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 ...
  78. None
  79. None
  80. Locked Coin Unlock Pass Lock Unlocked Coin Refund

  81. Locked Coin Unlock Pass Lock Unlocked Coin Refund Violation

  82. Locked Coin Unlock Pass Lock Unlocked Pass Coin Refund Violation

  83. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation
  84. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation
  85. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Ready
  86. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

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

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

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

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

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

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

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

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

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

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

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  97. None
  98. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

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

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

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

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

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

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

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

  107. None
  108. None
  109. 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); } }
  110. 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); } }
  111. 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); } }
  112. 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); } }
  113. 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); } }
  114. 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); } }
  115. 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); } }
  116. 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); } }
  117. 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); } }
  118. 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); } }
  119. 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); } }
  120. 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); } }
  121. 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); } }
  122. None
  123. abstract class TurnstileState { public function pass(Turnstile $turnstile) { //empty

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  149. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

    Violation Reset ResetAlarm Ready ResetAlarm Lock Coin Refund Pass
  150. None
  151. None
  152. 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); } }
  153. 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); } }
  154. 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); } }
  155. 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); } }
  156. 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); } }
  157. 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); } }
  158. 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); } }
  159. None
  160. 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 } }
  161. 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 } }
  162. 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 } }
  163. 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 } }
  164. None
  165. 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); } }
  166. 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); } }
  167. 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); } }
  168. 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); } }
  169. 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); } }
  170. 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); } }
  171. None
  172. 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); } }
  173. 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); } }
  174. 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); } }
  175. 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); } }
  176. None
  177. None
  178. Locked Coin Unlock Pass Lock Unlocked Pass Alarm Coin Refund

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

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

    classes To include new event triggers
  182. 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
  183. 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
  184. None
  185. Flexibility

  186. CONTROL

  187. VISIBILITY

  188. None
  189. None
  190. None
  191. None
  192. None
  193. None
  194. • State Variables

  195. • State Variables • $status

  196. • State Variables • $status • $state

  197. • State Variables • $status • $state • Boolean Driven

    Development
  198. • State Variables • $status • $state • Boolean Driven

    Development • $active, $published
  199. • State Variables • $status • $state • Boolean Driven

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

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

    Development • $active, $published • isActive(), isPublished() • Nullable Timestamps • Multipart Conditionals or Switches
  202. None
  203. None
  204. None
  205. None
  206. None
  207. None
  208. None
  209. None
  210. None
  211. None
  212. None
  213. None
  214. None
  215. None
  216. None
  217. github.com/sebastianbergmann/state

  218. None
  219. None
  220. <?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>
  221. None
  222. 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
  223. None
  224. 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; } }
  225. 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; } }
  226. 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; } }
  227. 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; } }
  228. 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; } }
  229. 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; } }
  230. 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; } }
  231. 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; } }
  232. None
  233. class ClosedDoorState extends AbstractDoorState { public function open() { return

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

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

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

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

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

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

  242. • Context Object maintains a reference to the Stateful Object.

    • States & Transitions are mapped and maintained by the Context Object.
  243. • 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.
  244. • 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.
  245. None
  246. github.com/yohang/finite

  247. None
  248. None
  249. class Document implements Finite\StatefulInterface { private $state; public function getFiniteState()

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

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

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

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

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

    { return $this->state; } public function setFiniteState($state) { $this->state = $state; } }
  255. None
  256. $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();
  257. $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();
  258. $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();
  259. $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();
  260. $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();
  261. $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();
  262. $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();
  263. None
  264. 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"
  265. 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"
  266. 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"
  267. 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"
  268. 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"
  269. None
  270. None
  271. Is Your State & Transition Graph

  272. • exceptionally complex? Is Your State & Transition Graph

  273. • exceptionally complex? • subject to frequent change? Is Your

    State & Transition Graph
  274. • exceptionally complex? • subject to frequent change? • externally

    configurable? Is Your State & Transition Graph
  275. None
  276. Follow Roughly the Same Design

  277. Follow Roughly the Same Design • Wrapper

  278. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

  279. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation
  280. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems
  281. Follow Roughly the Same Design • Wrapper • Polymorphic “Wrappee”

    • Delegation Solve the Same Problems • Allow object to alter behavior when internal state changes
  282. 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
  283. 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
  284. 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
  285. 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
  286. None
  287. Fear Not the Machine of State!