Builderビルダ-デザインパターンの勉強

Builderビルダ。
英語のBuildingが語源なのかな。
複雑なインスタンスを組み立てるときに使うデザインパターンです。

Javaデザインパターン徹底攻略 (標準プログラマーズライブラリ)の説明が非常に分かりやすかったので、それ沿って、自分なりにアレンジを加えながら説明していこうと思います。

背景

フィールドをたくさん持ったクラスってコンストラクタが複雑でメンテナンス大変じゃない?
例えば、フィールド値int,String,doubleを持ったクラスHogeがあるとします。
コンストラクタは以下のようにとても面倒です。

class Hoge {
  Hoge(int i) { ... }
  Hoge(String str) { ... }
  Hoge(double d) { ... }
  Hoge(int i ,String str) { ... }
  Hoge(int i ,double d) { ... }
  Hoge(String str ,double d) { ... }
  Hoge(int i ,String str ,double d) { ... }


よって、コンストラクタでフィールド値を初期化するのではなく、メソッドにより値をセットすることを考えます。

class Hoge {
  Hoge() {
  }
  void setInt(int i) { ... }
  void setString(String str) { ... }
  void setDouble(double d) { ... }

しかし、この方法にも良くない所があります。
以下のように呼び出し側で同じメソッドを呼んでしまったり、メソッドを呼ぶのを忘れてしまう可能性があります。

class Test {
  setInt(1);
  setString("Hey");
  setInt(10);          //2度目の呼び出し。  setDouble(double d)は呼び出されてない。

どうしましょう。

Builderビルダの登場

このパターンの構成は以下のようになっています。

クラス 説明
Builder(ビルダ) コンストラクタを分割したメソッドを持つクラス
Director(ディレクタ) Builder(ビルダ)クラスのメソッドを順番に呼び出すクラス


Javaデザインパターン徹底攻略 (標準プログラマーズライブラリ)に載っていたサンプルコードを少し改変して載せておきます。

クラス 説明
MyBuilder.java Builder(ビルダ)コンストラクタを分割したメソッドを持つクラス
Director.java Director(ディレクタ)インターフェース
MyDirectorA.java Directorの実装。すべての名前を要求する。
MyDirectorB.java Directorの実装。ミドルネームを要求しない。
MyClass.java オブジェクト
MyMain.java テスト用。
MyBuilder.java
  1 class MyBuilder {
  2   String name;
  3   void addFirstName() {
  4     name = "Tarou ";
  5   } 
  6   void addMiddleName() {
  7     name += "Steve ";
  8   }
  9   void addLastName() {
 10     name += "Tanaka";
 11   }
 12   MyClass getMyClass() {
 13     MyClass result = new MyClass(name);
 14     name = "";
 15     return result;
 16   }
 17 
 18 }
Director.java
  1 interface Director {
  2   MyClass createMyClass(MyBuilder builder);
  3 }
MyDirectorA.java
  1 class MyDirectorA implements Director {
  2   public MyClass createMyClass(MyBuilder builder) {
  3     builder.addFirstName();
  4     builder.addMiddleName();
  5     builder.addLastName();
  6     return builder.getMyClass();
  7   } 
  8 } 
MyDirectorB.java
  1 class MyDirectorB implements Director {
  2   public MyClass createMyClass(MyBuilder builder) {
  3     builder.addFirstName();
  4     builder.addLastName();
  5     return builder.getMyClass();
  6   } 
  7 } 
MyClass.java
  1 class MyClass {
  2   private String name;
  3 
  4   MyClass(String name) {
  5     this.name = name;
  6   }
  7 
  8   String getName() {
  9     return name;
 10   }
 11
 12 }
MyMain.java
  1 class MyMain {
  2   public static void main(String[] args) {
  3     MyBuilder builder = new MyBuilder();
  4     
  5     Director dirA = new MyDirectorA();
  6     MyClass myclass1 = dirA.createMyClass(builder);
  7     System.out.println(myclass1.getName());
  8 
  9     Director dirB = new MyDirectorB();
 10     MyClass myclass2 = dirB.createMyClass(builder);
 11     System.out.println(myclass2.getName());
 12 
 13   }
 14 }

実行してみる。

$ java MyMain
Tarou Steve Tanaka
Tarou Tanaka

雑感

  • 今は利用方法が浮かばないけど、覚えておきたい。