【Java】Lombokでビルダパターンを簡単に実装する

2020年8月1日

@Builderを使うと簡単にビルダパターンを実装できる

Lombokの@Builderをクラスにつけることによりビルダパターンを実装することができます。@Builderをつけただけはセッターが作成されないため@Valueと組み合わせて使うと良いと思います。これでインスタンスを作成し値を設定することはビルダーを使用する以外できなくなります。

フィールド変数へデフォルト値を設定するには@Builder.Defaultをフィールド変数へつけます。

@Builder, @Value, @Builder.Defaultを使うと以下の例のようになります。

// import文は省略

@Value
@Builder
public class Article {

    @Builder.Default
    private String title = "test article";

    @Builder.Default
    private String author = "taro";

    @Builder.Default
    private LocalDateTime publishedAt = LocalDateTime.now();

    @Builder.Default
    private String language = "Japanese";
}

インスタンを生成するコードは以下のようになります。値の確認にはAssertJを使用しています。

// import文は省略

public class TestSample {

    @Test
    public void test() {

        // authorとpublishedAt以外はデフォルト値でインスタンスを生成する
        var article1 = Article
                .builder()
                .author("foo") 
                .publishedAt(LocalDateTime.of(2000, 1, 1, 9, 10, 20))
                .build();

        // 生成したインスタンスの値をチェック
        assertThat(article1.getTitle()).isEqualTo("test article");
        assertThat(article1.getAuthor()).isEqualTo("foo");
        assertThat(article1.getPublishedAt()).isEqualTo(LocalDateTime.of(2000, 1, 1, 9, 10, 20));
        assertThat(article1.getLanguage()).isEqualTo("Japanese");
    }
}

ビルダーパターンのメリット

ビルダーパターンはフィールド変数の数が多いクラスに対して有効なパターンです。特に以下のような条件があるクラスに対して有効です。

  • フィールド変数の数が多い。
  • フィールド変数のデフォルト値が決まっている。
  • いくつかのフィールド変数の値をコンストラクタによって値を変更したい場合がある

コンストラクタで対応しようとすると、以下の例のようにデフォルト値から変更したいフィールド変数のパターン数だけコンストラクタを作成する必要があります。このようなコンストラクタの実装方法はテレスコーピングコンストラクタパターンと呼ばれています。

public class Article {

    private String title;

    private String author;

    private LocalDateTime published_at;

    private String language;

    // タイトル以外はデフォルト値とするコンストラクタ
    public Article(String title) {
        this(title, "taro", LocalDateTime.now(), "Japanese");
    }

    // タイトル、著者名以外はデフォルト値とするコンストラクタ
    public Article(String title, String author) {
        this(title, author, LocalDateTime.now(), "Japanese");
    }

    //  値を変更する組み合わせの数だけコンストラクタを書く必要がある

    // 全てのフィールドを引数に持つコンストラクタ
    public Article(String title, String author, LocalDateTime published_at, String language) {
        this.title = title;
        this.author = author;
        this.published_at = published_at;
        this.language = language;
    }

}

コンストラクタの引数が多いとインスタンス生成のためのコンストラクタ呼び出しのコード(例: new Article(…))を書く際に、何番目の引数がどのフィールド変数に対応するのか分かりにくくなります。実装者もコーディング時にストレスが貯まりますし、コードを読む人も辛くなります。

ビルダパターンでは値を設定する時にフィールド変数名に対応するメソッドを呼び出すためIDEによる補完が効きやすくコーディングが楽になります。コードを後から読むときにもメソッド名からどのフィールド変数へ値を設定しているかも分かりやすいです。