Builderパターン
結城浩さんの「Java言語で学ぶデザインパターン入門」を題材にpythonでデザインパターンを書いてみる。
今回の題材はBuilderパターン。複雑な構造のものを一気に組み立てるのは難しいので、事前に全体を構成する各部分をつくって
段階を踏んで構造を持ったインスタンスを組み上げていくもの。
サンプルになっているのはこのパターンを使って文書を作成するプログラム(builder.py)。
Builderクラスで文書作成用のメソッドを決めて、Directorクラスでそのメソッドを使って文書をつくる。
どんな文書にするかはBuilderクラスのサブクラスで決まるというもの。
まずBuilderクラス
# -*- coding: utf-8 -*- class Builder(object): def makeTitle(self, title): pass def makeString(self, st): pass def makeItems(self, items): pass def closing(self): pass
ここではメソッドの宣言だけ。
次はDirectorクラス
class Director(object): def __init__(self, builder): self.builder = builder def construct(self): self.builder.makeTitle("Greeting") self.builder.makeString("朝から昼にかけて") self.builder.makeItems(["おはようございます。", "こんにちは。"]) self.builder.makeString("夜に") self.builder.makeItems(["こんばんは。", "おやすみなさい。", "さようなら。"]) self.builder.closing()
ここでBuilderクラスで宣言したメソッドを使って文書をつくる。コンストラクタに渡されることになるのは
Builderクラスのサブクラスのインスタンス。渡されるインスタンスによってどんな文書になるか変わる。
次はTextBuilderクラス
class TextBuilder(Builder): _buffer = [] def makeTitle(self, title): self._buffer.append("===================================\n") self._buffer.append("[%s]" %title) self._buffer.append("\n") def makeString(self, st): self._buffer.append("*%s\n" %st) self._buffer.append("\n") def makeItems(self, items): for i in items: self._buffer.append(" +%s\n" %i) self._buffer.append("\n") def closing(self): self._buffer.append("===================================\n") def getResult(self): return "\n".join(self._buffer)
ここはBuilderクラスのサブクラスでプレーンテキストで文書をつくる。リストに文書の各部を順に入れていき、joinメソッドで
つなげた結果が文字列で返ってくる。
次はHTMLBuilderクラス
class HTMLBuilder(Builder): _buffer = [] def makeTitle(self, title): self.title = title self.filename = "%s.html" %title self._buffer.append("<html><head><title>%s</title></head><body>\n" %title) self._buffer.append("<h1>%s</h1>\n" %title) def makeString(self, st): self.st = st self._buffer.append("<p>%s</p>\n" %st) def makeItems(self, items): self.items = items self._buffer.append("<ul>\n") for i in items: self._buffer.append("<li>%s</li>\n" %i) self._buffer.append("</ul>\n") def closing(self): self._buffer.append("</body></html>\n") def getResult(self): self.writer = open(self.filename, "w") self.writer.write("\n".join(self._buffer)) self.writer.close() return self.filename
ここもBuilderクラスのサブクラスでHTMLファイルとして文書をつくる。ここでもリストに文書の各部を入れていってつなげたものを
ファイルに書き込む。結果はファイル名として返ってくる。
最後に動作テスト用の部分
if __name__=="__main__": import sys if len(sys.argv) == 1 or len(sys.argv) >= 3: print "python builder.py plain => plain text" print "python builder.py html => HTML text" elif sys.argv[1] == "plain": textbuilder = TextBuilder() director = Director(textbuilder) director.construct() result = textbuilder.getResult() print result elif sys.argv[1] == "html": htmlbuilder = HTMLBuilder() director = Director(htmlbuilder) director.construct() filename = htmlbuilder.getResult() print "%sが作成されました。" %filename else: print "python builder.py plain => plain text" print "python builder.py html => HTML text"
コマンドラインで指定した形式によってつくられる文書が変わる。
plainを指定するとTextBuilderクラスのインスタンスがDirectorクラスのコンストラクタに渡るし、htmlを指定すれば
HTMLBuilderクラスのインスタンスがDirectorクラスのコンストラクタに渡る。
実行結果は以下。
C:\works\python\book1>python builder.py plain =================================== [Greeting] *朝から昼にかけて +おはようございます。 +こんにちは。 *夜に +こんばんは。 +おやすみなさい。 +さようなら。 ===================================
C:\works\python\book1>python builder.py html Greeting.htmlが作成されました。