デザインパターン　Builderパターン

# Builder パターン
**オブジェクトの生成、初期化を簡潔にする**ためのデザインパターン。

オブジェクトを生成（インスタンス化、`new`）するためには、必ずコンストラクタを利用する必要がある。

ただし、「引数のバリエーションが多い」、「オプショナルな引数がある」などの場合、コンストラクタ内の処理が複雑化し、それを利用するクライアント側は引数が必須なのか、オプショナルなのかを理解して使用しなければならない。そのため、クライアントとコンストラクタ間の依存度が高くなる。

**Builderパターン**はこのようなシチュエーションで利用することで、以下の利点を得ることができる。

- コンストラクタ内の複雑な初期化を簡潔にできる
- クライアントにとってオブジェクトの初期化が行いやすくなる

Builderパターンではクライアントコードが直接コンストラクタを利用しない為、オブジェクト作成のロジックが隠蔽されることになる。この点ではFactoryパターンと似ているように思えるが、BuilderパターンはFactoryパターンと比較して、クライアントが必要なオブジェクトへの知識量が多い。

| デザインパターン | オブジェクトに対して必要な知識量 |
|:-:|:-:|
| Builderパターン | **多い** |
| Factoryパターン | 少ない |

https://qiita.com/AsahinaKei/items/3a1f17b728769bab95b7

:::note info
Builderパターンはオブジェクトの生成・初期化を簡潔にするためのデザインパターン
:::


## 構成要素

### Target
**生成したいオブジェクト**。

設定値を保持するデータオブジェクトとしての役割を持つ。

コンストラクタが`private`で定義されることで、クライアントに対して直接インスタンス化する方法を提供しない。

ただし以下の説明では`private`でないところから説明を始め、段階的に`private`化を行う。

基本的にTargetオブジェクトの生成はBuilderを介して行う。

### Builder
オブジェクトの生成を担当する。

**メソッドチェーン**によって簡潔な記述方法を提供する。

### Director
オブジェクトの作成過程を決定する。

Builderパターンにとって必須の要素ではないが、Directorの存在によって複雑な初期化ロジックがある場合にクライアントコードから複雑さを切り離す事ができる。

オプショナルな引数が多くある場合に、クライアント側にそれら引数の組み合わせを、より簡潔に選択する方法を提供する。

**内部でBuilderを保持している**。

## 全体図

![builder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/4356c131-f058-1038-79ec-b4436e481cab.png)


## パターン　〜その1〜

コンストラクタのオプショナルな引数に対する処理が`Builder`クラスの提供するメソッドチェーンによって簡潔な記述方法になる。

また`Director`がオブジェクトの生成方法を提供することで、クライアントが`Target`に依存しなくなる。

ただし、以下のコードにはクライアントが直接`Target`クラスを生成できるという課題がある。これについては[パターン〜その2〜](#パターンその2)で対応することとしたい。

```java:Target.java
// 生成対象のオブジェクト
public class Target {
    private int a;
    private int b;
    private int c;

    // できればprivate化したいところ
    Target(Builder builder) {
        // Builderの持つプロパティをコピーする
        this.a = builder.a;
        this.b = builder.b;
        this.c = builder.c;
    }

    int getA() {
        return a;
    }

    int getB() {
        return b;
    }

    int getC() {
        return c;
    }
}
```

```java:Builder.java
// オブジェクトを生成する
class Builder {
    int a;
    int b;
    int c;

    Builder setA(int a) {
        this.a = a;
        return this;
    }

    Builder setB(int b) {
        this.b = b;
        return this;
    }

    Builder setC(int c) {
        this.c = c;
        return this;
    }

    Target build() {
        return new Target(this);
    }
}
```

```java:Director.java
// オブジェクトの生成手順を管理する
class Director {

    private Builder builder;

    Director(Builder builder) {
        this.builder = builder;
    }

    // 生成方法1
    Target constructA() {
        return builder
                .setA(1)
                .build();
    }

    // 生成方法2
    Target constructB() {
        return builder
                .setB(2)
                .build();
    }

    // 生成方法3
    Target constructC() {
        return builder
                .setC(3)
                .build();
    }

    // 生成方法4
    Target constructD() {
        return builder
                .setA(1)
                .setB(2)
                .setC(3)
                .build();
    }
}
```

```java:Main.java
// クライアント
public class Main {
    public static void main(String[] args){
        Builder builder = new Builder();
        Director director = new Director(builder);

        // オブジェクトがどのように初期化されているのかを知らなくて良い（疎結合）
        Target targetA = director.constructA();
        Target targetB = director.constructB();
        Target targetC = director.constructC();
        Target targetD = director.constructD();

        // ただしコンストラクタがprivateではないので、クライアントが直接Targetを生成できる課題がある
        Target target = new Target(builder);
    }
}

```

## パターン　〜その2〜

[パターン〜その1〜](#パターンその1)には、クライアントコードから直接`Target`クラスがインスタンス化できてしまう課題があったため、これを解消する。

まず`Builder`クラスを`Target`クラスの [インナークラス](https://qiita.com/AsahinaKei/items/32e5c97740814ba6f3a5#%E3%82%A4%E3%83%B3%E3%83%8A%E3%83%BC%E3%82%AF%E3%83%A9%E3%82%B9) として定義する。

`Builder`は、`Target`クラス（インナークラスの外側クラスは **Enclosing Class** と呼ばれる）のインスタンスが存在しない段階で機能する必要があるため、`Target`クラスの`static`メンバとして定義する。

`static`インナークラスは Enclosing Class のインスタンスではなく、 Enclosing Class 自体に属する。

これにより`Target`クラスのコンストラクタを`private`化する事ができ、クライアントから直接`Target`がインスタンス化できなくなる。つまり、クライアントと`Target`が疎結合になる。

また、`Director`はBuilderパターンにとって必須の要素ではないため、ここからは削除してしまい、Builderパターンに焦点を当てていくことにする。（`Director`が持っていたオブジェクトに関わる知識はクライアント側に移動する。クライアント側はより多くの知識が必要になってしまうので、これが嫌な場合にはやはり`Director`が必要。）

```java:Target.java
public class Target {
    private int a;
    private int b;
    private int c;

    // privateなコンストラクタ
    private Target(Builder builder) {
        this.a = builder.a;
        this.b = builder.b;
        this.c = builder.c;
    }

    int getA() {
        return a;
    }

    int getB() {
        return b;
    }

    int getC() {
        return c;
    }

    // インナークラス化したBuilder
    static class Builder {
        private int a;
        private int b;
        private int c;

        Builder setA(int a) {
            this.a = a;
            return this;
        }

        Builder setB(int b) {
            this.b = b;
            return this;
        }

        Builder setC(int c) {
            this.c = c;
            return this;
        }

        Target build() {
            return new Target(this);
        }
    }
}
```

```java:Main.java
// クライアント
public class Main {
    public static void main(String[] args) {
        Target.Builder builder = new Target.Builder();

        // Directorの役割はクライアント側に移動
        Target target1 = builder
                .setA(1)
                .build();

        Target target2 = builder
                .setB(2)
                .build();

        Target target3 = builder
                .setC(3)
                .build();

        Target target4 = builder
                .setA(1)
                .setB(2)
                .setC(3)
                .build();

        // 直接Targetをインスタンス化できなくなった
        Target target = new Target(builder);　// コンパイルエラー
    }
}
```

## パターン　〜その3〜

パターン3では [ラムダ式](https://qiita.com/AsahinaKei/items/b05b5b59e94840cbbaa1#%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E8%A8%98%E8%BF%B0%E3%81%99%E3%82%8B) を利用してさらに記述を簡略化する。

**関数型インターフェース**の一つである`java.util.function.Consumer<T>`を利用する。

`Consumer<T>`には引数を一つ受け取り戻り値を返さないメソッド`accept()`が1つだけ定義されている。

> `@FunctionalInterface
public interface Consumer<T>{
    void accept(T t);
}`

この部分をラムダ式によって置き換える。

```java
Builder setA(int a) {
    this.a = a;
    return this;
}

builder
    .setA(1)
    .setB(2)
    .setC(3)
    .build();
```

特にセッター部分の定義が不要になるため、フィールドが増える度にセッターを追加する必要がない。

```java:Target.java
import java.util.function.Consumer;

public class Target {
    private int a;
    private int b;
    private int c;

    private Target(Builder builder) {
        this.a = builder.a;
        this.b = builder.b;
        this.c = builder.c;
    }

    int getA() {
        return a;
    }

    int getB() {
        return b;
    }

    int getC() {
        return c;
    }

    // セッターの定義が必要なくなったBuilder
    static class Builder {
        public int a;
        public int b;
        public int c;

        
        Builder with(Consumer<Builder> consumer) {  // consumer: 関数型インターフェースが実装されたクラスのインスタンス
            // 受け取ったインスタンスで実装されているメソッドを実行する
            consumer.accept(this);
            return this;
        }

        Target build() {
            return new Target(this);
        }
    }
}
```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        Target.Builder targetBuilder = new Target.Builder();

        // ラムダ式によって Consumer<T> を実装した匿名クラスを生成
        Target target1 = targetBuilder.with(builder -> {
            builder.a = 1;
        }).build();

        Target target2 = targetBuilder.with(builder -> {
            builder.b = 2;
        }).build();

        Target target3 = targetBuilder.with(builder -> {
            builder.c = 3;
        }).build();

        Target target4 = targetBuilder.with(builder -> {
            builder.a = 1;
            builder.b = 2;
            builder.c = 3;
        }).build();
    }
}
```

# 参考
・<a href="https://amzn.asia/d/6zVX04G" target="_blank" rel="noopener noreferrer">徹底攻略Java SE 11 Gold問題集[1Z0-816]対応</a>