4
\$\begingroup\$
class ReportFormat(object):
    PDF = 0
    TEXT = 1


class Report(object):
    """Strategy context."""

    def __init__(self, format_):
        self.title = 'Monthly report'
        self.text = ['Things are going', 'really, really well.']
        self.format_ = format_


class Handler(object):

    def __init__(self):
        self.nextHandler = None

    def handle(self, request):
        self.nextHandler.handle(request)


class PDFHandler(Handler):

    def handle(self, request):
        if request.format_ == ReportFormat.PDF:
            self.output_report(request.title, request.text)
        else:
            super(PDFHandler, self).handle(request)

    def output_report(self, title, text):
        print '<html>'
        print '  <head>'
        print '    <title>%s</title>' % title
        print '  </head>'
        print '  <body>'
        for line in text:
            print '    <p>%s</p>' % line
        print '  </body>'
        print '</html>'


class TextHandler(Handler):

    def handle(self, request):
        if request.format_ == ReportFormat.TEXT:
            self.output_report(request.title, request.text)
        else:
            super(TextHandler, self).handle(request)

    def output_report(self, title, text):
        print 5*'*' + title + 5*'*'
        for line in text:
            print line


class ErrorHandler(Handler):

    def handle(self, request):
        print "Invalid request"


if __name__ == '__main__':
    report = Report(ReportFormat.TEXT)
    pdf_handler = PDFHandler()
    text_handler = TextHandler()

    pdf_handler.nextHandler = text_handler
    text_handler.nextHandler = ErrorHandler()

    pdf_handler.handle(report)

O/P:

*****Monthly report*****
Things are going
really, really well.
\$\endgroup\$
3
\$\begingroup\$

In my opinion, this is abuse of the Chain-of-responsibility Pattern. It is completely surprising that pdf_handler.handle(report) would generate a text report instead.

The Wikipedia example for Chain-of-responsibility is a logger. As the message passes through the chain, each logger may choose to do something with it (write to standard output, send e-mail, etc.) depending on the verbosity level.

Another example of Chain-of-responsibility is the filter mechanism in Apache HTTPD. Each input or output filter in the chain can alter the request or response.

Your use case is different: you only want to generate one kind of report. For that, use a subtly different design: the Strategy Pattern. Basically, just do the simplest thing that could work:

  • Each kind of handler defines a handle(self, request) method.
  • If you want a text report, then call TextReport().handle(request).
\$\endgroup\$
  • \$\begingroup\$ No, its not an abuse. See this description \$\endgroup\$ – vivek May 25 '14 at 7:33
  • 1
    \$\begingroup\$ Yes, it is abuse, as I just explained. As your article states, Chain of Responsibility "[avoids] coupling the sender of a request to its receiver by giving more than one object a chance to handle the request." But you have no reason to let multiple objects handle the request. I've given examples of where a chain would be appropriate; your problem does not fit that pattern. \$\endgroup\$ – 200_success May 25 '14 at 7:40
  • \$\begingroup\$ The request should be handled by any on object, read it again. \$\endgroup\$ – vivek May 26 '14 at 17:36
  • 1
    \$\begingroup\$ +1 I totally agree with your answer, but rereading both articles linked here I see the examples use the same design where each link has a reference to the next link in the chain. By moving the chain creation into a separate method, you don't realize that the first link in the chain is being referenced by clients directly. Clients think they have a generic ReportHandler when in reality they hold the PDFHandler. \$\endgroup\$ – David Harkness May 26 '14 at 19:22
4
\$\begingroup\$

I would not use Chain of Responsibility to solve this problem. For every request, a single handler is chosen based on the same property: it's output format. For that you can use a simple dispatch table: a dict mapping each output format to its handler. There's no point asking each handler if it can handle the request.

Update: As usual, the Portland Pattern Repository has some good historical discussion of this pattern, specifically when not to use it:

Do not use Chain of Responsibility when each request is only handled by one handler, or, when the client object knows which service object should handle the request.

Note: Report and ReportFormat are unchanged from the OP and omitted for brevity.

class ReportDispatcher(object):
    def __init__(self):
        self.reports = {}

    def add(self, report):
        self.reports[report.format] = report;

    def handle(self, request):
        report = self.reports[request.format_]
        if report:
            report.handle(request)
        else
            print "Invalid request"

class Handler(object):

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

    def handle(self, request):
        print "subclass responsibility"

class PDFHandler(Handler):

    def __init__(self):
        super(PDFHandler, self).__init__(ReportFormat.PDF)

    def handle(self, request):
        print '<html>'
        print '  <head>'
        print '    <title>%s</title>' % request.title
        print '  </head>'
        print '  <body>'
        for line in request.text:
            print '    <p>%s</p>' % line
        print '  </body>'
        print '</html>'

class TextHandler(Handler):

    def __init__(self):
        super(TextHandler, self).__init__(ReportFormat.TEXT)

    def handle(self, request):
        print 5*'*' + request.title + 5*'*'
        for line in request.text:
            print line

if __name__ == '__main__':
    reports = ReportDispatcher()
    reports.add(PDFHandler())
    reports.add(TextHandler())

    report = Report(ReportFormat.TEXT)
    reports.handle(report)

As you can see from the main method, sending a report request is completely decoupled from the various report handlers. Instead, the request is sent to the dispatcher which knows about the handlers.

\$\endgroup\$
  • \$\begingroup\$ Hi, your example is similar to the strategy pattern. In my example I just tried to show the use case of the command pattern, maybe my use case is not so clear. I think the main problem here is I am calling directly the concrete report object maybe if I put them is some container and then calling a method on the container may solve the issue. \$\endgroup\$ – vivek May 27 '14 at 2:14
  • \$\begingroup\$ maybe this link provides some more insight oodesign.com/chain-of-responsibility-pattern.html \$\endgroup\$ – vivek May 27 '14 at 2:16
  • \$\begingroup\$ @vivekpoddar Your choice of use case is probably the only real issue. See my small update. \$\endgroup\$ – David Harkness May 27 '14 at 2:42
  • \$\begingroup\$ Although the use case regarding request handling differs here. I myself will not use this pattern in the above case, it was because I was not finding some useful use case I implemented in the above scenario. \$\endgroup\$ – vivek May 27 '14 at 5:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.