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が作成されました。