😽

デザインパターンを学ぶ #14 ビルダー(Builder)

に公開

1. はじめに

今回は Builder(ビルダー)パターン
目的は、複雑なオブジェクトの生成過程を分離して、同じ手順で異なる表現を作れるようにすることです。

典型的な用途は、構築手順は同じでも構成要素が違うオブジェクトを作りたいときや、
長いコンストラクタの代わりに分かりやすい段階的な構築をしたいときです。

2. Builderとは?

ビルダーパターンは、オブジェクトの生成手順(Director)と構築処理(Builder)を分離する仕組みです。

主な登場役は以下の4つです:

  • Product … 完成するオブジェクト
  • Builder … 部品を作る抽象インターフェース
  • ConcreteBuilder … 実際に部品を組み立てるクラス
  • Director … 構築手順(レシピ)を定義するクラス

同じ構築手順でもBuilderを差し替えるだけで、違う構成のオブジェクトを作れるのが特徴です。

3. 実装イメージ(PHP)

<?php
// Product(完成品)
class Burger {
    private array $ingredients = [];
    public function add(string $part): void {
        $this->ingredients[] = $part;
    }
    public function describe(): string {
        return implode(', ', $this->ingredients);
    }
}

// Builder(抽象ビルダー)
interface BurgerBuilder {
    public function addBun(): void;
    public function addPatty(): void;
    public function addSauce(): void;
    public function getResult(): Burger;
}

// ConcreteBuilder(具体ビルダー)
class CheeseBurgerBuilder implements BurgerBuilder {
    private Burger $burger;
    public function __construct() {
        $this->burger = new Burger();
    }
    public function addBun(): void    { $this->burger->add("Bun"); }
    public function addPatty(): void  { $this->burger->add("Beef Patty"); }
    public function addSauce(): void  { $this->burger->add("Cheese Sauce"); }
    public function getResult(): Burger { return $this->burger; }
}

// Director(監督役)
class Chef {
    public function make(BurgerBuilder $builder): Burger {
        $builder->addBun();
        $builder->addPatty();
        $builder->addSauce();
        return $builder->getResult();
    }
}

// --- 実行例 ---
$chef = new Chef();
$builder = new CheeseBurgerBuilder();

$burger = $chef->make($builder);
echo $burger->describe(); // Bun, Beef Patty, Cheese Sauce

4. メリット・デメリット

メリット

  • 生成処理と部品構築を分離できる
  • 同じ手順で異なる製品を作り分けられる
  • 複雑な初期化処理でもコードが読みやすい

デメリット

  • クラス数が増える(Builder/Director/Product など)
  • 単純な生成にはオーバーエンジニアリングになりやすい

5. 使いどころ

  • 複雑なオブジェクトを段階的に構築したいとき
  • 同じ構築手順で異なるバリエーションを作りたいとき
  • コンストラクタが長大になるケースを避けたいとき

作る手順を使い回したい」と思ったら、ビルダーパターンの出番です。

Discussion