dasukoの技術ブログ

現役エンジニアのブログです。

Builderパターン

Builderパターンとは

プログラミングのデザインパターンの一つで、オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にするデザインパターンです。

オブジェクト指向における再利用のためのデザインパターン」の著者、GoF(Gang of four)によって定義されたデザインパターンみたいです。

[GoFの書籍はこちら]

クラス内で複雑なオブジェクトを構築するロジックを書くのは柔軟性がありません。

Builderパターンでは、オブジェクトを作成するための値や振る舞いを設定し、作成されたオブジェクトでは セッターを公開しなければ、値や振る舞いを設定できなくすることもできます。 オブジェクトの構築部分をカプセル化できます。

要するに状況によって異なる複数の値を渡すときに、コンストラクタで渡すにしては数が多い場合や複雑になりすぎる場合などに便利です。

使用例

今回はゲームのキャラクターを作成する処理にBuilderパターンを使ってみようと思います。

今回の例ではBuildメソッドでCharacterオブジェクトを取得していますが、 GetResult()というメソッド名にするのが一般的かもしれないです。

/// <summary>
/// Character
/// </summary>
public class Character
{
    public string Name { get; }
    public bool IsEnemy { get; }
    public float HP { get; }

    public Character(string name, bool isEnemy, float hp)
    {
        Name = name;
        IsEnemy = isEnemy;
        HP = hp;
    }
}

/// <summary>
/// Character Builder interface
/// </summary>
public interface ICharacterBuilder
{
    float HP { get; set; }
    Character Build();
}

/// <summary>
/// Slime Builder class
/// </summary>
public class SlimeBuilder : ICharacterBuilder
{
    public float HP { get; set; }

    public Character Build()
    {
        return new Character("Slime", true, HP);
    }
}

/// <summary>
/// Director
/// </summary>
public class EnemyDirector
{
    private ICharacterBuilder builder;
    public EnemyDirector(ICharacterBuilder builder)
    {
        this.builder = builder;
    }

    public void Construct()
    {
        builder.HP = 100f;
    }
}

public class Client
{
    public void CreateSlime()
    {
        var builder = new SlimeBuilder();
        var director = new EnemyDirector(builder);

        director.Construct();
        var slime = builder.Build();
    }
}

ちなみにEffective Javaでは以下のようにインナークラスとしてビルダーを定義する形が推奨されてたりします。

Android開発をしているとこの形式のBuilderパターンをよく見ます。

AlertDialogなんかにもこのBuilderパターンが使われています。

// Character class
public class Character {
    private String name;
    private boolean isEnemy;
    private float hp;

    // Builder
    public static class Builder {
        private String name;
        private boolean isEnemy;
        private float hp;

        public Builder(String name) {
            this.name = name;
        }

        public Builder setIsEnemy(boolean isEnemy) {
            this.isEnemy = isEnemy;
            return this;
        }

        public Builder setHP(float hp) {
            this.hp = hp;
            return this;
        }

        public Character build() {
            Character character = new Character();
            character.name = name;
            character.isEnemy = isEnemy;
            character.hp = hp;

            return character;
        }
    }
}

// Client
public class Client {
    public void CreateCharacter() {
        Character character = new Character.Builder("Slime")
                .setIsEnemy(true)
                .setHP(100)
                .build();
    }
}

まとめ

Builderパターンはオブジェクトの生成手順が複雑な場合に、その生成ロジックを切り出したい場合などに有効です。

生成ロジックを切り出すことにより、新しいオブジェクトの追加(例で言うと新しいキャラクターの追加)が容易になります。

また、コンストラクタの引数を長くすると、どの値が何かというのがわかりづらくなります。

Builderパターンを使うことにより、どのパラメータにどの値を設定しているかがわかりやすくなります。

参考

Builder パターン - Wikipedia