ビルダーパターン
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 67/87。
Builder Patternは、多数のオプションパラメーターを持つ複雑なオブジェクトを構築するという、一般的な問題を解決する生成デザインパターンです。複数のコンストラクターを作成したり、使用しないパラメーターにnullを渡したりする代わりに、Builderを使うとオブジェクトを段階的に構築できます。
必須フィールド(name、email)とオプションのフィールド(age、phone、address)を持つ User オブジェクトの作成を考えてみましょう。Builder を使わない場合、段階的なコンストラクターを用意するか、多数のパラメーターを持つ扱いにくいコンストラクターを作成する必要があります。Builder パターンなら、よりすっきりした方法で実現できます。
public class User {
private final String name; // 必須
private final String email; // 必須
private final int age; // 任意
private final String phone; // 任意
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}Builder のメソッドは this を返すため、メソッドチェーンが可能になります。これは、オブジェクトを構築するための流れるようで読みやすい方法です。
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();User constructor が private であることに注目してください。オブジェクトは Builder を通してのみ作成できます。このパターンは、オブジェクトに多数の optional フィールドがある場合に特に効果を発揮し、長い parameter リストを使うよりもコードが読みやすく、エラーが発生しにくくなります。
チャレンジ
簡単Builder Patternを使用してピザ注文システムを構築しましょう!すべてのトッピングやオプションに対応する数十個のパラメータを受け取るconstructorを扱う代わりに、ピザのカスタマイズをすっきりと読みやすくするfluent builderを作成します。
コードを2つのファイルに分けて整理します。
Pizza.java: Builder Patternを使用して構築するPizzaclassを作成します。Pizzaには次のfieldsを持たせます。size(String) - 必須。ピザのサイズを表しますcheese(boolean) - optional。デフォルトはfalseですpepperoni(boolean) - optional。デフォルトはfalseですmushrooms(boolean) - optional。デフォルトはfalseですPizzaconstructorはprivateにし、parameterとしてPizzaBuilderのみを受け取り、builderのすべての値をPizzaのfieldsにコピーします。getDescription()methodを含め、次の形式のstringを返すようにします:[size] Pizza with: [toppings]。ここでtoppingsは、trueになっているトッピング(Cheese、Pepperoni、Mushrooms)をカンマ区切りで並べたlistです。トッピングが選択されていない場合、toppingsの部分はno toppingsとだけ表示します。次の内容を持つstaticなinner class
PizzaBuilderを作成します。- 必須の
sizeparameterを受け取るconstructor - それぞれ対応するbooleanをtrueに設定し、chainingのために
thisを返すcheese()、pepperoni()、mushrooms()methods - 新しい
Pizzaを返すbuild()method
- 必須の
Main.java: Pizza builderを実際に動作させてみましょう!4つのinputを受け取ります。ピザのサイズ(String)と、cheese、pepperoni、mushroomsをそれぞれ追加するかどうかを示す3つのyes/no値(Strings)です。指定されたsizeで
PizzaBuilderを作成します。次に、それぞれのyes/no inputに基づいて、適切なトッピングmethodsをchainします。inputが"yes"の場合はそのトッピングmethodを呼び出し、"no"の場合はスキップします。最後にbuild()を呼び出してPizzaを作成します。完成したPizzaに対して
getDescription()を呼び出した結果を出力します。
inputは次の順番で4つ受け取ります: size (String)、cheeseを追加 (String: "yes" または "no")、pepperoniを追加 (String: "yes" または "no")、mushroomsを追加 (String: "yes" または "no")。
たとえば、inputがLarge、yes、no、yesの場合、出力は次のようになります: Large Pizza with: Cheese, Mushrooms
Builder Patternを使うと、可能性ごとに別々のconstructorを用意する必要なく、トッピングを自由に組み合わせてPizzaを構築できることに注目してください!
自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 入力を読み取る
String size = scanner.nextLine();
String addCheese = scanner.nextLine();
String addPepperoni = scanner.nextLine();
String addMushrooms = scanner.nextLine();
// TODO: 指定されたサイズでPizzaBuilderを作成する
// TODO: yes/noの入力に基づいてトッピングメソッドをチェーンする
// TODO: ピザをビルドしてその説明を出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造自分で練習してみよう: Javaオンラインコンパイラ