「アルゴリズム等、戦略の変更を容易にする」パターンです。
月報をHTML or マークダウン形式で出力します。
タイトルは「Monthly Report」、中身は「Good」と「Best」の2行だけのシンプルな月報を作ります。
def main():
title = "Monthly report"
contents = ["good", "best"]
# Ouput by Markdown
report = Report(title, contents, PlainTextFormatter())
report.output_report()
print("-----")
# Output by HTML
report = Report(title, contents, HTMLFormatter())
report.output_report()
# Context
class Report():
def __init__(self, title, text, formatter):
self.title = title
self.text = text
self.formatter = formatter
def output_report(self):
self.formatter.output_report(self.title, self.text)
# Strategy
class Formatter:
def output_report(self, title, text):
raise NotImplementedError()
# ConcreteStrategy
class HTMLFormatter(Formatter):
def output_report(self, title, text):
print("<html><head><title>{}</title></head>".format(title))
print("<body>")
for line in text:
print("<p>{}</p>".format(line))
print("</body>")
print("</html>")
# ConcreteStrategy
class PlainTextFormatter(Formatter):
def output_report(self, title, text):
print("**{}**".format(title))
for line in text:
print(" -", line)
if __name__ == "__main__":
main()
重要な部分を以下に挙げます。
def main():
...
# Ouput by Markdown
report = Report(title, contents, PlainTextFormatter())
report.output_report()
...
# Output by HTML
report = Report(title, contents, HTMLFormatter())
report.output_report()
class HTMLFormatter(Formatter):
def output_report(self, title, text):
...
class PlainTextFormatter(Formatter):
def output_report(self, title, text):
...
PlainTextとHTMLで出力するとき、
共通 → 出力すること
差異 → 出力の仕方
です。
そこで、「変わるものを変わらないものから分離する」という原則に従い、出力の仕方の部分を別のクラス(PlainTextFormatter()
または HTMLFormatter()
)に任せます。
こうすることで、report.output_report()
と同じコードであっても、出力を変更することが可能になります。
このように、同じコードであっても、中身の(変数の)違いによって、ふるまいが変わることを委譲といいます。
(この場合、report
の振る舞いをformatter
に委譲しています)
委譲はデザパタでは頻出なので、ぜひ覚えておきましょう。
「Pythonのクラスは第一級オブジェクト」が使えます。
(ここでは関数が第一級オブジェクトであることを利用しています。)
具体的な例をあげると、こんな感じです。
def hoge():
print("hogehoge")
h = hoge
h() # => hogehoge
サンプルコードでは report.output_report()
にオブジェクトが入っていることで、
ふるまいを委譲することが可能でしたが、Pythonだと関数を変数として扱えるので、オブジェクトにしなくてもふるまいを委譲することができます。
def main():
title = "Monthly report"
contents = ["good", "best"]
# Ouput by PlainText
report = Report(title, contents, plain_formatter)
report.output_report()
print("-----")
# Output by HTML
report = Report(title, contents, html_formatter)
report.output_report()
# Context
class Report():
def __init__(self, title, text, formatter):
self.title = title
self.text = text
self.formatter = formatter
def output_report(self):
self.formatter(self.title, self.text)
def html_formatter(title, text):
print("<html><head><title>{}</title></head>".format(title))
print("<body>")
for line in text:
print("<p>{}</p>".format(line))
print("</body>")
print("</html>")
def plain_formatter(title, text):
print("**{}**".format(title))
for line in text:
print(" -", line)
if __name__ == "__main__":
main()
Contextクラスしかないため、省略。
具体的なアルゴリズムはグローバルな関数から直接選択することになります。