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. Fear Not
    the Machine
    of State!

    View full-size slide

  2. Yitzchok
    Willroth

    View full-size slide

  3. Yitzchok
    Willroth

    View full-size slide

  4. Yitzchok
    Willroth
    @coderabbi

    View full-size slide

  5. Yitzchok
    Willroth
    @coderabbi

    View full-size slide

  6. Yitzchok
    Willroth
    @coderabbi

    View full-size slide

  7. State Pattern

    View full-size slide

  8. State Pattern
    • Behavioral Pattern

    View full-size slide

  9. State Pattern
    • Behavioral Pattern
    • Allows an object to alter its behavior when its
    internal state changes, giving the appearance of
    changing class.

    View full-size slide

  10. 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.

    View full-size slide

  11. IDENTIFY
    STATES

    View full-size slide

  12. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  13. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  14. IDENTIFY
    TRANSITIONS

    View full-size slide

  15. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  16. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  17. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  18. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  19. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  20. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  21. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  22. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  23. 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;
    }
    }

    View full-size slide

  24. 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;
    }
    }

    View full-size slide

  25. 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;
    }
    }

    View full-size slide

  26. 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;
    }
    }

    View full-size slide

  27. 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;
    }
    }

    View full-size slide

  28. 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;
    }
    }

    View full-size slide

  29. 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;
    }
    }

    View full-size slide

  30. 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;
    }
    }

    View full-size slide

  31. 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;
    }
    }

    View full-size slide

  32. 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;
    }
    }

    View full-size slide

  33. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked

    View full-size slide

  34. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked
    Pass

    View full-size slide

  35. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked
    Pass
    Alarm

    View full-size slide

  36. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked
    Pass
    Alarm

    View full-size slide

  37. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked
    Pass
    Alarm
    Coin

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  40. // 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 ...

    View full-size slide

  41. // 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 ...

    View full-size slide

  42. // 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 ...

    View full-size slide

  43. // 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 ...

    View full-size slide

  44. // 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 ...

    View full-size slide

  45. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked
    Coin
    Refund

    View full-size slide

  46. Locked
    Coin
    Unlock
    Pass
    Lock
    Unlocked
    Coin
    Refund
    Violation

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  70. 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);
    }
    }

    View full-size slide

  71. 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);
    }
    }

    View full-size slide

  72. 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);
    }
    }

    View full-size slide

  73. 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);
    }
    }

    View full-size slide

  74. 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);
    }
    }

    View full-size slide

  75. 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);
    }
    }

    View full-size slide

  76. 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);
    }
    }

    View full-size slide

  77. 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);
    }
    }

    View full-size slide

  78. 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);
    }
    }

    View full-size slide

  79. 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);
    }
    }

    View full-size slide

  80. 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);
    }
    }

    View full-size slide

  81. 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);
    }
    }

    View full-size slide

  82. 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);
    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  106. 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);
    }
    }

    View full-size slide

  107. 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);
    }
    }

    View full-size slide

  108. 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);
    }
    }

    View full-size slide

  109. 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);
    }
    }

    View full-size slide

  110. 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);
    }
    }

    View full-size slide

  111. 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);
    }
    }

    View full-size slide

  112. 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);
    }
    }

    View full-size slide

  113. 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
    }
    }

    View full-size slide

  114. 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
    }
    }

    View full-size slide

  115. 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
    }
    }

    View full-size slide

  116. 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
    }
    }

    View full-size slide

  117. 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);
    }
    }

    View full-size slide

  118. 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);
    }
    }

    View full-size slide

  119. 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);
    }
    }

    View full-size slide

  120. 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);
    }
    }

    View full-size slide

  121. 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);
    }
    }

    View full-size slide

  122. 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);
    }
    }

    View full-size slide

  123. 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);
    }
    }

    View full-size slide

  124. 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);
    }
    }

    View full-size slide

  125. 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);
    }
    }

    View full-size slide

  126. 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);
    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  129. Extend the public API of the
    “context” & Abstract “state” classes
    To include new event triggers

    View full-size slide

  130. 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

    View full-size slide

  131. 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

    View full-size slide

  132. • State Variables

    View full-size slide

  133. • State Variables
    • $status

    View full-size slide

  134. • State Variables
    • $status
    • $state

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  139. • State Variables
    • $status
    • $state
    • Boolean Driven Development
    • $active, $published
    • isActive(), isPublished()
    • Nullable Timestamps
    • Multipart Conditionals or Switches

    View full-size slide

  140. github.com/sebastianbergmann/state

    View full-size slide

  141. 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

    View full-size slide

  142. 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;
    }
    }

    View full-size slide

  143. 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;
    }
    }

    View full-size slide

  144. 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;
    }
    }

    View full-size slide

  145. 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;
    }
    }

    View full-size slide

  146. 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;
    }
    }

    View full-size slide

  147. 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;
    }
    }

    View full-size slide

  148. 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;
    }
    }

    View full-size slide

  149. 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;
    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  158. • 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.

    View full-size slide

  159. • 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.

    View full-size slide

  160. github.com/yohang/finite

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  167. $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();

    View full-size slide

  168. $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();

    View full-size slide

  169. $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();

    View full-size slide

  170. $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();

    View full-size slide

  171. $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();

    View full-size slide

  172. $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();

    View full-size slide

  173. $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();

    View full-size slide

  174. 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"

    View full-size slide

  175. 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"

    View full-size slide

  176. 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"

    View full-size slide

  177. 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"

    View full-size slide

  178. 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"

    View full-size slide

  179. Is Your State & Transition Graph

    View full-size slide

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

    View full-size slide

  181. • exceptionally complex?
    • subject to frequent change?
    Is Your State & Transition Graph

    View full-size slide

  182. • exceptionally complex?
    • subject to frequent change?
    • externally configurable?
    Is Your State & Transition Graph

    View full-size slide

  183. Follow Roughly the Same Design

    View full-size slide

  184. Follow Roughly the Same Design
    • Wrapper

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  189. 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

    View full-size slide

  190. 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

    View full-size slide

  191. 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

    View full-size slide

  192. 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

    View full-size slide

  193. Fear Not
    the Machine
    of State!

    View full-size slide