Builder

パターンの目的

p.105,line.3-4: 複合オブジェクトについて、その作成過程を表現形式に依存しないものにすることにより、同じ作成過程で異なる表現形式のオブジェクトを生成できるようにする。

GoF パターンのクラス図

structure-08-05:builder

MixJuice 版 Builder (改善)

解決される GoF パターンの問題点

拡張性の問題点

p.108,line.35 - p.109,line1-3: 1.生成と組み立てのインターフェイス。ConcreteBuilderクラスは、Product オブジェクトを段階的に作成する。ゆえに、Builderクラスのインターフェイスは、あらゆる種類のConcreteBuilderクラスでのProductオブジェクトの作成に適応できるように、十分に一般的でなければならない。

補足:設計段階で予期していなかった種類の Product オブジェクトの作成が必要になった時、 Builder クラスが提供されているインタフェースでは不十分な場合がある。

対策

Builder クラスに新たなメソッド(例えば buildPartC )を追加し、 Director から呼べるようにする。このとき、追加したメソッドを abstract method にする方法と、中身が空のメソッドにする方法がある。前者の場合、既存のすべての ConcreteBuilder に buildPartC の実装を追加するための補完モジュールが必要になる。

結果

Builder クラスであらかじめ定義した生成のためのインターフェースが不十分であった場合にでも、あとからインターフェースを拡張できる。

構造

structure-08-09:builder1

module original {
 define abstract class Builder {
   define void buildPartA(){}
   define void buildPartB(){}
 }
 define class ConcreteBuilder extends Builder {
   void buildPartA(){...}
   void buildPartB(){...}
 }
 define class Director {
   Builder builder;
   define void construct(){
     builder.buildPartA();
     builder.buildPartB();
   }
 }
}
module extension extends original {
 class Builder {
   define void buildPartC(){}
 }
 define class ConcreteBuilder2 extends Builder {
   void buildPartA(){...}
   void buildPartB(){...}
   void buildPartC(){...}
 }
 class Director {
   void construct(){
     original();
     builder.buildPartC();
   }
 }
}

サンプルコード

コメントを無視するパーザから、コメントノードを生成するパーザへの拡張。

MixJuice 版 Builder (別解)

解決される GoF パターンの問題点

クラス数増加の問題点

対策

GoF 本では RTF からさまざまな形式への変換する TextConverter が例として述べられている。この例では、 RTF の読み込み部を共通とし、書き出し部を可変にするために Builder パターンが使われている。

TextConverter の例において、各書き出し形式毎に rtf2ascii, rtf2tex, rtfview といった複数のバイナリを生成する場合には、個々のコマンド毎に静的に書き出し部が決定する。この場合、MixJuice では書き出し部分を RTFReader への差分として各種の実装を行なえ、出力部分をクラスとする必要がなくなり、クラス階層の単純化が可能である。

結果

MixJuice では ConcreteBuilder, Builder を Director をまとめてひとつのクラスにしても、同じ生成過程で異なる表現形式のオブジェクトを生成できるようにするというパターンの目的は達成できる。

なお、ひとつのバイナリ内で出力形式を切替えたい時には複数の書き出し部をバイナリ内に共存させなければならないため、このように差分を使用することはできない。

構造

structure-08-09:builder2

module builder {
  define class Builder {
    // Director の機能も持っている
    define void construct() { ... }

    define void buildPartA() {}
    define void buildPartB() {}
    define void buildPartC() {}
  }
}

module builder.implementation1 extends builder {
  class Builder {
    void buildPartA() { ... }
    void buildPartB() { ... }
    void buildPartC() { ... }
  }
}

module builder.implementation2 extends builder {
  class Builder {
    void buildPartA() { ... }
    void buildPartB() { ... }
    void buildPartC() { ... }
  }
}

サンプルコード

structure-08-09:builder_sample

module rtf {
  define class RTFReader {
    define void parseRTF() { ... }

    define void convertCharacter(char c) {}
    define void convertFontChange(Font f) {}
    define void convertParagraph() {}
  }
}

module rtf.ascii extends rtf {
  class RTFReader {
    void convertCharacter(char c) { ... }
    define ASCIIText getASCIIText() { ... }
  }
}

module rtf.tex extends rtf {
  class RTFReader {
    void convertCharacter(char c) { ... }
    void convertFontChange(Font f) { ... }
    void convertParagraph() { ... }
    define TeXText getTeXText() { ... }
  }
}

module rtf.textwidget extends rtf {
  class RTFReader {
    void convertCharacter(char c) { ... }
    void convertFontChange(Font f) { ... }
    void convertParagraph() { ... }
    define TextWidget getTextWidget() { ... }
  }
}

MixJuice によるデザインパターン改善カタログ


田中 哲 <akr@m17n.org>, 一杉 裕志 <y-ichisugi@aist.go.jp>