マニュアル: Builderパターン

投稿日: 更新日:

作り方

クラス名

以下のいずれかがよく使われます。

  • 作りたいクラス + Builder
    • 例: DateTimeFormatter を作る DateTimeFormatterBuilder
  • 作りたいクラスの内部クラスでBuilder
    • 例: HttpUrl を作る HttpUrl.Builder

ファクトリー、コンストラクタ

一般的には引数なしのコンストラクタが使われます。 DbSetupOperationsのように、staticメソッドを用意しているものもありますが、 柔軟性でなく、流れるようなインタフェースにするために使われています。

メソッド

setterを実装しますが、 流れるようなインタフェースを考慮して、setなんとかという名前にはしません1

ただし、以下のような使い分けをする場合はsetを付けます。

最後に追加していくStringBuilderなどでは、append系の命名が使われることが多いです。

最後のメソッドはbuild()のことが多いです。

例外処理

  • setter: IllegalArgumentException
  • build(): IllegalStateException
    • 必要なパラメータを渡していない場合

チェック例外は流れるようなインタフェースと相性が悪いので、避けたほうがいいです。 必要なときは、ValueObject(URIなど)を渡すようにしたらいいと思います。

Mail = new MailBuilder()
         .to("test@example.com") // ここでチェック例外を投げたい場合
         .build();

ここでメールアドレスをセットするときにチェック例外を投げたい場合は以下のようにします。

MailAddress mailTo;
try {
  mailTo = MailAddress.of("test@example.com");
} catch (MailAddressException e) {
  // 例外処理
}

Mail = new MailBuilder()
         .to(mailTo)
         .build();

ビルダー自体のカスタマイズ

簡単なものなら、コンストラクタの引数に持つのが良いかと思います。 Builder自体を取り替えたい場合は、 DocumentBuilderFactoryのようにさらにファクトリを定義する例もありますが、 正直冗長かなと思います。setNamespaceAwareしか使ったことないし。。。

テンプレート

  • クラス名:
  • ビルドするクラス名:
  • 型:
  • 変数名:
public final class {{ builder_class_name }} {
  private {{ field_type }} {{ field_name }};

  public {{ builder_class_name }}() {
  }

  public {{ builder_class_name }} {{ field_name }}({{ field_type }} {{ field_name }}) {
    this.{{ field_name }} = {{ field_name }};
    return this;
  }

  public {{ class_name }} build() {
    return new {{ class_name }}(this.{{ field_name }});
  }
}