@@ -10,74 +10,180 @@ Behavioral Pattern: Chain of Responsibility
1010Lets you pass requests along a chain of handlers, where each handler decides
1111whether to process the request or pass it along.
1212Promotes loose coupling between sender and receiver.
13+
14+ Class Diagram:
15+
16+ +------------------+
17+ | Handler (ABC) |
18+ +------------------+
19+ | - _next_handler |
20+ +------------------+
21+ | + set_next() |
22+ | + handle() |
23+ | + pass_to_next() |
24+ +------------------+
25+ ^
26+ |
27+ +-------------------+ +-------------------+ +----------------+
28+ | MonkeyHandler | | SquirrelHandler | | DogHandler |
29+ +-------------------+ +-------------------+ +----------------+
30+ | + handle() | | + handle() | | + handle() |
31+ +-------------------+ +-------------------+ +----------------+
32+
33+ Runtime Chain Flow:
34+
35+ Client
36+ |
37+ v
38+ MonkeyHandler --> SquirrelHandler --> CatHandler --> DogHandler --> None
39+ |
40+ [Request: "Fish"]
41+ |
42+ v
43+ CatHandler handles it!
1344"""
1445
1546from __future__ import annotations
1647from abc import ABC , abstractmethod
1748from typing import Optional
49+ import unittest
50+
1851
1952class Handler (ABC ):
20- """ Base Handler class with chaining support."""
53+ """
54+ The base Handler class declares the interface for handling requests and for
55+ setting the next handler in the chain.
56+ """
2157
2258 _next_handler: Optional[Handler] = None
2359
2460 def set_next (self , handler : Handler) -> Handler:
61+ """
62+ Set the next handler in the chain and return the handler to allow chaining.
63+ """
2564 self ._next_handler = handler
2665 return handler
2766
2867 @abstractmethod
2968 def handle (self , request : str ) -> Optional[str ]:
69+ """
70+ Handle the request or pass it to the next handler in the chain.
71+ """
3072 pass
3173
74+ def pass_to_next (self , request : str ) -> Optional[str ]:
75+ """
76+ Utility method to pass request to the next handler, if any.
77+ """
78+ if self ._next_handler:
79+ print (f " { self .__class__ .__name__ } : Can't handle { request} , passing to next... " )
80+ return self ._next_handler.handle(request)
81+ print (f " { self .__class__ .__name__ } : No next handler for { request} . " )
82+ return None
83+
84+
3285class MonkeyHandler (Handler ):
86+ """ Handler that processes requests related to Bananas."""
87+
3388 def handle (self , request : str ) -> Optional[str ]:
3489 if request == " Banana" :
3590 return " Monkey: I'll eat the Banana."
36- elif self ._next_handler:
37- return self ._next_handler.handle(request)
38- return None
91+ return self .pass_to_next(request)
92+
3993
4094class SquirrelHandler (Handler ):
95+ """ Handler that processes requests related to Nuts."""
96+
4197 def handle (self , request : str ) -> Optional[str ]:
4298 if request == " Nut" :
4399 return " Squirrel: I'll eat the Nut."
44- elif self ._next_handler:
45- return self ._next_handler.handle(request)
46- return None
100+ return self .pass_to_next(request)
101+
47102
48103class DogHandler (Handler ):
104+ """ Handler that processes requests related to Meat."""
105+
49106 def handle (self , request : str ) -> Optional[str ]:
50107 if request == " Meat" :
51108 return " Dog: I'll eat the Meat."
52- elif self ._next_handler:
53- return self ._next_handler.handle(request)
54- return None
109+ return self .pass_to_next(request)
110+
111+
112+ class CatHandler (Handler ):
113+ """ Handler that processes requests related to Fish."""
114+
115+ def handle (self , request : str ) -> Optional[str ]:
116+ if request == " Fish" :
117+ return " Cat: I'll eat the Fish."
118+ return self .pass_to_next(request)
119+
120+
121+ # Unit Tests
122+ class TestChainOfResponsibility (unittest .TestCase ):
123+ """ Test cases for the Chain of Responsibility pattern."""
124+ def setUp (self ):
125+ self .monkey = MonkeyHandler()
126+ self .squirrel = SquirrelHandler()
127+ self .cat = CatHandler()
128+ self .dog = DogHandler()
129+ self .monkey.set_next(self .squirrel).set_next(self .cat).set_next(self .dog)
130+
131+ def test_monkey_handler (self ):
132+ """ Test that the MonkeyHandler handles a request."""
133+ self .assertEqual(self .monkey.handle(" Banana" ), " Monkey: I'll eat the Banana." )
134+
135+ def test_squirrel_handler (self ):
136+ """ Test that the SquirrelHandler handles a request."""
137+ self .assertEqual(self .monkey.handle(" Nut" ), " Squirrel: I'll eat the Nut." )
138+
139+ def test_cat_handler (self ):
140+ """ Test that the CatHandler handles a request."""
141+ self .assertEqual(self .monkey.handle(" Fish" ), " Cat: I'll eat the Fish." )
142+
143+ def test_dog_handler (self ):
144+ """ Test that the DogHandler handles a request."""
145+ self .assertEqual(self .monkey.handle(" Meat" ), " Dog: I'll eat the Meat." )
146+
147+ def test_unhandled_request (self ):
148+ """ Test that an unhandled request is passed to the next handler."""
149+ self .assertIsNone(self .monkey.handle(" Apple" ))
55150
56151# Example usage
57152if __name__ == " __main__" :
153+ # Create handlers
58154 monkey = MonkeyHandler()
59155 squirrel = SquirrelHandler()
60156 dog = DogHandler()
157+ cat = CatHandler()
61158
62- monkey.set_next(squirrel).set_next(dog)
159+ # Dynamically construct chain
160+ monkey.set_next(squirrel).set_next(cat).set_next(dog)
63161
64- for food in [" Nut" , " Banana" , " Meat" , " Apple" ]:
65- print (f " Client: Who wants a { food} ? " )
162+ # Test inputs
163+ for food in [" Nut" , " Banana" , " Meat" , " Fish" , " Apple" ]:
164+ print (f " \n Client: Who wants a { food} ? " )
66165 result = monkey.handle(food)
67166 if result:
68167 print (result)
69168 else :
70169 print (f " No one wants the { food} . " )
170+
171+ # Unit Tests
172+ unittest.main()
173+
71174```
72175
73176## Summary
74- Implementation of the Chain of Responsibility pattern in Python.
177+ Implementation of the Chain of Responsibility pattern in Python, demonstrating a request-passing mechanism through a series of handlers .
75178
76179## Docstrings
77- - Base Handler class with chaining support.
78- - Sets the next handler in the chain and returns the next handler.
79- - Abstract method to handle requests. Must be implemented by subclasses.
80- - Handler for monkeys that can eat bananas.
81- - Handler for squirrels that can eat nuts.
82- - Handler for dogs that can eat meat.
180+ - The base Handler class declares the interface for handling requests and for setting the next handler in the chain.
181+ - Set the next handler in the chain and return the handler to allow chaining.
182+ - Handle the request or pass it to the next handler in the chain.
183+ - Utility method to pass request to the next handler, if any.
184+ - Handler that processes requests related to Bananas.
185+ - Handler that processes requests related to Nuts.
186+ - Handler that processes requests related to Meat.
187+ - Handler that processes requests related to Fish.
188+ - Test cases for the Chain of Responsibility pattern.
83189
0 commit comments