Javaで書くBuilderパターンのパターン

## Builderパターン
個人的にBuilderパターンはオブジェクトの生成制御や、ものによっては可読性が高くて好きなパターンなんですが、その実装には用途によっていくつかパターンがあるので、まとめてみました。


## 生成するオブジェクトの条件
 - クラス名:People
 - フィールド:String name(必須), Integer age(必須), String hobby(オプション)
 - 必須要素はnullを禁止
 - PeopleクラスはStringを返り値とするhelloメソッドを持つ
 - 今回はBuilderパターンの比較のため、パターン上必要でない限りgetterなどのメソッドは省略

## Native Builder
Builderパターンではなく、ただのコンストラクタ。Builderパターンを使いたくなるのはこれをやりたくないからだけど、比較のために記載。

```java:NativeBuilder
class People {
    private String name;
    private Integer age;
    private String hobby;
    
    People(String name, Integer age, String hobby) {
        if (name == null || age == null) {
            throw new NullPointerException();
        }
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    // getter,setter...
    String hello() {
        //...
    }
}

// new People("Tom", 12, "BaseBall").hello();
```
- 記述が単純。
- 不要なオプション要素が増えると、コンストラクタに大量のnullなどを渡す必要がある。
- 全ての値を引数で渡すため、渡している値が何を表しているか確認しづらく、また間違いやすい。

## GoF Builder
GoF本(オブジェクト指向における再利用のためのデザインパターン)で紹介されているBuilderパターン。

```java:GoFBuilder
class People {
    private String name;
    private Integer age;
    private String hobby;
	
    // getter,setter...
    String hello() {
	    //...
    }
}

class Director {
    private Builder builder;

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

    void construct() {
        builder.name("Tom");
        builder.age(12);
        builder.hobby("BaseBall");
    }
}

interface Builder {
    void name(String name);
    void age(Integer age);
    void hobby(String hobby);

    People getResult();
}

class PeopleBuilder implements Builder {
    private People people;

    PeopleBuilder() {
        this.people = new People();
    }

    @Override
    public void name(String name) {
        people.setName(name);
    }
    @Override
    public void age(Integer age) {
        people.setAge(age);
    }
    @Override
    public void hobby(String hobby) {
        people.setHobby(hobby);
    }
    @Override
    public People getResult() {
        if (people.getName() == null || people.getAge() == null) {
            throw new NullPointerException();
        }
        return this.people;
    }
}

// Builder builder = new PeopleBuilder();
// Director director = new Director(builder);
// director.construct();
// builder.getResult().hello();

```
- 生成されるオブジェクト(People)と生成ロジック(Builder)が分離され、PeopleがBuilderを知らなくていい。
- Builderの実装クラスを切り替えることで、生成するPeopleの制御を出来る。
- interfaceや実装など、記述量が多くなる。
- 生成処理が他のBuilderと比べると複雑。


## Effective Java Builder
Effective Javaで紹介されているBuilderパターン

```java:EffectiveJavaBuilder
class People {
    private String name;
    private Integer age;
    private String hobby;

    static class Builder {
        private String name;
        private Integer age;
        private String hobby;

        Builder(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        Builder hobby(String hobby) {
            this.hobby = hobby;
            return this;
        }

        People build() {
            if (name == null || age == null) {
                throw new NullPointerException();
            }
            return new People(this);
        }
    }

    private People(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.hobby = builder.hobby;
    }

    String hello() {
	    //...
    }
}

// new People.Builder("Tom", 12).hobby("BaseBall").build().hello();
```
- 流れるインターフェイスでオブジェクトの生成が可能。
- GoF Builderと比べ少ない記述量でオブジェクトの生成ができる。
- 生成ロジック(Builder)とオブジェクト(People)の分離ができない。


## Lombok Builder
ちょっと変化球ですが、[Lombok](http://projectlombok.org/)というライブラリの@Builderアノテーションで作成するパターン。

```java:LombokBuilder
@Builder
class People {
    @NonNull
    private String name;
    @NonNull
    private Integer age;
    private String hobby;

    String hello() {
	    //...
    }
}

// People.builder().name("Tom").age(12).hobby("BaseBall").build().hello();
```
- 簡潔な記述で実現できる
- @NonNullで生成時に例外を発生させることで、必須を実現
- 生成ロジックとオブジェクトの分離ができない(実質ソースには記述されない)


## Fluent Builder
[Javaによる関数型プログラミング](http://www.amazon.co.jp/Java%E3%81%AB%E3%82%88%E3%82%8B%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E2%80%95Java-8%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F%E3%81%A8Stream-Venkat-Subramaniam/dp/4873117046/)に紹介されているパターン。こちらは実際はBuilderというより、ローンパターン。

```java:FluentBuilder
class People {
    private People() {
    }

    private String name;
    private Integer age;
    private String hobby;

    People name(String name) {
        this.name = name;
        return this;
    }
    People age(Integer age) {
        this.age = age;
        return this;
    }
    People hobby(String hobby) {
        this.hobby = hobby;
        return this;
    }

   static String hello(final Consumer<People> builder) {
        People people = new People();
        builder.accept(people);
        if (people.name == null || people.age == null) {
            throw new NullPointerException();
        }
        //...
    }
}

// Consumer<People> builder = people -> people.name("Tom").age(12).hobby("BaseBall");
// People.hello(builder);
```
- Peopleオブジェクトのスコープが制限され、hello実行終了すると参照が消える。
- 流れるインターフェイスでオブジェクトの生成ロジックが記述できる。
- 生成ロジック(Consumer\<People>)の切り替えが用意に可能。
- Java8環境でないと、記述量も可読性も悪くなる。



## まとめ
JavaでBuilderっていってもいろいろ種類がありますね。個人的には細かな生成の制御をしなくていいなら、Lombok Builderが記述量が少なくて好きです。
Fluent BuilderもLombokを使えば更に簡潔に記載できますし。
他にもいろいろあると思うので、コメントでもなんでももらえればと思います！