LoginSignup
9
11

More than 3 years have passed since last update.

Pythonで、デザインパターン「Builder」を学ぶ

Last updated at Posted at 2020-01-14

GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。

■ Builder(ビルダー)

Builderパターン(ビルダー・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。 オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にする。

UML class and sequence diagram

W3sDesign_Builder_Design_Pattern_UML.jpg

UML class diagram

2880px-Builder_UML_class_diagram.svg-2.png
(以上、ウィキペディア(Wikipedia)より引用)

■ "Builder"のサンプルプログラム

Builderパターンとは、複雑な構造のものを一気に組み立てるのは難しいので、事前に全体を構成する各部分をつくって、段階を踏んで構造を持ったインスタンスを組み上げていくものらしいです。
実際に、Builderパターンを使って、「文書」を作成するサンプルプログラムを動かしてみて、動作の様子を確認したいと思います。

  • plainモードで、動作させると、プレーンテキスト形式の文書が出力されます。
  • htmlモードで、動作させると、テーブル形式のリンク集のHTMLファイルが生成されます。

(1) plainモードで動かしてみる

まずは、プレーンテキスト形式の文書が出力するコードを動かしてみます。

$ python Main.py plain
======================
Greeting

*** From the morning to the afternoon ***
- Good morning
- Hello
*** In the evening ***
- Good evening
- Good night
- Good bye
======================

所謂、プレーンテキストの文書が出力されました。

(2) htmlモードを動かしてみる

つづいて、TableベースのWebページを作成するコードを動かしてみます。

$ python Main.py html
[Greeting.html] was created.

Greeting.htmlというファイルが生成されました。
Webブラウザで、見た目を確認してみると、こんな感じでした。
tablehtml.png

■ サンプルプログラムの詳細

Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Builder

  • ディレクトリ構成
.
├── Main.py
└── builder
    ├── __init__.py
    ├── builder.py
    ├── director.py
    ├── htmlbuilder
    │   ├── __init__.py
    │   └── html_builder.py
    └── textbuilder
        ├── __init__.py
        └── text_builder.py

(1) Builder(建築者)の役

Builder役は、インスタンスを作成するためのインタフェースを定めます。
Builder役には、インスタンスの各部分を作るためのメソッドが用意されます。
サンプルプログラムでは、Builderクラスが、この役を努めます。

builder/builder.py
from abc import ABCMeta, abstractmethod

class Builder(metaclass=ABCMeta):
    @abstractmethod
    def makeTitle(self, title):
        pass

    @abstractmethod
    def makeString(self, str):
        pass

    @abstractmethod
    def makeItems(self, items):
        pass

    @abstractmethod
    def close(self):
        pass

(2) ConcreteBuilder(具体的建築者)の役

ConcreteBuilder役は、Builder役のインタフェースを実装しているクラスです。実際のインスタンス作成で呼び出されるメソッドが、ここで定義されます。また、最終的にできた結果を得るためのメソッドが用意されます。
サンプルプログラムでは、TextBuilderクラスや、HTMLBuilderクラスが、この役を努めます。

builder/text_builder.py
from builder.builder import Builder

class TextBuilder(Builder):
    def __init__(self):
        self.buffer = []

    def makeTitle(self, title):
        self.buffer.append("======================\n")
        self.buffer.append(title + "\n")
        self.buffer.append("\n")

    def makeString(self, str):
        self.buffer.append("*** " + str + " ***" + "\n")

    def makeItems(self, items):
        for i in items:
            self.buffer.append("- " + i + "\n")

    def close(self):
        self.buffer.append("======================\n")

    def getResult(self):
        return ''.join(self.buffer)
builder/html_builder.py
from builder.builder import Builder

class HTMLBuilder(Builder):
    def __init__(self):
        self.buffer = []
        self.filename = ""
        self.f = None
        self.makeTitleCalled = False

    def makeTitle(self, title):
        self.filename = title+".html"
        self.f = open(self.filename, "w")
        self.f.write("<html><head><title>"+title+"</title></head></html>")
        self.f.write("<h1>"+title+"</h1>")
        self.makeTitleCalled = True

    def makeString(self, str):
        if not self.makeTitleCalled:
            raise RuntimeError
        self.f.write("<p>"+str+"</p>")

    def makeItems(self, items):
        if not self.makeTitleCalled:
            raise RuntimeError
        self.f.write("<ul>")
        for i in items:
            self.f.write("<li>"+i+"</li>")
        self.f.write("</ul>")

    def close(self):
        if not self.makeTitleCalled:
            raise RuntimeError
        self.f.write("</body></html>")
        self.f.close()

    def getResult(self):
        return self.filename

(3) Director(監督者)の役

Director役は、Builder役のインタフェースを使って、インスタンスを生成します。
ConcreteBuilder役に依存したプログラミングは行いません。ConcreteBuilder役が何であってもうまく動作するように、Builder役のメソッドのみを使います
サンプルプログラムでは、Directorクラスが、この役を努めます。

builder/director.py
class Director(object):
    def __init__(self, builder):
        self.__builder = builder

    def construct(self):
        self.__builder.makeTitle("Greeting")
        self.__builder.makeString("From the morning to the afternoon")
        self.__builder.makeItems(["Good morning", "Hello"])
        self.__builder.makeString("In the evening")
        self.__builder.makeItems(["Good evening", "Good night", "Good bye"])
        self.__builder.close()

(4) Client(依頼人)の役

Builder役を利用する役です。
サンプルプログラムでは、startMainメソッドが、この役を努めます。

Main.py
import sys

from builder.director import Director
from builder.textbuilder.text_builder import TextBuilder
from builder.htmlbuilder.html_builder import HTMLBuilder

def startMain(opt):
    if opt == "plain":
        builder = TextBuilder()
        director = Director(builder)
        director.construct()
        result = builder.getResult()
        print(result)
    elif opt == "html":
        builder = HTMLBuilder()
        director = Director(builder)
        director.construct()
        result = builder.getResult()
        print("[" + result + "]" + " was created.")


if __name__ == "__main__":
    startMain(sys.argv[1])

■ 参考URL

9
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11