Skip to content

Commit

Permalink
Add pattern State implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
balancy committed Oct 10, 2023
1 parent ee91d20 commit 024ed7b
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 1 deletion.
1 change: 1 addition & 0 deletions patterns/chapter_10_state/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Init file for chapter_10_state package."""
41 changes: 41 additions & 0 deletions patterns/chapter_10_state/gumball_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Gumball machine module."""
from .state import NoQuarterState, SoldOutState, State


class GumballMachine:
"""Gumball machine class."""

def __init__(self, gumballs_amount: int) -> None:
"""Initialize with state and gubmalls amount."""
self._state: State = (
NoQuarterState() if gumballs_amount > 0 else SoldOutState()
)
self._gubmalls_amount = gumballs_amount

def _transition_to(self, state: State | None) -> None:
"""Transition to a new state."""
if state is not None:
self._state = state

def insert_quarter(self) -> None:
"""Insert quarter method."""
new_state: State | None = self._state.insert_quarter()
self._transition_to(new_state)

def eject_quarter(self) -> None:
"""Eject quarter method."""
new_state: State | None = self._state.eject_quarter()
self._transition_to(new_state)

def turn_crunk(self) -> None:
"""Turn crank method."""
new_state: State | None = self._state.turn_crank()
self._transition_to(new_state)

def dispense(self) -> None:
"""Dispense gumball method."""
is_gumball_last = self._gubmalls_amount == 1
new_state: State | None = self._state.dispense(is_gumball_last)
if new_state is not None:
self._gubmalls_amount -= 1
self._transition_to(new_state)
54 changes: 54 additions & 0 deletions patterns/chapter_10_state/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""State module."""

from .gumball_machine import GumballMachine


def run_pattern_example() -> None:
"""Run pattern example."""
machine = GumballMachine(3)

print()
print('Trying to manipulate the machine without coins')
machine.turn_crunk()
machine.dispense()
machine.eject_quarter()

print()
print('Trying to insert two coins')
machine.insert_quarter()
machine.insert_quarter()

print()
print('Trying to eject coin two times')
machine.eject_quarter()
machine.eject_quarter()

print()
print('Trying to turn crank two times')
machine.insert_quarter()
machine.turn_crunk()
machine.turn_crunk()

print()
print('Trying to dispense gumball two times')
machine.dispense()
machine.dispense()

print()
print('Use machine the normal way')
machine.insert_quarter()
machine.turn_crunk()
machine.dispense()
machine.insert_quarter()
machine.turn_crunk()
machine.dispense()

print()
print('Trying to manipulate the sold out machine')
machine.insert_quarter()
machine.turn_crunk()
machine.dispense()


if __name__ == "__main__":
run_pattern_example()
126 changes: 126 additions & 0 deletions patterns/chapter_10_state/state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""Abstract state class and 4 its implementations."""

from __future__ import annotations

from abc import ABC, abstractmethod


class State(ABC):
"""Abstract state class."""

@abstractmethod
def insert_quarter(self) -> State | None:
"""Abstract insert quarter method.
All child classes should implement this method.
"""

@abstractmethod
def eject_quarter(self) -> State | None:
"""Abstract eject quarter method.
All child classes should implement this method.
"""

@abstractmethod
def turn_crank(self) -> State | None:
"""Abstract turn crank method.
All child classes should implement this method.
"""

@abstractmethod
def dispense(self, is_gumball_last: bool) -> State | None:
"""Abstract dispense gumballn method.
All child classes should implement this method.
"""


class NoQuarterState(State):
"""No quarter state class."""

def insert_quarter(self) -> State:
"""Insert quarter method implementation."""
print("You inserted a quarter")
return HasQuarterState()

def eject_quarter(self) -> None:
"""Eject quarter method implementation."""
print("You haven't inserted a quarter")

def turn_crank(self) -> None:
"""Turn crank method implementation."""
print("You turned, but there's no quarter")

def dispense(self, is_gumball_last: bool) -> None:
"""Dispense gumball method implementation."""
print("You need to pay first")


class HasQuarterState(State):
"""Has quarter state class."""

def insert_quarter(self) -> None:
"""Insert quarter method implementation."""
print("You can't insert another quarter")

def eject_quarter(self) -> State:
"""Eject quarter method implementation."""
print("Quarter returned")
return NoQuarterState()

def turn_crank(self) -> State:
"""Turn crank method implementation."""
print("You turned...")
return SoldState()

def dispense(self, is_gumball_last: bool) -> None:
"""Dispense gumball method implementation."""
print("No gumball dispensed")


class SoldState(State):
"""Sold state class."""

def insert_quarter(self) -> None:
"""Insert quarter method implementation."""
print("Wait for ball")

def eject_quarter(self) -> None:
"""Eject quarter method implementation."""
print("You already turned crank")

def turn_crank(self) -> State:
"""Turn crank method implementation."""
print("Even if you turn twice you got one ball")
return SoldState()

def dispense(self, is_gumball_last: bool) -> State:
"""Dispense gumball method implementation."""
print("A gumball comes rolling out the slot...")
if not is_gumball_last:
return NoQuarterState()

print('Machine is sold out!')
return SoldOutState()


class SoldOutState(State):
"""Sold out state class."""

def insert_quarter(self) -> None:
"""Insert quarter method implementation."""
print("You can't insert a quarter, the machine is sold out")

def eject_quarter(self) -> None:
"""Eject quarter method implementation."""
print("You haven't inserted a quarter")

def turn_crank(self) -> None:
"""Turn crank method implementation."""
print("You turned, but there is no gumballs")

def dispense(self, is_gumball_last: bool) -> None:
"""Dispense gumball method implementation."""
print("No gumball dispensed")
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ omit = [
parallel = true

[tool.coverage.report]
fail_under = 80
fail_under = 75
show_missing = true

0 comments on commit 024ed7b

Please sign in to comment.