Learn the design pattern "Chain of Responsibility" in Python

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.

■ Chain of Responsibility (Chain of Responsibility pattern)

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 W3sDesign_Chain_of_Responsibility_Design_Pattern_UML.jpg UML class diagram designpattern-chain_of_responsibility01.gif (The above is quoted from "Technical Support Site for IT Engineers by IT Senka")

□ Memorandum

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.

Chain_of_responsibility.png

■ "Chain of Responsibility" sample program

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".

(1) Decide the order of turning the tubs in advance ("Alice"-> "Bob"-> "Charlie"-> "Diana"-> "Elmo"-> "Fred") (2) First, ʻAlice` accepts the request for troubleshooting. ʻAlice` is a ** unsupported ** role, so circulate the request to the next` Bod` (3) `Bod` accepts the request. `Bod` is the role of ** limited support (upper limit of Trouble number" 100 ") **, and we will do our best within that role and end the business if we can solve it. If it cannot be resolved, circulate the request to the next `Charlie` (4) `Charlie` accepts the request. `Charlie` is the role of ** special support (Trouble number is" 429 "only) **, and we will do our best within that role and end the business if we can solve it. If it cannot be resolved, circulate the request to the next `Diana` (5) `Diana` accepts the request. `Diana` is the role of ** limited support (upper limit of Trouble number" 200 "**), and we will do our best within that role and end the business if we can solve it. If it cannot be resolved, circulate the request to the next ʻElmo` (6) ʻElmo` accepts the request. ʻElmo` has a role of ** supporting only odd-numbered Trouble numbers **, and will work hard within that role range, and if it can be resolved, the business will be terminated. If it cannot be resolved, circulate the request to the next `Fred` (7) `Fred` accepts the request. `Fred` is the role of ** restricted support (upper limit of Trouble number" 300 ") **, and we will do our best within that role and end the business if we can solve it. If it cannot be resolved, it will eventually 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.

■ Details of 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

(1) The role of Handler

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))

(2) The role of ConcreteHandler

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

(3) The role of Client

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()

(4) Other

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)

■ Reference URL

-[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

Learn the design pattern "Chain of Responsibility" in Python
Learn the design pattern "Prototype" in Python
Learn the design pattern "Builder" in Python
Learn the design pattern "Flyweight" in Python
Learn the design pattern "Observer" in Python
Learn the design pattern "Memento" in Python
Learn the design pattern "Proxy" in Python
Learn the design pattern "Command" in Python
Learn the design pattern "Bridge" in Python
Learn the design pattern "Mediator" in Python
Learn the design pattern "Iterator" in Python
Learn the design pattern "Strategy" in Python
Learn the design pattern "Composite" in Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
Learn the design pattern "Abstract Factory" in Python
Learn the design pattern "Template Method" in Python
Chain of Responsibility pattern in Java
Learn the design pattern "Singleton" with Python
Learn the design pattern "Facade" with Python
[Gang of Four] Design pattern learning --Chain of Responsibility
Implement the Singleton pattern in Python
Learn the basics of Python ① Beginners
Check the behavior of destructor in Python
The result of installing python in Anaconda
The basics of running NoxPlayer in Python
In search of the fastest FizzBuzz in Python
Intuitively learn the reshape of Python np
Output the number of CPU cores in Python
[Python] Sort the list of pathlib.Path in natural sort
Get the caller of a function in Python
Match the distribution of each group in Python
View the result of geometry processing in Python
Make a copy of the list in Python
Find the solution of the nth-order equation in python
The story of reading HSPICE data in Python
[Note] About the role of underscore "_" in Python
About the behavior of Model.get_or_create () of peewee in Python
Solving the equation of motion in Python (odeint)
Output in the form of a python array
Singleton pattern in Python
Visitor pattern in Python
Experience the good calculation efficiency of vectorization in Python
Learn Nim with Python (from the beginning of the year).
[python] Get the list of classes defined in the module
The story of FileNotFound in Python open () mode ='w'
Implement the solution of Riccati algebraic equations in Python
Get the size (number of elements) of UnionFind in Python
Not being aware of the contents of the data in python
Reproduce the execution example of Chapter 4 of Hajipata in Python
Let's use the open data of "Mamebus" in Python
Implemented the algorithm of "Algorithm Picture Book" in Python3 (Heapsort)
[Python] Outputs all combinations of elements in the list
Get the URL of the HTTP redirect destination in Python
A reminder about the implementation of recommendations in Python
Reproduce the execution example of Chapter 5 of Hajipata in Python
To do the equivalent of Ruby's ObjectSpace._id2ref in Python
Check the asymptotic nature of the probability distribution in Python
I studied about design patterns (personal memo) Part 6 (Chain of Responsibility pattern, Facade pattern, Mediator pattern)
Towards the retirement of Python2
Download the file in Python