🎨

【Unity】デザインパターン Factory

2024/11/24に公開

Unityで開発をしていると、GameManagerのようなクラスに、
あらゆる処理を詰め込んでしまうことがよくあります。
しかし、これではコードが複雑になり、拡張性やメンテナンス性が低下してしまいます。

コードを整理するための「Factoryパターン」について調べたので、
内容をまとめてみました。

悪い例:直接オブジェクト生成をするコード

public class GameManager : MonoBehaviour
{
    [SerializeField] private GameObject enemyPrefab;
    [SerializeField] private GameObject itemPrefab;

  void Start()
   {
        SpawnEnemy(new Vector3(0,0,1));
        SpawnItem(new Vector3(0,0,2));
   }
   
    public void SpawnEnemy(Vector3 position)
    {
        GameObject enemy = Instantiate(enemyPrefab, position, Quaternion.identity);
        enemy.name = "Enemy";
        // 生成後の処理をここに追加 (例: コンポーネント取得)
    }

    public void SpawnItem(Vector3 position)
    {
        GameObject item = Instantiate(itemPrefab, position, Quaternion.identity);
        item.name = "Item";
        // 生成後の処理をここに追加 (例: パラメータ設定)
    }
}

問題点

  • 生成処理が散らかる

    GameManagerが敵もアイテムも直接生成し、その後の初期化も担うため、管理が煩雑

  • 拡張性が低い

    新しい敵やアイテムを追加するときは、GameManagerを修正する必要がある

  • 保守性に欠ける

    問題が発生した場合、生成処理全体を見直す必要がある


Factoryパターンで生成処理を分離する

Assets/
├── Scripts/
│ ├── Interfaces/
│ │ └── IFactory.cs
│ ├── Factories/
│ │ ├── EnemyFactory.cs
│ │ ├── ItemFactory.cs
│ ├── Managers/
│ └── GameManager.cs

// 共通の生成インターフェース
public interface IFactory
{
    GameObject Create(Vector3 position);
}
// 敵を生成するファクトリー
public class EnemyFactory : IFactory
{
    [SerializeField] private GameObject enemyPrefab;

    public GameObject Create(Vector3 position)
    {
        GameObject enemy = Instantiate(enemyPrefab, position, Quaternion.identity);
        enemy.name = "Enemy";
        // 初期化処理
        return enemy;
    }
}
// アイテムを生成するファクトリー
public class ItemFactory : IFactory
{
    [SerializeField] private GameObject itemPrefab;

    public GameObject Create(Vector3 position)
    {
        GameObject item = Instantiate(itemPrefab, position, Quaternion.identity);
        item.name = "Item";
        // 初期化処理
        return item;
    }
}
public class GameManager : MonoBehaviour
{
    [SerializeField] private EnemyFactory enemyFactory;
    [SerializeField] private ItemFactory itemFactory;

    // 初期化時に生成するオブジェクトの座標
    private Vector3 enemySpawnPosition = new Vector3(0, 0, 0);
    private Vector3 itemSpawnPosition = new Vector3(2, 0, 0);

    private void Start()
    {
        // Start時に敵とアイテムを生成
        SpawnEnemy(enemySpawnPosition);
        SpawnItem(itemSpawnPosition);
    }

    public void SpawnEnemy(Vector3 position)
    {
        GameObject enemy = enemyFactory.Create(position);
        Debug.Log($"Enemy spawned at {position}");
    }

    public void SpawnItem(Vector3 position)
    {
        GameObject item = itemFactory.Create(position);
        Debug.Log($"Item spawned at {position}");
    }
}

改善点

  • 生成処理が整理される

    Factoryが生成処理を専任で担当し、GameManagerの責務が減る

  • 拡張性が向上

    新しいオブジェクトを追加するときは、対応するFactoryを新たに作るだけで済む

  • 保守性が向上

    問題が発生しても、該当Factoryのみを調査すればよい

Discussion