必須プロパティのセットを強要するBuilderパターン

# 概要
- オブジェクトの生成時に<font color=red>**Mandatory(必須)のプロパティ**</font>を**<font color=red>必ずセットさせたい**</font>場合に使う**Builderパターン**です
- さらに決められた順序でしかプロパティをセットできないようにオブジェクト生成シーケンスに制約を設けるパターンです
- Effective Java,GoF,Lombokとも異なるパターンです

## 環境
- Java8以降

# 対象とするオブジェクトとプロパティ(フィールド)

あるプロパティをもつオブジェクトをつくりたいとき
今回は例として Person(人間) というオブジェクトについて考える

- **必須でセットさせたいプロパティ**
  - String name(名前)
  - Integer age(年齢)
  - String gender(性別)
  - Integer height(身長)
- **任意でOKなプロパティ**
  - String eyeColor(瞳の色)
  - String hairColor(髪の色)
  - String hobby(趣味)

# コード

Builderのコードは以下のとおり。

```java:Person

package com.example;
import java.util.Optional;

public class Person {

  private final String name;// Required
  private final Integer age;// Required
  private final String gender;// Required
  private final Integer height;// Required
  private final Optional<String> eyeColor;// Optional(use Optional<String>)
  private final Optional<String> hairColor;// Optional(use Optional<String>)
  private final Optional<String> hobby;// Optional(use Optional<String>)

  Person(Builder.Builder1 builder) {
    this.name = builder.name; 
    this.age = builder.age; 
    this.gender = builder.gender; 
    this.height = builder.height; 
    this.eyeColor = builder.eyeColor; 
    this.hairColor = builder.hairColor; 
    this.hobby = builder.hobby; 
  }
  public static Builder builder() {
    return new Builder();
  }

  public static final class Builder {
    public Builder1 name(String name) {
      return new Builder1(name);
    }
    public static final class Builder1 {
      final String name;
      Integer age;
      String gender;
      Integer height;
      Optional<String> eyeColor;
      Optional<String> hairColor;
      Optional<String> hobby;

      private Builder1(String name) {
        this.name = name;
      }
      public Builder2 age(Integer age) {
        this.age = age;
        return new Builder2(Builder1.this);
      }
    }
    public static final class Builder2 {
      final Builder1 builder;

      private Builder2(Builder1 builder) {
        this.builder = builder;
      }
      public Builder3 gender(String gender) {
        this.builder.gender = gender;
        return new Builder3(this.builder);
      }
    }
    public static final class Builder3 {
      final Builder1 builder;

      private Builder3(Builder1 builder) {
        this.builder = builder;
      }
      public Builder4 height(Integer height) {
        this.builder.height = height;
        return new Builder4(this.builder);
      }
    }
    public static final class Builder4 {
      final Builder1 builder;

      private Builder4(Builder1 builder) {
        this.builder = builder;
      }
      public Builder4 eyeColor(String eyeColor){
        this.builder.eyeColor = Optional.of(eyeColor);
        return this;
      }
      public Builder4 hairColor(String hairColor){
        this.builder.hairColor = Optional.of(hairColor);
        return this;
      }
      public Builder4 hobby(String hobby){
        this.builder.hobby = Optional.of(hobby);
        return this;
      }
      public Person build() {
        return new Person(this.builder);
      }
    }
  }

  public String name() {
    return this.name;
  }
  public Integer age() {
    return this.age;
  }
  public String gender() {
    return this.gender;
  }
  public Integer height() {
    return this.height;
  }
  public Optional<String> eyeColor() {
    return this.eyeColor;
  }
  public Optional<String> hairColor() {
    return this.hairColor;
  }
  public Optional<String> hobby() {
    return this.hobby;
  }

  @Override
  public String toString() {
    return "Person(name=" + this.name + ", age=" + this.age + ", gender=" + this.gender + ", height=" + this.height + ", eyeColor=" + this.eyeColor + ", hairColor=" + this.hairColor + ", hobby=" + this.hobby + ")";
  }

  public void doSomething() {
      // do something
  }
}
```

## ポイント

- 必須となるプロパティのsetterメソッドごとに個別のBuilderを割り振る
- setterの戻り値に次のメソッドのBuilderを返していく
- このようにプロパティごとにBuilderを互い違い（ツイストさせる）に返していく

## 利用側のコード

```java
package test;
import com.example.Person;
public class Main {
    public static void main(String[] args) {
        Person person = Person.builder().name("Tom").age(20).gender("male").height(200).build();
        System.out.println(person);
    }
}
```

![builder_man.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170905/169f0d9d-347b-a25f-621a-53fda1345f21.gif)
## メリット

- 必須のプロパティをすべて指定しないと**build**できないようになっている
- 必須のプロパティを決められた順番かつ明示的(setterを呼ぶことで)に設定できる  
逆にいえば、以下の**Telescoping Constructor**のように引数を列挙していくやり方だとプロパティの数が増えるにつけて、利用側のコードの見通しが悪くなっていく。

```java:TelescopingConstructor
  public Person(String name, Integer age, String gender, Integer height, String eyeColor, String hairColor, String hobby) {
    this.name = name; 
    this.age = age; 
    this.gender = gender; 
    this.height = height; 
    this.eyeColor = eyeColor; 
    this.hairColor = hairColor; 
    this.hobby = hobby; 
  }
```

## デメリット

- (利用側のコードではなく)  
Builder側のコードが複雑になり見通しがわるくなる
- ボイラープレート的コードを書くのに苦痛をともなう（※）

（※）デメリット緩和のため自動生成ツールをつくりました
https://riversun.github.io/java-builder/

# まとめ
- オブジェクト生成時に決められた初期化シーケンスを強要するBuilderパターンを紹介しました
- このパターンはバリエーションも多く**interface**を用いて実装する方法や**Builder**を明示的に生成せずにやる方法なども利用されているようです。

# 関連記事
- https://github.com/riversun/java-builder-patterns
- [(Qiitaに自動生成ツール関連記事も投稿しています)](https://qiita.com/riversun/items/41869b9fd88fae74cd92)