pythonでtemplate methodパターン

デザインパターンの中でもわかりやすいTemplate methodパターン。
Rubyによるデザインパターンで紹介されていたコードをpythonで書いてみた。

AbstractClassであるReportの各メソッドの実装はpassにしてあるけど、必ず実装してもらうためにはNotImplementedErrorをraiseするようにする。

# coding=utf-8
class Report(object):
    def __init__(self, title, *text):
        self.title = title
        self.text = text

    def output_report(self):
        self.output_start()
        self.output_head()
        self.output_body_start()
        self.output_body()
        self.output_body_end()
        self.output_end()

    def output_start(self):
        pass

    def output_head(self):
        pass

    def output_body_start(self):
        pass

    def output_body(self):
        for line in self.text:
            self.output_line(line)

    def output_line(self, line):
        print line

    def output_body_end(self):
        pass

    def output_end(self):
        pass


class HTMLReport(Report):
    def output_start(self):
        print "<html>"

    def output_head(self):
        print "<head>"
        print "<title>%s</title>" % self.title
        print "</head>"

    def output_body_start(self):
        print "<body>"

    def output_line(self, line):
        print "<p>%s</p>" % line

    def output_body_end(self):
        print "</body>"

    def output_end(self):
        print "</html>"


class PlainTextReport(Report):
    def output_head(self):
        print "***** %s *****" % self.title
        print ""


Report(u'月次報告', u"順調", u"最高の調子").output_report()
# 順調
# 最高の調子

HTMLReport(u'月次報告', u"順調", u"最高の調子").output_report()
# <html>
# <head>
# <title>月次報告</title>
# </head>
# <body>
# <p>順調</p>
# <p>最高の調子</p>
# </body>
# </html>


PlainTextReport(u'月次報告', u"順調", u"最高の調子").output_report()
# ***** 月次報告 *****
#
# 順調
# 最高の調子

実例

djangoのClass Based Viewはフックポイントがいろいろ用意されてて、template method patternっぽい感じですね

以下のDetailViewではget_querysetをオーバーライドしてます。

class BlogEntryDetailView(DetailView):
    model = BlogEntry

    def get_queryset(self):
        if self.request.GET.get("show_drafts"):
            return BlogEntry.objects.all()
        else:
            return BlogEntry.objects.filter(published=True)


Rubyによるデザインパターン

Rubyによるデザインパターン