TIL: 팩토리 패턴 (Factory Pattern)
팩토리 패턴이란?
객체 생성을 new 키워드로 직접 하지 않고, 전용 클래스(Factory) 가 대신 생성해주는 디자인 패턴.
생성 로직을 하나의 장소로 집중시켜서, 유지보수, 확장성, 유닛 테스트 등에 유리하다.
상황문제팩토리 패턴으로 해결
| new가 여기저기 흩어져 있음 | 결합도 증가 | Factory 하나로 통합 |
| 생성 조건이 다양함 | 코드 중복, 혼란 | 내부에서 조건 분기 처리 |
| 다양한 하위 클래스 필요 | 타입 의존성 증가 | 추상 클래스/인터페이스로 통일 |
public enum EnemyType { Goblin, Orc }
public class Enemy { }
public class Goblin : Enemy { }
public class Orc : Enemy { }
public static class EnemyFactory
{
public static Enemy CreateEnemy(EnemyType type)
{
switch (type)
{
case EnemyType.Goblin: return new Goblin();
case EnemyType.Orc: return new Orc();
default: throw new ArgumentOutOfRangeException();
}
}
}
- 객체 생성을 중앙에서 관리하니 코드가 깔끔하고 유연해졌다.
- 나중에 적 타입이 추가되더라도 Factory만 수정하면 끝이라 유지보수가 편함.
- 특히 유니티에서 프리팹 기반으로 적 생성할 때 딱 좋은 패턴이라는 걸 느낌.
public class ObjectPool<T> where T : Component
{
private Queue<T> pool = new Queue<T>();
private T prefab;
public ObjectPool(T prefab, int initialSize)
{
this.prefab = prefab;
for (int i = 0; i < initialSize; i++)
{
T obj = GameObject.Instantiate(prefab);
obj.gameObject.SetActive(false);
pool.Enqueue(obj);
}
}
public T Get(Vector3 position, Transform parent = null)
{
T obj = pool.Count > 0 ? pool.Dequeue() : GameObject.Instantiate(prefab);
obj.transform.position = position;
obj.transform.SetParent(parent);
obj.gameObject.SetActive(true);
return obj;
}
public void ReturnToPool(T obj)
{
obj.gameObject.SetActive(false);
pool.Enqueue(obj);
}
}
public class EnemyFactory : MonoBehaviour
{
public GameObject goblinPrefab;
public GameObject orcPrefab;
private ObjectPool<EnemyStats> goblinPool;
private ObjectPool<EnemyStats> orcPool;
private void Awake()
{
goblinPool = new ObjectPool<EnemyStats>(goblinPrefab.GetComponent<EnemyStats>(), 10);
orcPool = new ObjectPool<EnemyStats>(orcPrefab.GetComponent<EnemyStats>(), 10);
}
public EnemyStats CreateEnemy(EnemyType type, Vector3 position)
{
return type switch
{
EnemyType.Goblin => goblinPool.Get(position),
EnemyType.Orc => orcPool.Get(position),
_ => null
};
}
public void ReturnEnemy(EnemyStats enemy)
{
if (enemy is Goblin) goblinPool.ReturnToPool(enemy);
else if (enemy is Orc) orcPool.ReturnToPool(enemy);
}
}
팩토리 + 오브젝트 풀
둘을 동시에 사용시 생성책임은 팩토리에서 관리 재사용을 통한 instantiate 최소화 가능