Design Pattern | Builder Pattern(ビルダーパターン)

Builderパターンとは?

  • Buildという単語は、構造を持つ大きなものを構築するという意味である。
  • 複雑な構造を持つものを作るとき、一度に完成させるのは難しい。まず全体を構成する各部分を作り、段階を踏んで組み立てていくことになる。
  • 同じ構築工程で、異なる表現オブジェクトを生成する。
  • コンストラクタに入るパラメータが多くても少なくても、順番にパラメータを受け取り、すべてのパラメータを受け取った後にこれらの変数を統合して一度に使用する。
  • Builderパターンは、構造を持つインスタンスを構築する方法である。
  • GoFデザインパターンでは、生成に関するデザインパターンに分類される。

Builderパターンのサンプルプログラム

文書を通常のテキストおよびHTML形式で出力するサンプルプログラムである。

Class Diagram
Builder Pattern Class Diagram

1. Builderクラス

文書を構成するためのメソッドを定義した抽象クラスである。

Builder.java

public abstract class Builder {
    public abstract void makeTitle(String title);
    public abstract void makeString(String str);
    public abstract void makeItems(String[] items);
    public abstract void close();
}

2. Guideクラス

1つの文書を作るクラスである。

Guide.java

package com.devkuma.designpattern.creational.builder;

public class Guide {

    private Builder builder;

    public Guide(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.makeTitle("야유회에 대해서");
        builder.makeString("일시");
        builder.makeItems(new String[]{
                "2022/3/14 (월)",
                "11:00~",
        });
        builder.makeString("장소");
        builder.makeItems(new String[]{
                "xxx 캠핌장",
        });
        builder.makeString("준비물");
        builder.makeItems(new String[]{
                "회비",
                "고기",
                "음료수",
        });
        builder.close();
    }
}

3. TextBuilderクラス

通常のテキストで文書を作るクラスである。

TextBuilder.java

package com.devkuma.designpattern.creational.builder;

public class TextBuilder extends Builder {

    private StringBuffer buffer = new StringBuffer();

    public void makeTitle(String title) {
        buffer.append("==============================\n");
        buffer.append("'" + title + "'\n");
        buffer.append("\n");
    }

    public void makeString(String str) {
        buffer.append("- " + str + "\n");
    }

    public void makeItems(String[] items) {
        for (int i = 0; i < items.length; i++) {
            buffer.append(" - " + items[i] + "\n");
        }
        buffer.append("\n");
    }

    public void close() {
        buffer.append("==============================\n");
    }

    public String getResult() {
        return buffer.toString();
    }
}

4. HTMLBuilderクラス

HTMLファイルとして文書を作成するクラスである。

HTMLBuilder.java

package com.devkuma.designpattern.creational.builder;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class HtmlBuilder extends Builder {

    private String filename;
    private PrintWriter writer;

    public void makeTitle(String title) {
        filename = title + ".html";
        try {
            writer = new PrintWriter(new FileWriter(filename));
        } catch (IOException e) {
            e.printStackTrace();
        }
        writer.println("<html><head><title>" + title + "</title></head><body>");
        writer.println("<h1>" + title + "</h1>");
    }

    public void makeString(String str) {
        writer.println("<p>" + str + "</p>");
    }

    public void makeItems(String[] items) {
        writer.println("<ul>");
        for (int i = 0; i < items.length; i++) {
            writer.println("<li>" + items[i] + "</li>");
        }
        writer.println("</ul>");
    }

    public void close() {
        writer.println("</body></html>");
        writer.close();
    }

    public String getResult() {
        return filename;
    }
}

5. Mainクラス

サンプルプログラムを実行するメインクラスである。

Main.java

package com.devkuma.designpattern.creational.builder;

public class Main {
    public static void main(String[] args) {

        if (args.length != 1) {
            System.exit(0);
        }
        if (args[0].equals("plain")) {
            TextBuilder textbuilder = new TextBuilder();
            Guide guide = new Guide(textbuilder);
            guide.construct();
            String result = textbuilder.getResult();
            System.out.println(result);
        } else if (args[0].equals("html")) {
            HtmlBuilder htmlbuilder = new HtmlBuilder();
            Guide guide = new Guide(htmlbuilder);
            guide.construct();
            String filename = htmlbuilder.getResult();
            System.out.println(filename + "이 작성되었습니다.");
        } else {
            System.exit(0);
        }
    }
}

6. 実行結果

テキスト

==============================
'야유회에 대해서'

- 일시
 - 2022/3/14 (월)
 - 11:00~

- 장소
 - xxx 캠핌장

- 준비물
 - 회비
 - 고기
 - 음료수

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

HTML

<html><head><title>야유회에 대해서</title></head><body>
<h1>야유회에 대해서</h1>
<p>일시</p>
<ul>
<li>2022/3/14 (월)</li>
<li>11:00~</li>
</ul>
<p>장소</p>
<ul>
<li>xxx 캠핌장</li>
</ul>
<p>준비물</p>
<ul>
<li>회비</li>
<li>고기</li>
<li>음료수</li>
</ul>
</body></html>

7. Builderパターンのメリット

サンプルプログラムを見ると、Mainクラスは文書の構築方法(Builderクラスのメソッド)を知らない。MainクラスはGuideクラスのconstructメソッドを呼び出すだけで文書を構築できる。
そして、GuideクラスはBuilderクラスを使って文書を作成するが、Guideクラスは自分が実際に利用しているクラスが何か(TextBuilderなのかHtmlBuilderなのか)を知らない。
TextBuilderのインスタンスをGuideに渡しても、HTMLBuilderのインスタンスをGuideに渡しても正しく機能するのは、GuideクラスがBuilderクラスの具体的なクラスを知らないためである。
知らないからこそ差し替えが可能であり、差し替えが可能だからこそ部品としての価値が高くなる。