【デザインパターン】Builderを学ぶ【C#】

C#

一言で言うと…

  • 複雑なオブジェクトの生成手順を分離し、柔軟に組み立てられる仕組み

概要

Builder パターンは、GoF デザインパターンの一つで、生成に関するデザインパターン です。
別名 Virtual Constructor とも呼ばれます。

大量のプロパティやステップを持つオブジェクトの生成過程を分離し、
同じ手順で異なるバリエーションを構築できるようにするのが目的です。

構成

classDiagram
   direction BT

   class Product
   class IBuilder {
      << interface >>
      + reset()
      + buildStepA()
      + buildStepB()
   }
   class ConcreteBuilder {
      - result: Product
      + reset()
      + buildStepA()
      + buildStepB()
      + getResult() Product
   }

   class Director {
      - builder: IBuilder
      + Director(builder)
      + construct()
      + make(type)
   }

   ConcreteBuilder --> Product
   ConcreteBuilder ..|> IBuilder: impements
   Director o-- IBuilder
   Client ..> Director: << use >>

実装例(C#)

クラス概要

早速実装例です。今回はC#でゲーム開発を行う場合を想定してみました。

利用ケースは以下のように、異なるジョブのプレイヤーキャラを生成する機構です。

  • Player:構築対象 (Product)
  • IPlayerBuilder:ビルダーインターフェース (IBuilder)
  • WarriorBuilder / MageBuilder:具象ビルダー (Concrete Builder)
  • PlayerDirector:構築手順指示 (Director)
  • クライアント:生成結果を利用する

ソースコード

1. 構築対象 (Product)

C#
using System;
using System.Collections.Generic;

/// <summary>
/// プレイヤー (Product)
/// </summary>
public class Player
{
    public string Name { get; set; }
    public string Job { get; set; }
    public List<string> Skills { get; } = new List<string>();

    public void ShowInfo()
    {
        Console.WriteLine($"キャラクター名: {Name}");
        Console.WriteLine($"職業: {Job}");
        Console.WriteLine("スキル: " + string.Join(", ", Skills));
    }
}

これは構築対象となるプレイヤークラスです。Builder パターンの Product に位置します。

2. ビルダーインターフェース (IBuilder)

C#
/// <summary>
/// ビルダーインターフェース (IBuilder)
/// </summary>
public interface IPlayerBuilder
{
    void Reset();
    void BuildName();
    void BuildJob();
    void BuildSkills();
    Player GetResult();
}

これはプロダクト生成を行うビルダーのインタフェースです。Bulder パターンの IBuilder に位置します。

3. 具象ビルダー (Concrete Builder)

C#
/// <summary>
/// 戦士ビルダー (Concrete Builder)
/// </summary>
public class WarriorBuilder : IPlayerBuilder
{
    private Player _player = new Player();

    public void Reset() => _player = new Player();

    public void BuildName() => _player.Name = "勇敢な戦士";
    public void BuildJob() => _player.Job = "戦士";
    public void BuildSkills() => _player.Skills.AddRange(new[] { "剣技", "防御" });

    public Player GetResult() => _player;
}

/// <summary>
/// 魔法使いビルダー (Concrete Builder)
/// </summary>
public class MageBuilder : IPlayerBuilder
{
    private Player _player = new Player();

    public void Reset() => _player = new Player();

    public void BuildName() => _player.Name = "賢き魔法使い";
    public void BuildJob() => _player.Job = "魔法使い";
    public void BuildSkills() => _player.Skills.AddRange(new[] { "ファイアボール", "ヒール" });

    public Player GetResult() => _player;
}

これらのクラスはプロダクトの構築を行うビルダーの具象クラスです。Builder パターンの Concrete Builder に位置します。

今回は戦士と魔法使いの2種類のビルダーを用意しました。

4. 構築手順指示 (Director)

C#
/// <summary>
/// 構築手順指示 (Director)
/// </summary>
public class PlayerDirector
{
    private readonly IPlayerBuilder _builder;

    public PlayerDirector(IPlayerBuilder builder)
    {
        _builder = builder;
    }

    public Player Construct()
    {
        _builder.Reset();
        _builder.BuildName();
        _builder.BuildJob();
        _builder.BuildSkills();
        return _builder.GetResult();
    }
}

これはプロダクトの構築手順を指示するディレクタークラスです。Builder パターンの Director に位置します。

ディレクターはビルダーのインタフェースの参照を持ち、プロダクトの生成処理はビルダーに委譲します。(ディレクターは指示するだけ)

5. クライアントコード

C#
class BuilderSample
{
    static void Main()
    {
        // 戦士を生成
        var warriorDirector = new PlayerDirector(new WarriorBuilder());
        Player warrior = warriorDirector.Construct();
        warrior.ShowInfo();
        // => キャラクター名: 勇敢な戦士
        // => 職業: 戦士
        // => スキル: 剣技, 防御

        Console.WriteLine();

        // 魔法使いを生成
        var mageDirector = new PlayerDirector(new MageBuilder());
        Player mage = mageDirector.Construct();
        mage.ShowInfo();
        // => キャラクター名: 賢き魔法使い
        // => 職業: 魔法使い
        // => スキル: ファイアボール, ヒール
    }
}

これはクライアントコードの例です。各プレイヤーを生成する場面を想定しています。

クライアント側はディレクターに各ジョブのビルダーを渡すことで、どのジョブを生成するかを決めることができます。

ビルダーは抽象化されているため、新たにジョブを追加したい時は新たにビルダーを作成し、ディレクターに渡すことで簡単に拡張可能です。

使いどころ

  • 大量のプロパティを持つクラスの生成処理を整理したいとき
  • クラスごとの構築バリエーションを柔軟に切り替えたいとき
  • 共通の構築手順を持ちながら、具体的な内容を差し替えたいとき

長所・短所

✅ 長所

  • 段階的なオブジェクト生成や、構築手順の再利用が可能
  • 複雑な構築処理を分離でき、単一責任の原則に従える
  • 同じ Director で異なるビルダーを使うだけで、別バリエーションを簡単に作れる

⚠️ 短所

  • ビルダーやディレクターなど、クラス数が増える
  • 小規模なオブジェクト生成では 過剰設計 になる可能性がある

まとめ

Builder パターンは、ゲーム開発における キャラクターやステージ構築 のように
「同じ構築手順だけど、バリエーションがたくさんある」ケースに特に有効です。

大量のプロパティや複雑な生成処理を整理できるので、
ゲーム開発の柔軟性と拡張性を高めるためにぜひ活用してください!

タイトルとURLをコピーしました