デザインパターン入門 | Builder(ビルダー)パターン
「Builder(ビルダー)」パターンは、インスタンスを構築する際に「構造」を持つことができるデザインパターンです。
といっても、特別な方法を用いるわけではなく、「委譲」「継承」など、普段利用しているテクニックを活用して作ることができるパターンとなっています。
今回は「家」「施設」「工場」を建てる過程を事例として「Builder(ビルダー)」パターンの使い方をご説明していきたいと思います。
「Builder(ビルダー)」パターンに登場する「クラス・インターフェース」
今回ご説明する「Builder(ビルダー)」パターンでは、下図のようにクラスを作成していきます。
まず、一番上にある「Builder」インターフェースですが、下記のような内容となっています。(プログラム言語:Java)
public interface Builder { public abstract void createFoundation(); //基礎を作る public abstract void createRoof(); //屋根を作る public abstract void createExterior(); //外装を作る public abstract void createInterior(); //内装を作る }
このインターフェースには、建物を作成するために必要な「抽象メソッド」が定義されています。
この抽象メソッドにより「処理の構造」が決定されていますので、「Builder(ビルダー)」パターンでは重要な役割を担っています。
この抽象メソッドを実装しているのは、家を作るための「HomeBuilder」クラス、施設を作るための「FacilityBuilder」クラス、工場を作るための「FactoryBuilder」クラスになります。
「HomeBuilder」クラスは下記のようになります。
public class HomeBuilder extends Building implements Builder{ boolean floorHeater; //フロアヒーターの設置要否 public HomeBuilder(String name, boolean floorHeater) { this.name = name; this.floorHeater = floorHeater; } // 基礎を作る @Override public void createFoundation() { System.out.println("家の基礎を作成しました。"); } // 屋根を作る @Override public void createRoof() { System.out.println("家の屋根を作成しました。"); } // 外装を作る @Override public void createExterior() { System.out.println("家の外装を作成しました。"); } // 内装を作る @Override public void createInterior() { System.out.println("家の内装を作成しました。"); if (floorHeater) { System.out.println("家の床にフロアヒーターを設置しました。"); } } }
「Building」というクラスを継承していますが、これは「HomeBuilder」クラス、「FactoryBuilder」クラス、「FacilityBuilder」クラスの実装内容が定まっているメソッドを定義しているクラスです。
ちなみにこのクラスは下記のようになっています。
public class Building { String name; // 建物のオプションの要否を確認 public static boolean askNeedOption(String name) { boolean loopFlg = true; boolean result = false; while (loopFlg) { System.out.println("■ " + name + "を設置しますか?"); System.out.println("0:はい 1:いいえ "); int choice = new java.util.Scanner(System.in).nextInt(); if ( choice == 0 ) { result = true; loopFlg = false; } else if ( choice == 1 ) { loopFlg = false; } else { System.out.println("数値は0~2のいずれかを入力してください。"); } } return result; } }
建物の名前を保存する「name」フィールドと「建物のオプションの要否を確認」を行う「askNeedOption」メソッドが定義されていますが、これは後ほど建物を構築する処理で利用していきます。
といっても、このクラスは「Builder(ビルダー)」パターンには直接関連するクラスではありませんので、あまり気に掛ける必要はありません。(例外処理なども割愛しています。)
次に、「FacilityBuilder」クラスは下記のようになります。
public class FacilityBuilder extends Building implements Builder { public FacilityBuilder(String name) { this.name = name; } // 基礎を作る @Override public void createFoundation() { System.out.println("施設の基礎杭を作成しました。"); System.out.println("施設の基礎を作成しました。"); } // 屋根を作る @Override public void createRoof() { System.out.println("施設の屋根を作成しました。"); } // 外装を作る @Override public void createExterior() { System.out.println("施設の外装を作成しました。"); } // 内装を作る @Override public void createInterior() { System.out.println("施設の内装を作成しました。"); } }
そして、「FactoryBuilder」クラスは下記のようになります。
public class FactoryBuilder extends Building implements Builder { boolean solarPanel; public FactoryBuilder(String name, boolean setSolarPanel) { this.name = name; this.solarPanel = setSolarPanel; } // 基礎を作る @Override public void createFoundation() { System.out.println("工場の基礎杭を作成しました。"); System.out.println("工場の基礎を作成しました。"); } // 屋根を作る @Override public void createRoof() { System.out.println("工場の屋根を作成しました。"); if (solarPanel) { System.out.println("工場の屋根にソーラーパネルを設置しました。"); } } // 外装を作る @Override public void createExterior() { System.out.println("工場の外装を作成しました。"); } // 内装を作る @Override public void createInterior() { System.out.println("工場の内装を作成しました。"); } }
これらのクラスを使うのは「Director」クラスとなります。
「Director」クラスは、「Builder」クラス型のフィールドを持っていますので、「委譲」の関係を作っています。
「Director」クラスは、下記のようになります。
public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } // 建物を建築(構築)するメソッド public void construct() { builder.createFoundation(); builder.createExterior(); builder.createInterior(); builder.createRoof(); } }
「construct」メソッドは、建物を建築するためのメソッドですが、その内容は、「Builder」インターフェースで定義されている抽象メソッドを実行しているだけです。
これで必要な「クラス・インターフェース」が揃いました。
「Builder(ビルダー)」パターンのプログラムの実行
「main」メソッドを含むクラスは下記のようになります。
public class Main { public static void main(String[] args) { Builder bl = null; Director dr; boolean loopFlg = true; int choice = -1; String name = ""; while (loopFlg) { try { System.out.println("■作成する建物の種類を数値で入力してください。"); System.out.println("0:home 1:Facility 2:Factory"); choice = new java.util.Scanner(System.in).nextInt(); if (0 <= choice && choice <= 2) { loopFlg = false; } else { displayInputErrMsg(); } } catch (InputMismatchException e) { displayInputErrMsg(); } catch ( Exception e) { displayInputErrMsg(); } } loopFlg = true; while (loopFlg) { System.out.println("■建物の名前を入力してください。"); name = new java.util.Scanner(System.in).nextLine(); if (name.length() != 0) { loopFlg = false; } } switch (choice) { case 0: boolean homeOption = Building.askNeedOption("フロアヒーター"); bl = new HomeBuilder(name, homeOption); loopFlg = false; break; case 1: bl = new FacilityBuilder(name); loopFlg = false; break; case 2: boolean FactoryOption = Building.askNeedOption("ソーラーパネル"); bl = new FactoryBuilder(name, FactoryOption); loopFlg = false; break; default: displayInputErrMsg(); } System.out.println(""); System.out.println("--- 【作成する建物名「" + name + "」】 ---"); dr = new Director(bl); dr.construct(); } private static String displayInputErrMsg() { return "数値は0~2のいずれかを入力してください。"; } }
「Director」クラスのコンストラクタに「HomeBuilder」「FacilityBuilder」「FactoryBuilder」のいずれかのインスタンスをコンストラクタに渡し、「Director」クラスの「construct」メソッドを実行していますね。
まず、どの建物を建築するのかを選択し、建物の名前を入力して、建物によってはオプションの要否も選択していきます。
「HomeBuilder」クラスを利用した場合は、下記のような実行結果になります。
■作成する建物の種類を数値で入力してください。 0:home 1:Facility 2:Factory 0 ■建物の名前を入力してください。 サンプルハイツ ■ フロアヒーターを設置しますか? 0:はい 1:いいえ 0 --- 【作成する建物名「サンプルハイツ」】 --- 家の基礎を作成しました。 家の外装を作成しました。 家の内装を作成しました。 家の床にフロアヒーターを設置しました。 家の屋根を作成しました。
のような出力結果になります。
建築物を作るように、下から順番に複雑なものを作りあげる際に活用できるのが「Builder(ビルダー)」パターンです。
「counstruct」メソッドの内容を増やすことでさらに複雑なインスタンスを作ることもできます。
「Builder(ビルダー)」パターンがほかに利用できるものがないかいろいろと考えてみるのもよいかもしれません。