Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Chain of responsibility pattern in python using coroutines

I am exploring different concepts in python and I happened to read upon an example of coroutines which can be used for the chain of responsibility design pattern. I wrote the following code:

from functools import wraps

def coroutine(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        generator = function(*args, **kwargs)
        next(generator)
        return generator
    return wrapper

@coroutine
def PlatinumCustomer(successor=None):
   cust = (yield)
   if cust.custtype == 'platinum':
       print "Platinum Customer"
   elif successor is not None:
       successor.send(cust)

@coroutine
def GoldCustomer(successor=None):
    cust = (yield)
    if cust.custtype == 'gold':
        print "Gold Customer"
    elif successor is not None:
        successor.send(cust)

@coroutine
def SilverCustomer(successor=None):
    cust = (yield)
    if cust.custtype == 'silver':
        print "Silver Customer"
    elif successor is not None:
        successor.send(cust)

@coroutine
def DiamondCustomer(successor=None):
    cust = (yield)
    if cust.custtype == 'diamond':
        print "Diamond Customer"
    elif successor is not None:
        successor.send(cust)

class Customer:
    pipeline = PlatinumCustomer(GoldCustomer(SilverCustomer(DiamondCustomer())))

    def __init__(self,custtype):
        self.custtype = custtype

    def HandleCustomer(self):
        try:
            self.pipeline.send(self)
        except StopIteration:
            pass

if __name__ == '__main__':
    platinum = Customer('platinum')
    gold = Customer('gold')
    silver = Customer('silver')
    diamond = Customer('diamond')
    undefined = Customer('undefined')

    platinum.HandleCustomer()
    gold.HandleCustomer()
    undefined.HandleCustomer()   

What I have tried to do here is try to create a chain of responsibility pattern solution for handling different types of customers (Platinum, Gold, Diamond, Silver).

For that Customer has a pipeline where I have mentioned the order in which the different customers will be handled. Customer().HandleCustomer will send an instance of itself through the pipeline which will check whether its custtype matches and then process it accordingly or it will send it across to its successor (if available)

PROBLEM: The problem is that when I run the above script, it will handle the first platinum customer but not the gold or the undefined. I am assuming this is because he has reached the end of the generator. How do I modify the code so that everytime it is a new instance of a customer, it will go through the pipeline from its beginning?

like image 906
Rivas Avatar asked Feb 23 '15 05:02

Rivas


1 Answers

Your coroutines must loop forever in order to handle successive calls, as in:

@coroutine
def PlatinumCustomer(successor=None):
    while 1:    #   <---- this is missing from your coroutines
        cust = (yield)
        if cust.custtype == 'platinum':
           print "Platinum Customer"
        elif successor is not None:
           successor.send(cust)

And to handle the 'undefined' type, you'll need a final catch-all handler:

@coroutine
def UndefinedCustomer():
    while 1:
        cust = (yield)
        print "No such customer type '%s'" % cust.custtype

and add it to your pipeline:

pipeline = PlatinumCustomer(GoldCustomer(SilverCustomer(DiamondCustomer(UndefinedCustomer()))))

(A terminating UndefinedCustomer handler also will allow you to remove the 'if there is no successor' code from your coroutines - all will have successors except for the terminator, which knows that it is the terminator and will not call a successor.)

With these changes, I get this output from your tests:

Platinum Customer
Gold Customer
No such customer type 'undefined'

Also, why the catch for StopIteration in HandleCustomer? This code should be sufficient:

def HandleCustomer(self):
    self.pipeline.send(self)
like image 124
PaulMcG Avatar answered Oct 24 '22 07:10

PaulMcG