As a material for learning GoF design patterns, the book "Introduction to Design Patterns Learned in the Augmented and Revised Java Language" seems to be helpful. However, since the examples taken up are based on JAVA, I tried the same practice in Python to deepen my understanding.
The English word "Chain of Responsibility" means "chain of responsibility". This pattern builds a chain relationship between multiple objects that are the target of receiving a request, and sequentially receives requests along the built chain relationship until it reaches an object that can process the request. It is a pattern to go. By applying this pattern, the user side does not have to be aware of the command tower role (connection) such as "this request is processed by this object", and the user side "to any object in a chain relationship". The division of roles becomes clear: "just throw a request", and the processing side "processes the incoming request if it can be processed by itself, and if it cannot, just pass the request to the next object". (The user does not need to be aware of the details of the process)
UML class and sequence diagram UML class diagram (The above is quoted from "Technical Support Site for IT Engineers by IT Senka")
The Chain of Responsibility
pattern is reminiscent of the old-fashioned bureaucratic" working style ".
Requests come to people. If the person can handle it, do it. If it can't be processed, it circulates the request to the "next person". If the next person can handle it, do it. If it cannot be processed, the request is circulated to "the next person".
I understand the Chain of Responsibility
pattern as a so-called linear list-like processing model.
Actually, I would like to run a sample program that utilizes the Chain of Responsibility pattern and check the following behavior.
――For the matter necessary for troubleshooting, assign a trouble number
and solve the problem according to the following trouble-solving business flow.
--When the trouble is solved, the solution result is output.
--If the trouble cannot be solved, it will end with "Unsolvable".
$ python Main.py
[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].
Somehow, the output result was difficult to understand. To understand the output result of the sample program, it seems faster to check the details of the sample program.
Similar code has been uploaded to the Git repository. https://github.com/ttsubo/study_of_design_pattern/tree/master/Chain_of_Responsibility
--Directory structure
.
├── Main.py
├── support.py
└── trouble.py
The Handler
role defines the interface for processing requests.
Keep the "next person", and if you receive a request that you cannot handle, you can send it to that person. Of course, the "next person" also plays the role of Handler
.
In the sample program, the Support
class serves this role.
The method that handles the request was the support
method.
support.py
from abc import ABCMeta, abstractmethod
class Support(metaclass=ABCMeta):
def __init__(self, name):
self.__name = name
self.__next = None
def setNext(self, next):
self.__next = next
return next
def support(self, trouble):
if self.resolve(trouble):
self.done(trouble)
elif self.__next is not None:
self.__next.support(trouble)
else:
self.fail(trouble)
def __str__(self):
return "[{0}]".format(self.__name)
@abstractmethod
def resolve(self, trouble):
pass
def done(self, trouble):
print("{0} is resolved by {1}.".format(trouble, self))
def fail(self, trouble):
print("{0} cannot be resolved.".format(trouble))
The ConcreteHandler
role is a specific role for processing a request.
In the sample program, the NoSupport
, LimitSupport
, ʻOddSupport, and
SpecialSupport` classes serve this role.
support.py
class NoSupport(Support):
def __init__(self, name):
super(NoSupport, self).__init__(name)
def resolve(self, trouble):
return False
class LimitSupport(Support):
def __init__(self, name, limit):
super(LimitSupport, self).__init__(name)
self.__limit = limit
def resolve(self, trouble):
return True if trouble.getNumber() < self.__limit else False
class OddSupport(Support):
def __init__(self, name):
super(OddSupport, self).__init__(name)
def resolve(self, trouble):
return True if trouble.getNumber() % 2 == 1 else False
class SpecialSupport(Support):
def __init__(self, name, number):
super(SpecialSupport, self).__init__(name)
self.__number = number
def resolve(self, trouble):
return True if trouble.getNumber() == self.__number else False
The Client
role is the role that makes a request to the first ConcreteHandler
role.
In the sample program, the startMain
method serves this role.
Main.py
from support import NoSupport, LimitSupport, SpecialSupport, OddSupport
from trouble import Trouble
def startMain():
alice = NoSupport("Alice")
bob = LimitSupport("Bob", 100)
charlie = SpecialSupport("Charlie", 429)
diana = LimitSupport("Diana", 200)
elmo = OddSupport("Elmo")
fred = LimitSupport("Fred", 300)
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred)
for i in range(0, 500, 33):
alice.support(Trouble(i))
if __name__ == '__main__':
startMain()
Centrally manage trouble numbers.
trouble.py
class Trouble:
def __init__(self, number):
self.__number = number
def getNumber(self):
return self.__number
def __str__(self):
return '[Trouble {0}]'.format(self.__number)
-[Finishing "Introduction to Design Patterns Learned in Java Language" (Not)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java Introduction to Design Patterns Learned in Language-Finishing-Not-2cc9b34a30b2)
Recommended Posts