Skip to content

Commit 4f647ad

Browse files
committed
Separate chain_of_responsibility script per python version
1 parent 4292a34 commit 4f647ad

File tree

4 files changed

+141
-27
lines changed

4 files changed

+141
-27
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ __Behavioral Patterns__:
3737

3838
| Pattern | Description |
3939
|:-------:| ----------- |
40-
| [chain_of_responsibility](patterns/behavioral/chain_of_responsibility.py) | apply a chain of successive handlers to try and process the data |
40+
| [chain_of_responsibility](patterns/behavioral/chain_of_responsibility__py3.py) | apply a chain of successive handlers to try and process the data |
4141
| [catalog](patterns/behavioral/catalog.py) | general methods will call different specialized methods based on construction parameter |
4242
| [chaining_method](patterns/behavioral/chaining_method.py) | continue callback next object method |
4343
| [command](patterns/behavioral/command.py) | bundle a command and arguments to call later |

patterns/behavioral/chain_of_responsibility.py renamed to patterns/behavioral/chain_of_responsibility__py2.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -92,29 +92,28 @@ def check_range(request):
9292

9393

9494
def main():
95-
h0 = ConcreteHandler0()
96-
h1 = ConcreteHandler1()
97-
h2 = ConcreteHandler2(FallbackHandler())
98-
h0.successor = h1
99-
h1.successor = h2
100-
101-
requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
102-
for request in requests:
103-
h0.handle(request)
95+
"""
96+
>>> h0 = ConcreteHandler0()
97+
>>> h1 = ConcreteHandler1()
98+
>>> h2 = ConcreteHandler2(FallbackHandler())
99+
>>> h0.successor = h1
100+
>>> h1.successor = h2
101+
102+
>>> requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
103+
>>> for request in requests:
104+
... h0.handle(request)
105+
request 2 handled in handler 0
106+
request 5 handled in handler 0
107+
request 14 handled in handler 1
108+
request 22 handled in handler 2
109+
request 18 handled in handler 1
110+
request 3 handled in handler 0
111+
end of chain, no handler for 35
112+
request 27 handled in handler 2
113+
request 20 handled in handler 2
114+
"""
104115

105116

106117
if __name__ == "__main__":
107-
main()
108-
109-
110-
OUTPUT = """
111-
request 2 handled in handler 0
112-
request 5 handled in handler 0
113-
request 14 handled in handler 1
114-
request 22 handled in handler 2
115-
request 18 handled in handler 1
116-
request 3 handled in handler 0
117-
end of chain, no handler for 35
118-
request 27 handled in handler 2
119-
request 20 handled in handler 2
120-
"""
118+
import doctest
119+
doctest.testmod(optionflags=doctest.ELLIPSIS)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
*What is this pattern about?
6+
7+
The Chain of responsibility is an object oriented version of the
8+
`if ... elif ... elif ... else ...` idiom, with the
9+
benefit that the condition–action blocks can be dynamically rearranged
10+
and reconfigured at runtime.
11+
12+
This pattern aims to decouple the senders of a request from its
13+
receivers by allowing request to move through chained
14+
receivers until it is handled.
15+
16+
Request receiver in simple form keeps a reference to a single successor.
17+
As a variation some receivers may be capable of sending requests out
18+
in several directions, forming a `tree of responsibility`.
19+
20+
*TL;DR80
21+
Allow a request to pass down a chain of receivers until it is handled.
22+
"""
23+
24+
import abc
25+
26+
27+
class Handler(metaclass=abc.ABCMeta):
28+
29+
def __init__(self, successor=None):
30+
self.successor = successor
31+
32+
def handle(self, request):
33+
"""
34+
Handle request and stop.
35+
If can't - call next handler in chain.
36+
37+
As an alternative you might even in case of success
38+
call the next handler.
39+
"""
40+
res = self.check_range(request)
41+
if not res and self.successor:
42+
self.successor.handle(request)
43+
44+
@abc.abstractmethod
45+
def check_range(self, request):
46+
"""Compare passed value to predefined interval"""
47+
48+
49+
class ConcreteHandler0(Handler):
50+
"""Each handler can be different.
51+
Be simple and static...
52+
"""
53+
54+
@staticmethod
55+
def check_range(request):
56+
if 0 <= request < 10:
57+
print("request {} handled in handler 0".format(request))
58+
return True
59+
60+
61+
class ConcreteHandler1(Handler):
62+
"""... With it's own internal state"""
63+
64+
start, end = 10, 20
65+
66+
def check_range(self, request):
67+
if self.start <= request < self.end:
68+
print("request {} handled in handler 1".format(request))
69+
return True
70+
71+
72+
class ConcreteHandler2(Handler):
73+
"""... With helper methods."""
74+
75+
def check_range(self, request):
76+
start, end = self.get_interval_from_db()
77+
if start <= request < end:
78+
print("request {} handled in handler 2".format(request))
79+
return True
80+
81+
@staticmethod
82+
def get_interval_from_db():
83+
return (20, 30)
84+
85+
86+
class FallbackHandler(Handler):
87+
@staticmethod
88+
def check_range(request):
89+
print("end of chain, no handler for {}".format(request))
90+
return False
91+
92+
93+
def main():
94+
"""
95+
>>> h0 = ConcreteHandler0()
96+
>>> h1 = ConcreteHandler1()
97+
>>> h2 = ConcreteHandler2(FallbackHandler())
98+
>>> h0.successor = h1
99+
>>> h1.successor = h2
100+
101+
>>> requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
102+
>>> for request in requests:
103+
... h0.handle(request)
104+
request 2 handled in handler 0
105+
request 5 handled in handler 0
106+
request 14 handled in handler 1
107+
request 22 handled in handler 2
108+
request 18 handled in handler 1
109+
request 3 handled in handler 0
110+
end of chain, no handler for 35
111+
request 27 handled in handler 2
112+
request 20 handled in handler 2
113+
"""
114+
115+
116+
if __name__ == "__main__":
117+
import doctest
118+
doctest.testmod(optionflags=doctest.ELLIPSIS)

tests/test_outputs.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
from patterns.behavioral.catalog import main as catalog_main
1212
from patterns.behavioral.catalog import OUTPUT as catalog_output
13-
from patterns.behavioral.chain_of_responsibility import main as chain_main
14-
from patterns.behavioral.chain_of_responsibility import OUTPUT as chain_output
1513
from patterns.behavioral.chaining_method import main as chaining_method_main
1614
from patterns.behavioral.chaining_method import OUTPUT as chaining_method_output
1715
from patterns.behavioral.command import main as command_main
@@ -38,7 +36,6 @@
3836
reason="requires python3.4 or higher")
3937
@pytest.mark.parametrize("main,output", [
4038
(catalog_main, catalog_output),
41-
(chain_main, chain_output),
4239
(chaining_method_main, chaining_method_output),
4340
(command_main, command_output),
4441
(iterator_main, iterator_output),

0 commit comments

Comments
 (0)