patternpythonMinor
Chain of responsibility pattern
Viewed 0 times
patternresponsibilitychain
Problem
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 ''
print ' '
print ' %s' % title
print ' '
print ' '
for line in text:
print ' %s' % line
print ' '
print ''
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.
Solution
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:
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.
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 ''
print ' '
print ' %s' % request.title
print ' '
print ' '
for line in request.text:
print ' %s' % line
print ' '
print ''
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.
Code Snippets
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)Context
StackExchange Code Review Q#51429, answer score: 4
Revisions (0)
No revisions yet.