6.2. State¶
EN: State
PL: Stan
Type: object
6.2.1. Pattern¶
Changes based on class
Open/Close principle
Using polymorphism

6.2.2. Problem¶
Canvas object can behave differently depending on selected Tool
All behaviors are represented by subclass of the tool interface

from enum import Enum
class Tool(Enum):
SELECTION = 1
PENCIL = 2
ERASE = 3
BRUSH = 4
class Window:
current_tool: Tool
def on_left_mouse_button(self):
if self.current_tool == Tool.SELECTION:
print('Select')
elif self.current_tool == Tool.PENCIL:
print('Draw')
elif self.current_tool == Tool.ERASE:
print('Erase')
elif self.current_tool == Tool.BRUSH:
print('Paint')
def on_right_mouse_button(self):
if self.current_tool == Tool.SELECTION:
print('Unselect')
elif self.current_tool == Tool.PENCIL:
print('Stop drawing')
elif self.current_tool == Tool.ERASE:
print('Undo erase')
elif self.current_tool == Tool.BRUSH:
print('Stop painting')
if __name__ == '__main__':
window = Window()
window.current_tool = Tool.BRUSH
window.on_left_mouse_button()
window.on_right_mouse_button()
window.current_tool = Tool.SELECTION
window.on_left_mouse_button()
window.on_right_mouse_button()
window.current_tool = Tool.ERASE
window.on_left_mouse_button()
window.on_right_mouse_button()
# Paint
# Stop painting
# Select
# Unselect
# Erase
# Undo erase
6.2.3. Solution¶

from abc import ABC, abstractmethod
#%% Abstracts
class WindowEvents(ABC):
@abstractmethod
def on_left_mouse_button(self): ...
@abstractmethod
def on_right_mouse_button(self): ...
class Tool(WindowEvents, ABC):
pass
#%% Tools
class SelectionTool(Tool):
def on_left_mouse_button(self):
print('Select')
def on_right_mouse_button(self):
print('Unselect')
class EraseTool(Tool):
def on_left_mouse_button(self):
print('Erase')
def on_right_mouse_button(self):
print('Undo erase')
class PencilTool(Tool):
def on_left_mouse_button(self):
print('Draw')
def on_right_mouse_button(self):
print('Stop drawing')
class BrushTool(Tool):
def on_left_mouse_button(self):
print('Paint')
def on_right_mouse_button(self):
print('Stop painting')
#%% Main
class Window(WindowEvents):
current_tool: Tool
def on_left_mouse_button(self):
self.current_tool.on_left_mouse_button()
def on_right_mouse_button(self):
self.current_tool.on_right_mouse_button()
if __name__ == '__main__':
window = Window()
window.current_tool = BrushTool()
window.on_left_mouse_button()
window.on_right_mouse_button()
window.current_tool = SelectionTool()
window.on_left_mouse_button()
window.on_right_mouse_button()
window.current_tool = EraseTool()
window.on_left_mouse_button()
window.on_right_mouse_button()
# Paint
# Stop painting
# Select
# Unselect
# Erase
# Undo erase
6.2.4. Use Case - 0x01¶

Figure 6.1. GIMP (GNU Image Manipulation Project) window with tools and canvas [1]¶
>>> class Tool:
... def on_mouse_over(self): raise NotImplementedError
... def on_mouse_out(self): raise NotImplementedError
... def on_mouse_click_leftbutton(self): raise NotImplementedError
... def on_mouse_unclick_leftbutton(self): raise NotImplementedError
... def on_mouse_click_rightbutton(self): raise NotImplementedError
... def on_mouse_unclick_rightbutton(self): raise NotImplementedError
... def on_key_press(self): raise NotImplementedError
... def on_key_unpress(self): raise NotImplementedError
>>>
>>>
>>> class Pencil(Tool):
... def on_mouse_over(self):
... ...
...
... def on_mouse_out(self):
... ...
...
... def on_mouse_click_leftbutton(self):
... ...
...
... def on_mouse_unclick_leftbutton(self):
... ...
...
... def on_mouse_click_rightbutton(self):
... ...
...
... def on_mouse_unclick_rightbutton(self):
... ...
...
... def on_key_press(self):
... ...
...
... def on_key_unpress(self):
... ...
>>>
>>>
>>> class Pen(Tool):
... def on_mouse_over(self):
... ...
...
... def on_mouse_out(self):
... ...
...
... def on_mouse_click_leftbutton(self):
... ...
...
... def on_mouse_unclick_leftbutton(self):
... ...
...
... def on_mouse_click_rightbutton(self):
... ...
...
... def on_mouse_unclick_rightbutton(self):
... ...
...
... def on_key_press(self):
... ...
...
... def on_key_unpress(self):
... ...
6.2.5. References¶
6.2.6. Assignments¶
"""
* Assignment: DesignPatterns Behavioral State
* Complexity: medium
* Lines of code: 34 lines
* Time: 13 min
English:
1. Implement State pattern
2. Then add another language:
a. Chinese hello: 你好
b. Chinese goodbye: 再见
3. Run doctests - all must succeed
Polish:
1. Zaimplementuj wzorzec State
2. Następnie dodaj nowy język:
a. Chinese hello: 你好
b. Chinese goodbye: 再见
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> polish = Translation(Polish())
>>> english = Translation(English())
>>> chinese = Translation(Chinese())
>>> polish.hello()
'Cześć'
>>> polish.goodbye()
'Do widzenia'
>>> english.hello()
'Hello'
>>> english.goodbye()
'Goodbye'
>>> chinese.hello()
'你好'
>>> chinese.goodbye()
'再见'
"""
from enum import Enum
class Language(Enum):
POLISH = 'pl'
ENGLISH = 'en'
SPANISH = 'es'
class Translation:
language: Language
def __init__(self, language: Language):
self.language = language
def hello(self) -> str:
if self.language is Language.POLISH:
return 'Cześć'
elif self.language is Language.ENGLISH:
return 'Hello'
elif self.language is Language.SPANISH:
return 'Buenos Días'
else:
return 'Unknown language'
def goodbye(self) -> str:
if self.language is Language.POLISH:
return 'Do widzenia'
elif self.language is Language.ENGLISH:
return 'Goodbye'
elif self.language is Language.SPANISH:
return 'Adiós'
else:
return 'Unknown language'