pythonでStrategyパターン

template methodパターンが継承を使ってオブジェクトのふるまいに多様性をもたらすのに対して、Strategyパターンは移譲を使います。

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

    def output_report(self):
        self.formatter.output_report(self)


class HTMLFormatter(object):
    def output_report(self, context):
        print "<html>"
        print "<head>"
        print "<title>%s</title>" % context.title
        print "</head>"
        print "<body>"
        for line in context.text:
            print "<p>%s</p>" % line
        print "</body>"
        print "</html>"


class PlainTextFormatter(object):
    def output_report(self, context):
        print "***** %s *****" % context.title
        print ""
        for line in context.text:
            print line


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


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

class Report2(object):
    """strategyクラスでなく、関数を受け取る
    """
    def __init__(self, format_func, title, *text):
        self.format_func = format_func
        self.title = title
        self.text = text

    def output_report(self):
        print self.format_func(self)

Report2(lambda x: "----- %s ----- \n%s" % (x.title, '\n'.join(x.text)), u"月次報告", u"順調", u"最高の調子").output_report()
# ----- 月次報告 -----
# 順調
# 最高の調子

実例

djangoだとurlでのkwargs指定がStrategyパターンに近い使い方ができるかもしれない。

djangoではurls.pyでルーティングの指定をする際に、kwargsでview関数で使用するロジックを渡すことができるようになってます。
以下はパスワードリセットのview関数です。

@csrf_protect
def password_reset(request, is_admin_site=False,
                   template_name='registration/password_reset_form.html',
                   email_template_name='registration/password_reset_email.html',
                   subject_template_name='registration/password_reset_subject.txt',
                   password_reset_form=PasswordResetForm,
                   token_generator=default_token_generator,
                   post_reset_redirect=None,
                   from_email=None,
                   current_app=None,
                   extra_context=None):

token_generatorとしてdefault_token_generatorがデフォルトで渡されて、関数内で使用されます。
こいつを切り替えたいときはurls.pyで以下のように指定します。

url(r'^password_reset/?$', password_reset, {'token_generator': my_token_generator}, name='password_reset'),

一度指定したらずっとそれで動くのであまり動的に切り替えるって感じがしないけど