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.
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked May 22, 2014 at 16:48
\$\endgroup\$

2 Answers 2

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).
answered May 22, 2014 at 17:24
\$\endgroup\$
4
  • \$\begingroup\$ No, its not an abuse. See this description \$\endgroup\$ Commented May 25, 2014 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\$ Commented May 25, 2014 at 7:40
  • \$\begingroup\$ The request should be handled by any on object, read it again. \$\endgroup\$ Commented May 26, 2014 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\$ Commented May 26, 2014 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.

answered May 26, 2014 at 18:59
\$\endgroup\$
4
  • \$\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\$ Commented May 27, 2014 at 2:14
  • \$\begingroup\$ maybe this link provides some more insight oodesign.com/chain-of-responsibility-pattern.html \$\endgroup\$ Commented May 27, 2014 at 2:16
  • \$\begingroup\$ @vivekpoddar Your choice of use case is probably the only real issue. See my small update. \$\endgroup\$ Commented May 27, 2014 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\$ Commented May 27, 2014 at 5:44

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.