forked from faif/python-patterns
-
Notifications
You must be signed in to change notification settings - Fork 0
/
command.py
107 lines (77 loc) · 2.83 KB
/
command.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""
Command pattern decouples the object invoking a job from the one who knows
how to do it. As mentioned in the GoF book, a good example is in menu items.
You have a menu that has lots of items. Each item is responsible for doing a
special thing and you want your menu item just call the execute method when
it is pressed. To achieve this you implement a command object with the execute
method for each menu item and pass to it.
*About the example
We have a menu containing two items. Each item accepts a file name, one hides the file
and the other deletes it. Both items have an undo option.
Each item is a MenuItem class that accepts the corresponding command as input and executes
it's execute method when it is pressed.
*TL;DR
Object oriented implementation of callback functions.
*Examples in Python ecosystem:
Django HttpRequest (without execute method):
https://docs.djangoproject.com/en/2.1/ref/request-response/#httprequest-objects
"""
from typing import List, Union
class HideFileCommand:
"""
A command to hide a file given its name
"""
def __init__(self) -> None:
# an array of files hidden, to undo them as needed
self._hidden_files: List[str] = []
def execute(self, filename: str) -> None:
print(f"hiding {filename}")
self._hidden_files.append(filename)
def undo(self) -> None:
filename = self._hidden_files.pop()
print(f"un-hiding {filename}")
class DeleteFileCommand:
"""
A command to delete a file given its name
"""
def __init__(self) -> None:
# an array of deleted files, to undo them as needed
self._deleted_files: List[str] = []
def execute(self, filename: str) -> None:
print(f"deleting {filename}")
self._deleted_files.append(filename)
def undo(self) -> None:
filename = self._deleted_files.pop()
print(f"restoring {filename}")
class MenuItem:
"""
The invoker class. Here it is items in a menu.
"""
def __init__(self, command: Union[HideFileCommand, DeleteFileCommand]) -> None:
self._command = command
def on_do_press(self, filename: str) -> None:
self._command.execute(filename)
def on_undo_press(self) -> None:
self._command.undo()
def main():
"""
>>> item1 = MenuItem(DeleteFileCommand())
>>> item2 = MenuItem(HideFileCommand())
# create a file named `test-file` to work with
>>> test_file_name = 'test-file'
# deleting `test-file`
>>> item1.on_do_press(test_file_name)
deleting test-file
# restoring `test-file`
>>> item1.on_undo_press()
restoring test-file
# hiding `test-file`
>>> item2.on_do_press(test_file_name)
hiding test-file
# un-hiding `test-file`
>>> item2.on_undo_press()
un-hiding test-file
"""
if __name__ == "__main__":
import doctest
doctest.testmod()