TL;DR

複雑なオブジェクトの組み立てを、ビルダと呼ぶ別のオブジェクトに任せる生成のデザインパターン。GoF の『Design Patterns』で紹介された 23 のパターンの 1 つである。

なぜ必要か

Wikipedia によると、複雑なオブジェクトをクラスの中で直接組み立てると、クラスが特定の表現に縛られ、表現を変えるたびにクラスを書き換えることになる。ビルダパターンでは構築の手順と表現を分けるので、同じ手順から異なる表現を作れる。

Rust では事情が少し違う。Effective Rust 項目7 によると、Rust の構造体は作るときにすべてのフィールドを指定する必要があり、省略したいフィールドにも None を書かなければならない。フィールドを足すと、構造体を作っている箇所をすべて直すことにもなる。

仕組み

GoF の形では、製品を作る手順を Builder のインタフェースが定め、ConcreteBuilder がそれを実装する。Director は Builder のインタフェースだけを使って、手順を呼ぶ順番を決める。

Rust では、必須のフィールドを new() で受け取り、任意のフィールドをメソッドで設定し、最後に build() で完成品を返す形が多い。

例

Effective Rust 項目7 の例である。

let also_bob = DetailsBuilder::new("Robert", "Builder", dob)
    .middle_name("the")
    .preferred_name("Bob")
    .just_seen()
    .build();

注意点

Rust のビルダで self を値で受け取るとメソッドチェーンは書きやすいが、build() がビルダを消費するので、1 つのビルダから 1 つしか作れない。&mut self を受け取るとビルダを再利用できるが、コンストラクタから直接チェーンすると一時値の寿命のエラーになる。また、定型のコードはビルダの側に移るだけでなくならない。

関連

出典