Pythonでデザインパターン　TemplateMethodパターン

pythonとデザインパターンの学習のため、記事をまとめようと思いました。
デザインパターンの最初はTemplateパターンです。

#環境
python3

#参考
@kidach1 さん、まるパクリさせてもらいますm(__)m（ソースのみ修正　ruby→python）
参考「**Ruby**によるデザインパターン」【Template Method】-テンプレは準備した、あとはお好きに-
https://qiita.com/kidach1/items/7c2a80bfc8a87a05051f
**この記事はPythonです**


#どんなパターンか
ソフトウェアには変化がつきものである。
その変化の度に、広範囲な修正が入っているようではとても対応できない。

そこで、
***抽象的な処理と具体的な処理***を分離することで、
***変化に強い構造***を実現したい。

そのための手段の一つがTemplate Method。

骨子は以下2種のクラスの利用。

骨格となるメソッドを持った抽象基底クラス（テンプレートクラス）
処理の詳細を詰める具象クラス（サブクラス）

#ひどいコード
あるレポートを様々なフォーマット（HTMLやPlainText）で出力するReportクラス

```python:template.py
#!/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分で処理を分けている。
フォーマットがさらに増えたらどうなるのか・・

ということで、解決策を考える。

#パターンの適用によるリファクタリング

##変わるものと変わらないものを分離する
基本的な処理の流れ
・テンプレート（変わらないもの）をフォーマット（変わるもの）に依存させるべきではない。
・前者は抽象的な基底クラスに、後者は具象サブクラスに定義する。

##つまり
「基本的な処理の流れ」である、

１．特定フォーマットに必要なヘッダ情報を出力する。
２．タイトルを出力する。
３．レポート本体（body）を出力する。
４．フォーマットに要求される残りの要素を出力する。

これらを「テンプレ」として抽象基底クラスに定義し、残りはサブクラスへ切り出す。

##実装

###抽象基底クラス

```python
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は、「各フォーマットごとに処理が異なる＝より変化しやすい処理」
という想定があるため。

###具象サブクラス１（フォーマット：HTML）

```python
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>

```

###具象サブクラス２（フォーマット：PlainText）

```python
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