0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Javaのビルダーパターン実践 — コンストラクタ引数が増えた時の解決策

0
Posted at

Javaのビルダーパターン実践 — コンストラクタ引数が増えた時の解決策

はじめに

こんなコードを書いたことはありませんか?

User user = new User("田中太郎", 25, "tokyo@example.com", "Tokyo", true, "admin");

引数が6つあって、どれが何かまったくわかりません。しかも引数の型が同じだと渡し間違えてもコンパイルエラーにならないという怖さがあります。

この問題を解決するのが「ビルダーパターン」です。


問題:コンストラクタ引数が増えた時の苦しみ

// 悪い例:引数が多すぎて意味不明
public class User {
    private final String name;
    private final int age;
    private final String email;
    private final String address;
    private final boolean active;
    private final String role;

    public User(String name, int age, String email, String address, boolean active, String role) {
        this.name = name;
        this.age = age;
        this.email = email;
        this.address = address;
        this.active = active;
        this.role = role;
    }
}
// 呼び出し側:何が何かわからない
User user = new User("田中太郎", 25, "tokyo@example.com", "Tokyo", true, "admin");
//                    名前?      年齢? メール?             住所?    有効? ロール?

問題点:

  • 引数の意味が見た目でわからない
  • String 型が複数あると渡す順番を間違えやすい
  • コンパイルエラーにならないので実行時まで気づかない

ビルダーパターンとは

オブジェクトの生成を「段階的に組み立てる」パターンです。

// ビルダーパターンを使った呼び出し
User user = User.builder()
        .name("田中太郎")
        .age(25)
        .email("tokyo@example.com")
        .address("Tokyo")
        .active(true)
        .role("admin")
        .build();

何を設定しているかが一目瞭然になります。


手動でビルダーを実装する

public class User {
    private final String name;
    private final int age;
    private final String email;
    private final String address;
    private final boolean active;
    private final String role;

    // コンストラクタはprivate(Builderからのみ生成できる)
    private User(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.email = builder.email;
        this.address = builder.address;
        this.active = builder.active;
        this.role = builder.role;
    }

    // ゲッター
    public String name()    { return name; }
    public int age()        { return age; }
    public String email()   { return email; }
    public String address() { return address; }
    public boolean active() { return active; }
    public String role()    { return role; }

    // ビルダーのエントリーポイント
    public static Builder builder() {
        return new Builder();
    }

    // 静的内部クラスとしてBuilderを定義
    public static class Builder {
        private String name;
        private int age;
        private String email;
        private String address;
        private boolean active;
        private String role;

        public Builder name(String name) {
            this.name = name;
            return this; // メソッドチェーンのためにthisを返す
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        public Builder active(boolean active) {
            this.active = active;
            return this;
        }

        public Builder role(String role) {
            this.role = role;
            return this;
        }

        public User build() {
            // バリデーションをここに集約できる
            if (name == null || name.isBlank()) {
                throw new IllegalStateException("nameは必須です");
            }
            if (email == null || email.isBlank()) {
                throw new IllegalStateException("emailは必須です");
            }
            return new User(this);
        }
    }
}

Lombokの@Builderで楽をする

実務ではLombokを使うとボイラープレートコードを省略できます。

<!-- pom.xml -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class User {
    private final String name;
    private final int age;
    private final String email;
    private final String address;
    private final boolean active;
    private final String role;
}

これだけで手動実装と同等のビルダーが自動生成されます。

// 呼び出し方は手動実装と同じ
User user = User.builder()
        .name("田中太郎")
        .age(25)
        .email("tokyo@example.com")
        .address("Tokyo")
        .active(true)
        .role("admin")
        .build();

デフォルト値を設定したい場合は @Builder.Default を使います:

@Getter
@Builder
public class User {
    private final String name;
    private final int age;
    private final String email;
    private final String address;
    @Builder.Default
    private final boolean active = true;  // デフォルトtrue
    @Builder.Default
    private final String role = "user";   // デフォルト"user"
}

いつビルダーパターンを使うか

状況 判断
コンストラクタ引数が4つ以上 ビルダーを検討する
同じ型の引数が複数ある ビルダーを強く推奨
省略可能なフィールドがある ビルダーが適している
引数が2〜3つで全て必須 コンストラクタでOK

まとめ

ビルダーパターンを使うメリット:

  • 何を設定しているか名前で明示される
  • 引数の順番を気にしなくてよい
  • 省略可能なフィールドを柔軟に扱える
  • build() にバリデーションを集約できる

手動実装はコード量が増えますが、Lombokの @Builder を使えば1アノテーションで済みます。コンストラクタ引数が増えてきたら、ビルダーパターンへの切り替えを検討してみてください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?