一言で言うと、「同じ生成過程で素材の異なるオブジェクトを作る」パターンです。
①タイトル
②ヘッダー
③本文
④フッター
が必要な文章を、HTMLとPlainTextで作成します。
import abc
def main():
html = Director().construct(HTMLBuilder())
text = Director().construct(TextBuilder())
print(html)
print("****************")
print(text)
class Director():
def construct(self, builder):
all_str = ""
all_str += builder.build_title("Monthly Report")
all_str += builder.build_header("-------")
all_str += builder.build_contents(["Monday: 20", "Tuesday: 30"])
all_str += builder.build_footer("-*-*-*-")
return all_str
class AbstractBuilder(metaclass=abc.ABCMeta):
@abc.abstractmethod
def build_title(self, title):
pass
@abc.abstractmethod
def build_header(self, header):
pass
@abc.abstractmethod
def build_contents(self, contents):
pass
@abc.abstractmethod
def build_footer(self, footer):
pass
# ConcreteBuilder
class HTMLBuilder(AbstractBuilder):
def build_title(self, title):
return "<h1>{}</h1>\n".format(title)
def build_header(self, header):
return "<header><p>{}</p></header>\n".format(header)
def build_contents(self, contents):
html_contents = []
for content in contents:
html_contents.append("<p>{}</p>\n".format(content))
return "".join(html_contents)
def build_footer(self, footer):
return "<footer><p>{}</p></footer>\n".format(footer)
# ConcreteBuilder
class TextBuilder(AbstractBuilder):
def build_title(self, title):
return "**{}**\n".format(title)
def build_header(self, header):
return "{}\n".format(header)
def build_contents(self, contents):
text_contents = []
for content in contents:
text_contents.append("{}\n".format(content))
return "".join(text_contents)
def build_footer(self, footer):
return "{}\n".format(footer)
if __name__ == "__main__":
main()
大事なのはDicector
クラスを噛ませていることです。
class Director():
def construct(self, builder):
all_str = ""
all_str += builder.build_title("Monthly Report")
all_str += builder.build_header("-------")
all_str += builder.build_contents(["Monday: 20", "Tuesday: 30"])
all_str += builder.build_footer("-*-*-*-")
return all_str
construct
メソッドにに具体的な手順を書き込み、実際の作成はbuilderに任せることで、
オプションがかなり簡易化されます。
引数をbuilderにしないと、以下のような「telescoping constructor」アンチパターンとなってしまいます。
class Director():
def construct(self, format="html", title="", header="", contents=[], footer=""):
# ...
特になし。
サンプルコードではabcクラスを使用しています。
Wikipedia
クラス図をお借りしました。
Tech Scoreさま
概念的なところの説明に一部参考にさせていただきました。