dasukoの技術ブログ

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

Abstract Factoryパターン

Abstract Factoryパターンとは

プログラミングのデザインパターンの一つで、関連する(または依存する)オブジェクト群を具体的なクラスを指定せずに、まとめて生成する方法を提供するパターンです。

日本語に直訳すると「抽象的工場」となります。

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

[GoFの書籍はこちら]

これらのオブジェクトは共通のインターフェースを実装するため、使用する側はどのクラスのオブジェクトを使用するかは気にしません。

また、オブジェクトの作成はファクトリーインターフェースで公開されているメソッドで行われるので、クライアントから分離することができます。

使用例

今回はゲームのプレイヤーにランサーや剣士という職業があるとして、装備を取得する処理をAbstract Factoryパターンを使って実装します。

各装備(剣、ランス)のオブジェクトの作成は各Factoryクラスで実装します。

剣士の装備を取得したい場合はSwordsmanEquipmentFactoryから、ランサーの装備を取得したい場合はLancerEquipmentFactoryから取得します。

※パラメータとかは適当なので気にしないでください

Class Diagram

f:id:dasuko:20200503195501p:plain
Class Diagram

C#での実装例

// 装備のインターフェース
public interface IEquipment
{
    Parameter Parameter { get; }
}

// 装備のパラメータ
public struct Parameter
{
    public float attackBuff;
    public float defenseBuff;
}

// 装備のタイプ(今回は武器だけ)
public enum EquipmentType
{
    Weapon
}

// 装備を取得するFactoryのインターフェース
public interface IEquipmentFactory
{
    IEquipment CreateEquipment(EquipmentType equipmentType);
}

// 剣士の装備を取得するFactory
public class SwordsmanEquipmentFactory : IEquipmentFactory
{
    public IEquipment CreateEquipment(EquipmentType equipmentType)
    {
        switch (equipmentType)
        {
            case EquipmentType.Weapon:
                return new Sword();
            default:
                return null;
        }
    }
}

// 剣
public class Sword : IEquipment
{
    public Parameter Parameter
    {
        get
        {
            return new Parameter
            {
                attackBuff = 10f,
                defenseBuff = 1f
            };
        }
    }
}

// ランサーの装備を取得するFactory
public class LancerEquipmentFactory : IEquipmentFactory
{
    public IEquipment CreateEquipment(EquipmentType equipmentType)
    {
        switch (equipmentType)
        {
            case EquipmentType.Weapon:
                return new Lance();
            default:
                return null;
        }
    }
}

// ランス
public class Lance : IEquipment
{
    public Parameter Parameter
    {
        get
        {
            return new Parameter
            {
                attackBuff = 8f,
                defenseBuff = 2f
            };
        }
    }
}

javaでの実装例

// 装備のインターフェース
public interface Equipment {
    Parameter getParameter();
}

// 装備のパラメータ
public class Parameter {
    private float attackBuff;
    private float defenseBuff;

    public Parameter(float attackBuff, float defenseBuff) {
        this.attackBuff = attackBuff;
        this.defenseBuff = defenseBuff;
    }

    public float getAttackBuff() {
        return attackBuff;
    }

    public float getDefenseBuff() {
        return defenseBuff;
    }
}

// 装備のタイプ(今回は武器だけ)
public enum  EquipmentType {
    WEAPON
}

// 装備を取得するFactoryのインターフェース
public interface EquipmentFactory {
    Equipment createEquipment(EquipmentType equipmentType);
}

// 剣士の装備を取得するFactory
public class SwordsmanEquipmentFactory implements EquipmentFactory {
    @Override
    public Equipment createEquipment(EquipmentType equipmentType) {
        switch (equipmentType) {
            case WEAPON:
                return new Sword();
            default:
                return null;
        }
    }
}

// 剣
public class Sword implements Equipment {
    @Override
    public Parameter getParameter() {
        return new Parameter(10f, 1f);
    }
}

// ランサーの装備を取得するFactory
public class LancerEquipmentFactory implements EquipmentFactory {
    @Override
    public Equipment createEquipment(EquipmentType equipmentType) {
        switch (equipmentType) {
            case WEAPON:
                return new Lance();
            default:
                return null;
        }
    }
}

// ランス
public class Lance implements Equipment {
    @Override
    public Parameter getParameter() {
        return new Parameter(8f, 2f);
    }
}

まとめ

クライアントはFactoryのオブジェクト生成ロジックに依存しません。 例えば、モバイル向けアプリの開発をしていて、OS毎に振る舞いが違う場合に iOS用のFactory、Android用のFactoryを用意するのもいいかなと思います。

参考

Abstract Factory パターン - Wikipedia