はじめに
本記事は、GoFデザインパターン解説シリーズの1つです。
今回は、生成に関するパターンの中のBuilder(ビルダー)パターンを、Javaのサンプルコードとともに解説します。
解決したい課題
コンストラクタの引数がどんどん増えていく、いわゆる「テレスコーピング・コンストラクタ(望遠鏡状に伸びるコンストラクタ)」という問題があります。
// 引数が多すぎるコンストラクタの例
public class Pizza {
public Pizza(String size, boolean cheese, boolean pepperoni,
boolean mushroom, boolean onion, boolean extraSauce) {
// ...
}
}
// 呼び出し側: どの引数が何を表すか、コードを見ただけでは分かりにくい
Pizza pizza = new Pizza("L", true, false, true, false, true);
このようなコンストラクタは、①引数の並び順を間違えやすい、②「一部の項目だけ指定したい」という柔軟な使い方がしづらい、③何番目がどの意味かコードから読み取りにくい、といった問題を抱えます。
Builderパターンは、オブジェクトの組み立て手順を、専用のBuilderオブジェクトに分離し、メソッドチェーンで1つずつ項目を指定してから、最後に組み立てを完了させることで、この問題を解決します。
クラス構成
- Product(製品): 最終的に組み立てられる、複雑な構造を持つオブジェクト
- Builder(ビルダー): Productを組み立てるためのメソッド群を持つクラス。メソッドチェーンで各項目を設定できる
- Director(ディレクター): (省略されることも多い)決まった手順でBuilderを操作し、定型的なProductを組み立てる役割
Javaでの実装例
先ほどのPizza(ピザ)を例に、Builderパターンで書き直してみます。
public class Pizza {
private final String size; // 必須
private final boolean cheese;
private final boolean pepperoni;
private final boolean mushroom;
private final boolean onion;
private final boolean extraSauce;
// コンストラクタはprivateにし、Builder経由でのみ生成させる
private Pizza(Builder builder) {
this.size = builder.size;
this.cheese = builder.cheese;
this.pepperoni = builder.pepperoni;
this.mushroom = builder.mushroom;
this.onion = builder.onion;
this.extraSauce = builder.extraSauce;
}
@Override
public String toString() {
return String.format(
"Pizza[size=%s, cheese=%b, pepperoni=%b, mushroom=%b, onion=%b, extraSauce=%b]",
size, cheese, pepperoni, mushroom, onion, extraSauce
);
}
// Pizzaクラスの内部クラスとしてBuilderを定義する(一般的な実装パターン)
public static class Builder {
// 必須項目
private final String size;
// 任意項目(デフォルト値を設定しておく)
private boolean cheese = false;
private boolean pepperoni = false;
private boolean mushroom = false;
private boolean onion = false;
private boolean extraSauce = false;
public Builder(String size) {
this.size = size; // 必須項目はBuilderのコンストラクタで受け取る
}
// 各メソッドがBuilder自身(this)を返すことで、メソッドチェーンを可能にする
public Builder cheese(boolean value) {
this.cheese = value;
return this;
}
public Builder pepperoni(boolean value) {
this.pepperoni = value;
return this;
}
public Builder mushroom(boolean value) {
this.mushroom = value;
return this;
}
public Builder onion(boolean value) {
this.onion = value;
return this;
}
public Builder extraSauce(boolean value) {
this.extraSauce = value;
return this;
}
// 最後に呼び出し、実際にPizzaインスタンスを組み立てる
public Pizza build() {
return new Pizza(this);
}
}
}
利用側のコード
public class Main {
public static void main(String[] args) {
Pizza pizza = new Pizza.Builder("L")
.cheese(true)
.mushroom(true)
.extraSauce(true)
.build();
System.out.println(pizza);
// 別の組み合わせも、必要な項目だけ指定すればよい
Pizza simplePizza = new Pizza.Builder("M")
.cheese(true)
.build();
System.out.println(simplePizza);
}
}
実行結果:
Pizza[size=L, cheese=true, pepperoni=false, mushroom=true, onion=false, extraSauce=true]
Pizza[size=M, cheese=true, pepperoni=false, mushroom=false, onion=false, extraSauce=false]
コンストラクタに大量の引数を並べる代わりに、設定したい項目だけをメソッドチェーンで指定し、最後にbuild()を呼ぶだけで済みます。
コードを読むだけで「どの項目に何を設定しているか」が明確になり、任意項目を省略しても正しく動作します。
Director(ディレクター)を使う例
決まったパターンの組み立てを何度も行う場合は、その手順自体をDirectorクラスにまとめることもあります。
public class PizzaDirector {
// 「マルゲリータ風」の決まった組み立て手順をカプセル化する
public static Pizza createMargheritaStyle(String size) {
return new Pizza.Builder(size)
.cheese(true)
.extraSauce(true)
.build();
}
}
Pizza margherita = PizzaDirector.createMargheritaStyle("M");
Java標準ライブラリでの実例
Builderパターンは、Java標準ライブラリでも使われています。
文字列を組み立てるStringBuilderが代表例です。
String result = new StringBuilder()
.append("Hello, ")
.append("World")
.append("!")
.toString();
append()が自分自身(StringBuilder)を返すことでメソッドチェーンが可能になっている点は、本記事のPizza例と同じ考え方です。
使用場面
- コンストラクタの引数(特に任意項目)が多く、可読性が損なわれている場合
- 「一部の項目だけ指定した、様々なバリエーションのオブジェクト」を作りたい場合
- オブジェクトの組み立て手順自体が複数のステップに分かれ、順序が重要な場合
- イミュータブル(不変)なオブジェクトを、柔軟に組み立てて生成したい場合
まとめ
| 項目 | 内容 |
|---|---|
| 解決する課題 | コンストラクタの引数が多すぎて、可読性・柔軟性が損なわれる問題(テレスコーピング・コンストラクタ) |
| 実現方法 | 組み立て専用のBuilderクラスを用意し、メソッドチェーンで項目を指定してからbuild()で完成させる |
| メリット | 任意項目を自由に組み合わせられ、コードの可読性も高い |
| 実例 | Java標準のStringBuilderも同じ考え方を採用している |
おすすめ書籍
GoFの23パターンをより深く学びたい方には、以下の書籍がおすすめです。
Java言語のサンプルコードとUMLを使い、初心者にもわかりやすく解説されています。
Java言語で学ぶデザインパターン入門 第3版 [ 結城 浩 ]
※本リンクはアフィリエイトリンクを含みます。


