Pythonでデザインパターンを学ぼう[Template Method]
Pythonを用いてのTemplate Methodパターンの実装方法について解説します。
Template Methodパターンは、「振る舞い関するパターン」に分類されるデザインパターンです。
Template Methodパターンとは?
おおまかな処理内容をあらかじめ決めておいた抽象クラスを作成し、その処理内容の実装をサブクラスに任せることです。
もう少し具体的に言うならば、抽象クラスにある程度の変数やメソッドなどの処理を実装し、その抽象クラスを継承した具象クラスに具体的な実装をさせるパターンのことです。
抽象クラスを作ることで、「共通した基本的なアルゴリズム」を作成できます。出力方法や必要な引数の内容など、少しの差に関しては継承した具象クラスに任せます。
簡単なTemplate Methodパターンのサンプル
実際に作ってみた方がわかりやすいと思うので、以下の内容を作成してみましょう。
・二つの引数「タイトル」と「テキスト一覧」の二つの要素を表示させる
・HTML形式、又は通常のテキスト形式で表示させる
「タイトル」と「テキスト一覧」が以下のような内容だとします。
・タイトル
Report Title
・テキスト一覧
- report line1
- report line2
- report line3
これらを通常のテキストと、HTMLで表示させたいとします。出力内容は以下のようになります。
$ python template_method.py
**** #Report Title ****
report line 1
report line 2
report line 3
<html><head><title>Report Title</title></head><body>
<p>report line 1</p>
<p>report line 2</p>
<p>report line 3</p>
</body></html>
片方は通常のテキストで表示されており、もう一方はHTMLの形式で表示されています。これらをTemplate Methodパターンを使用して、上記のような処理を実現するプログラムを作成していきます。
共通処理をまとめた抽象クラス部分を作成
それでは「タイトル」と「テキスト一覧」を出力するプログラムの共通部分を作成していきます。
HTMLや普通のテキストで表示するにしても、共通部分となりそうなのは以下の二つ部分だと考えられます。
・インスタンス生成時にタイトルとテキストの内容を受け取る
・タイトルとテキストを表示する処理を実行
それでは、クラス名をAbstractReportとした場合の抽象クラスを作成してみます。
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class AbstractReport(metaclass=ABCMeta):
def __init__(self, title, text_list):
self.title = title
self.text_list = text_list
@abstractmethod
def pprint(self):
pass
@abstractmethod
def start(self):
pass
@abstractmethod
def end(self):
pass
def display(self):
self.start()
self.pprint()
self.end()
まずコンストラクタであるdef __init__(self, title, text_list)でタイトルとテキスト一覧を引数で受け取っています。
次に実際に文字列の表示を行う処理であるpprint, start ,endに関しては抽象メソッドとして定義しました。メソッドの上部に@abstractmethodと付けることで、抽象メソッドとして定義できます。
次に、start,pprint,endの順番で処理を実行させるdisplayメソッドを実装します。
HTMLだろうが通常テキストだろうが、表示させる内容の順番は変わらないため、一括でこれらをまとめて実行してくれるメソッドを定義します。
AbstractReportを継承させることで、start,pprint,endを実装させることを強制します。
テキストを表示する具象クラスの作成
それでは通常のテキストでタイトルとテキスト一覧を表示するクラスを作成していきます。
抽象クラスAbstractReport で定義したdisplayメソッドが実行されると、start, pprint, endの順で処理が実行されます。
まず最初にstartメソッドでtitle部分の前後に*** # <ttile> ****という記号を付け加える処理を実装しましょう。インスタンス変数であるself.titleに記号文字列を付け加える処理を実装しています。
pprintメソッドでは、インスタンス変数であるself.text_listをfor文で回して「テキスト一覧」を表示させています。
最後にendメソッドは特に使い道がない為、passで処理を記述しません。
それでは、抽象クラスAbstractReportを継承したPlaneTextReportを作成します。
# -*- coding: utf-8 -*-
class PlaneTextReport(AbstractReport):
def pprint(self):
for text in self.text_list:
print(text)
def start(self):
title = "**** #"+self.title+" ****"
print(title)
def end(self):
pass
HTMLを表示する具象クラスの作成
次にHTMLでタイトルとテキスト一覧を表示するクラスを作成していきます。
上記同様に抽象クラスAbstractReport を継承している為、displayメソッドが実行されると、start, pprint, endの順で処理が実行されます。
まず最初にstartメソッドでHTMLで必要な<html><head>などのHTMLタグを標準出力しています。タイトル部分は、<title></title>タグで囲む処理を加え、<body>タグの始まりまでを実装させます。
pprintメソッドでは、インスタンス変数であるself.text_listをfor文で回して「テキスト一覧」を<p>タグで囲むように表示させています。
最後にendメソッドは</body><html>を加え、HTMLの終了タグを標準出力させます。
それでは、抽象クラスAbstractReportを継承したHtmlReportを作成します。
# -*- coding: utf-8 -*-
class HtmlReport(AbstractReport):
def pprint(self):
for text in self.text_list:
print('<p>'+text+'</p>')
def start(self):
title = "<html><head>"
title += "<title>"+self.title+"</title>"
title += "</head><body>"
print(title)
def end(self):
title = "</body></html>"
print(title)
Template Methodパターンの使い道とは?
デザインパターンの中ではあまり特徴が無いような理解も簡単な為、オブジェクト指向言語を使ってプログラミングしていれば、無意識的にTemplate Methodパターンを使っているのではないのかなと思います。
冒頭でも述べた通り、共通処理部分をまとめて、細かいわずかな違いの部分をサブクラスで実装させたいときなどに使えるでしょう。
又は、Pythonではインターフェースを標準でサポートしていないため、インターフェースを使いたいときの代わりにもなります。
上記のサンプルコードはこちらにまとめてあります
参考資料
・Template Method パターン (スーパークラスで処理の流れを定義し, その処理の詳細はサブクラスで定義する)
・pythonによるデザインパターン[template method]
・Template Method パターン
この記事が気に入ったらサポートをしてみませんか?