pythonとデザインパターンの学習のため、記事をまとめようと思いました。
デザインパターンの最初はTemplateパターンです。
#環境
python3
#参考
@kidach1 さん、まるパクリさせてもらいますm(__)m(ソースのみ修正 ruby→python)
参考「Rubyによるデザインパターン」【Template Method】-テンプレは準備した、あとはお好きに-
https://qiita.com/kidach1/items/7c2a80bfc8a87a05051f
この記事はPythonです
#どんなパターンか
ソフトウェアには変化がつきものである。
その変化の度に、広範囲な修正が入っているようではとても対応できない。
そこで、
抽象的な処理と具体的な処理を分離することで、
変化に強い構造を実現したい。
そのための手段の一つがTemplate Method。
骨子は以下2種のクラスの利用。
骨格となるメソッドを持った抽象基底クラス(テンプレートクラス)
処理の詳細を詰める具象クラス(サブクラス)
#ひどいコード
あるレポートを様々なフォーマット(HTMLやPlainText)で出力するReportクラス
#!/usr/bin/python
# coding: utf-8
class Report:
def __init__(self):
self.title = '月次報告'
self.text = ['最高!', '順調', '普通']
def output_report(self, format):
if ( format == 'plain' ):
print("*** "+ self.title + " ***")
elif (format == 'html'):
print('<html>')
print(' <head>')
print(" <title>"+ self.title +"</title>")
print(' </head>')
print(' <body>')
else:
raise ValueError("Unknown format")
for elem in self.text:
if( format == 'plain'):
print(elem)
else:
print(" <p>"+ elem +"</p>")
if (format == 'html'):
print(' </body>')
print('</html>')
report = Report()
report.output_report("plain")
report.output_report("html")
フォーマットがHTMLなのかPlainTextなのかによって、
都度if分で処理を分けている。
フォーマットがさらに増えたらどうなるのか・・
ということで、解決策を考える。
#パターンの適用によるリファクタリング
##変わるものと変わらないものを分離する
基本的な処理の流れ
・テンプレート(変わらないもの)をフォーマット(変わるもの)に依存させるべきではない。
・前者は抽象的な基底クラスに、後者は具象サブクラスに定義する。
##つまり
「基本的な処理の流れ」である、
1.特定フォーマットに必要なヘッダ情報を出力する。
2.タイトルを出力する。
3.レポート本体(body)を出力する。
4.フォーマットに要求される残りの要素を出力する。
これらを「テンプレ」として抽象基底クラスに定義し、残りはサブクラスへ切り出す。
##実装
###抽象基底クラス
class Report:
def __init__(self):
self.title = '月次報告'
self.text = ['最高!', '順調', '普通']
def output_report(self):
self.output_start()
self.output_title()
self.output_body_start()
for line in self.text:
self.output_line(line)
self.output_body_end()
self.output_end()
def output_start(self):
pass
def output_title(self):
self.output_line(self.title)
def output_body_start(self):
pass
def output_line(self, line):
raise 'Called abstract method: output_line'
def output_body_end(self):
pass
def output_end(self):
pass
output_start, output_title, output_body_start, output_body_end, output_endのように
***具象クラスによってオーバーライドできる(不要ならしなくてもよい)***メソッドのことを
フックメソッドと呼ぶ。
一方でoutput_lineは、直接呼び出すと例外を発生させる。つまり、
具象クラスによるオーバーライドを強要している。
→output_lineは、「各フォーマットごとに処理が異なる=より変化しやすい処理」
という想定があるため。
###具象サブクラス1(フォーマット:HTML)
class HTMLReport(Report):
def output_start(self):
print('<html>')
def output_title(self):
print(' <head>')
print(" <title>"+ self.title + "</title>")
print(' </head>')
def output_body_start(self):
print(' <body>')
def output_line(self, line):
print(" <p>"+ line +"</p>")
def output_body_end(self):
print(' </body>')
def output_end(self):
print('</html>')
html_report = HTMLReport()
html_report.output_report()
#<html>
# <head>
# <title>月次報告</title>
# </head>
# <body>
# <p>最高!</p>
# <p>順調</p>
# <p>普通</p>
# </body>
#</html>
###具象サブクラス2(フォーマット:PlainText)
class PlainTextReport(Report):
def output_title(self):
print("*** "+ self.title + " ***")
def output_line(self, line):
print(line)
plain_repot = PlainTextReport()
plain_repot.output_report()
#*** 月次報告 ***
#最高!
#順調
#普通
#まとめ
TemplateMethodパターンにより、
変わるもの(具象サブクラス)と変わらないもの(テンプレート)を分離して、変化に強い構造へ。
kidach1さんまるパクリですみませんm(__)m