유니티 팩토리패턴에 대해 알아보자! #Factory 패턴
예전에 팩토리패턴에 대해 작성했었습니다. 따라서 팩토리 패턴의 정의에 대해서는 언급하지는 않겠습니다.
제가 학교 팀플에서 2D 로그라이크 장르의 게임을 만들고 있는데, 거기에 사용할 만한 Enemy Factory를 한 번 제작해보겠습니다.
IEnemy 인터페이스 정의
public interface IEnemy
{
void Spawn();
}
먼저, 모든 Enemy 클래스가 구현할 IEnemy 인터페이스를 정의합니다. 각 Enemy는 Spawn이라는 메서드를 갖게 되어 소환 시 특정한 동작을 수행할 수 있습니다.
Enemy 클래스 정의
public class Zombie : IEnemy
{
public void Spawn()
{
Debug.Log("Zombie has spawned!");
}
}
public class Goblin : IEnemy
{
public void Spawn()
{
Debug.Log("Goblin has spawned!");
}
}
public class Dragon : IEnemy
{
public void Spawn()
{
Debug.Log("Dragon has spawned!");
}
}
각 Enemy 유형(Zombie, Goblin, Dragon)은 IEnemy 인터페이스를 구현합니다.
팩토리 클래스 정의 with MonoBehaviour
using UnityEngine;
public class EnemyFactory : MonoBehaviour
{
public GameObject zombiePrefab;
public GameObject goblinPrefab;
public GameObject dragonPrefab;
public GameObject CreateEnemy(EnemyType type)
{
switch (type)
{
case EnemyType.Zombie:
return Instantiate(zombiePrefab);
case EnemyType.Goblin:
return Instantiate(goblinPrefab);
case EnemyType.Dragon:
return Instantiate(dragonPrefab);
default:
throw new System.ArgumentException("Invalid enemy type");
}
}
}
팩토리 클래스인 EnemyFactory를 만들어 특정 Enemy을 생성하도록 만들었습니다. 이 클래스는 적의 유형(Type)을 기반으로 Enemy를 생성하는 메서드(CreateEnemy)를 가지고 있습니다.
팩토리 패턴을 적용한 소환 코드
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public EnemyFactory enemyFactory;
private void Start()
{
SpawnEnemy(EnemyType.Zombie);
SpawnEnemy(EnemyType.Goblin);
SpawnEnemy(EnemyType.Dragon);
}
public void SpawnEnemy(EnemyType type)
{
GameObject enemy = enemyFactory.CreateEnemy(type);
}
}
EnemySpawner는 Enemy 생성에 필요한 타입 정보만 받고 있습니다. 이 구조는 새로운 Enemy Type을 추가할 때 EnemySpawner코드를 수정하지 않고도 새로운 Enemy을 생성할 수 있습니다.
EnemyFactory코드만 수정하면 된다는 뜻이지요.
팩토리 패턴을 적용하지 않았더라면?
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public GameObject zombiePrefab;
public GameObject goblinPrefab;
public GameObject dragonPrefab;
private void Start()
{
GameObject zombie = Instantiate(zombiePrefab);
GameObject goblin = Instantiate(goblinPrefab);
GameObject dragon = Instantiate(dragonPrefab);
}
public void SpawnEnemy(string enemyType)
{
switch (enemyType)
{
case "Zombie":
Instantiate(zombiePrefab);
break;
case "Goblin":
Instantiate(goblinPrefab);
break;
case "Dragon":
Instantiate(dragonPrefab);
break;
default:
Debug.LogError("Unknown enemy type");
break;
}
}
}
EnemySpawner가 소환과 각 Enemy의 생성에 대한 모든 세부 사항을 직접 관리해야한다는 점을 불편한 사항으로 뽑을 수 있습니다.
위 코드처럼 Type이 3가지 밖에 없는 상황이라면 굳이 팩토리패턴과 같이 복잡하게 구현할 필요는 없지만 Enemy의 Type이 늘어남에 따라 코드 복잡성이 증가하는 상황이라면 팩토리패턴을 사용해서 코드 복잡성을 줄이고 재사용성을 높이는 방법을 택할 수 있습니다.
팩토리패턴을 사용함으로써 EnemyFactory는 Enemy를 생성하는 로직만을 담당하고, EnemySpawner는 Enemy의 소환만을 처리함으로 클래스의 책임이 명확해지는 장점도 있습니다.