さるへい備忘録

さるへいのやったことを綴っているブログです。基本的にテクノロジーの話題です。

Pythonで学ぶデザインパターン入門Builderパターン

結城先生の Java言語で学ぶデザインパターン入門 のコードを Python に書き直していくシリーズ第7段です。

Builderパターンとは

具体的な処理の骨組みを先につくっておくというパターンです。
Template Methodパターンでは、スーパークラスがサブクラスをコントロールしてましたが、このBuilderパターンでは他クラスでコントロールします。

ソースコード

コードの概要

文書を作成するプログラムを作成します。
文書を構成するためには、 Builder クラスを継承します。 Builder クラスを継承したクラスを、 Director クラスがコントロールして文書を具体的に作るといったものになります。

ソースコード

Builder

# coding: utf-8
from abc import ABCMeta, abstractmethod


class Builder(metaclass=ABCMeta):

    @abstractmethod
    def make_title(self, title: str):
        pass

    @abstractmethod
    def make_string(self, string: str):
        pass

    @abstractmethod
    def make_items(self, items: list):
        pass

    @abstractmethod
    def close(self):
        pass

文書を作成するための抽象クラスです。
後に紹介する Direcctor クラスにコントロールされています。

Director

# coding: utf-8
from Builder.builder import Builder


class Director(object):

    def __init__(self, builder: Builder):
        self.__builder = builder

    def construct(self):
        self.__builder.make_title('Greeting')
        self.__builder.make_string('朝から昼にかけて')
        self.__builder.make_items([
            'おはようございます。',
            'こんにちは。'
        ])
        self.__builder.make_string('夜に')
        self.__builder.make_items([
            'こんばんは。',
            'おやすみなさい。',
            'さようなら。'
        ])
        self.__builder.close()

Builder を継承したクラスをコントロールして文書を作成します。
construct で文書を作成します。

TextBuilder(Builderを継承)

# coding: utf-8
from Builder.builder import Builder


class TextBuilder(Builder):

    def __init__(self):
        self.__buffer = ''

    def make_title(self, title: str):
        self.__buffer += '================================\n'
        self.__buffer += '「%s」\n' % title
        self.__buffer += '\n'

    def make_string(self, string: str):
        self.__buffer += '■ %s \n' % string
        self.__buffer += '\n'

    def make_items(self, items: list):
        for i in items:
            self.__buffer += '・%s \n' % i
        self.__buffer += '\n'

    def close(self):
        self.__buffer += '================================\n'

    def get_result(self) -> str:
        return self.__buffer

テキストの文書を作成するクラスです。
最終的には文字列として文書が出力されます。

HTMLBuilder(Builderを継承)

# coding: utf-8
from Builder.builder import Builder


class HTMLBuilder(Builder):

    def __init__(self):
        self.__filename = None

    def make_title(self, title: str):
        self.__filename = '%s.html' % title
        with open(self.__filename, 'a') as f:
            f.write('<html><head><title>%s</title></head><body>' % title)
            f.write('<h1>%s</h1>' % title)

    def make_string(self, string: str):
        with open(self.__filename, 'a') as f:
            f.write('<p>%s</p>' % string)

    def make_items(self, items: list):
        with open(self.__filename, 'a') as f:
            f.write('<ul>')
            for i in items:
                f.write('<li>%s</li>' % i)
            f.write('</ul>')

    def close(self):
        with open(self.__filename, 'a') as f:
            f.write('</body></html>')

    def get_result(self) -> str:
        return self.__filename

HTMLファイルとして文書が作成されるクラスです。
最終的にはファイル名が出力されます。

実行ファイル

# coding: utf-8
import sys

from Builder.html_builder import HTMLBuilder
from Builder.text_builder import TextBuilder
from Builder.director import Director


def usage():
    print('python main.py plain  プレーンテキストで文書作成')
    print('python main.py html   HTMLファイルで文書作成')


if __name__ == '__main__':
    if len(sys.argv) != 2:
        usage()
        exit()

    if sys.argv[1] == 'plain':
        html_builder = TextBuilder()
        director = Director(html_builder)
        director.construct()
        result = html_builder.get_result()
        print(result)
    elif sys.argv[1] == 'html':
        html_builder = HTMLBuilder()
        director = Director(html_builder)
        director.construct()
        filename = html_builder.get_result()
        print(filename)
    else:
        usage()
        exit()

実行結果

python Builder/main.py html
Greeting.html

python Builder/main.py plain
================================
「Greeting」

■ 朝から昼にかけて 

・おはようございます。 
・こんにちは。 

■ 夜に 

・こんばんは。 
・おやすみなさい。 
・さようなら。 

================================

実際どう便利か

Builderパターンを利用する時の便利な点は、Directorクラスは基本的に何も知らなくて良いことです。
Directorクラスは何も知らなくて良いのですが、ただどうやって文書を作成するかの方法だけを指定する必要があります。
実際、 Builderクラスを継承したインスタンスであればなんでもDirectorクラスは受け入れます。
Builderを継承したクラスを入れ替えることができるので、いろいろなBuilderクラスを構築して文書を作成することができます。