[2025_12_18] Factory패턴의 실적용 여부
2025. 12. 18. 21:04ㆍTIL
12-18 Todo
1. 유닛 스킬 시스템(Base Skill)
2. 시간 남으면 오브젝트 풀링
3. 최대 유닛 수 제한(UI 포함)
팩토리 패턴 (Factory Pattern) - 확장성을 위한 객체 생성 패턴
배경: 스킬 시스템 설계 고민
스킬 시스템을 구현하면서 팩토리 패턴을 적용할지 고민했다.
처음 생각했던 것:
- 다양한 스킬들 (공격, 버프, 힐링 등)을 생성해야 함
- 스킬 타입에 따라 생성 로직이 다를 수 있음
- 팩토리 패턴으로 스킬 생성을 관리하면 좋지 않을까?
하지만 현재 상황을 분석해보니...
팩토리 패턴이 필요 없는 이유
현재 구조
csharp
public class UnitBase : MonoBehaviour
{
public List<SkillBase> skills;
public void UseSkill(int index)
{
if (index < skills.Count)
{
skills[index].Execute(this);
}
}
}
public abstract class SkillBase
{
public abstract void Execute(UnitBase caster);
}
public class AttackSkill : SkillBase
{
public override void Execute(UnitBase caster)
{
// 공격 스킬 로직
}
}
스킬 생성 방식
csharp
// UnitBase 초기화 시
public void InitSkills()
{
skills = new List<SkillBase>
{
new AttackSkill(),
new BuffSkill(),
new HealSkill()
};
}
데이터 기반 설계
JSON 데이터로 스킬 관리
csharp
[System.Serializable]
public class SkillData
{
public int id;
public SkillType skillType;
public string name;
public int damage;
public int healAmount;
public float range;
public float cooldown;
public string effectPrefabPath;
}
public class DataDrivenSkillFactory
{
private SkillDataManager dataManager;
public DataDrivenSkillFactory(SkillDataManager manager)
{
dataManager = manager;
}
public SkillBase CreateSkill(int skillID)
{
SkillData data = dataManager.GetSkillData(skillID);
SkillBase skill = data.skillType switch
{
SkillType.Attack => CreateAttackSkill(data),
SkillType.Heal => CreateHealSkill(data),
SkillType.Buff => CreateBuffSkill(data),
_ => null
};
// 공통 속성 설정
skill.skillName = data.name;
skill.cooldown = data.cooldown;
return skill;
}
private AttackSkill CreateAttackSkill(SkillData data)
{
AttackSkill skill = new AttackSkill();
skill.damage = data.damage;
skill.range = data.range;
return skill;
}
private HealSkill CreateHealSkill(SkillData data)
{
HealSkill skill = new HealSkill();
skill.healAmount = data.healAmount;
skill.range = data.range;
return skill;
}
}
장점:
- 코드 수정 없이 JSON만 수정하면 됨
- 기획자가 밸런스 조정 가능
- 새 스킬 추가가 매우 쉬움
왜 팩토리 패턴이 불필요한가?
- 스킬을 사용하는 주체가 현재는 UnitBase 하나뿐
- 각 유닛이 자신의 스킬을 직접 관리하면 충분
- 스킬 타입이 정해져 있고 동적 생성이 필요 없음
- 단순히 new로 생성해도 문제없음
결론: 지금은 YAGNI(You Aren't Gonna Need It) 원칙에 따라 단순하게 구현하는 게 맞다.
추후 리팩토링 관점
나중에 이런 상황이 오면?
시나리오 1: 유저(플레이어)도 스킬 사용
csharp
public class Player : MonoBehaviour
{
public void UseUltimateSkill()
{
// 플레이어 궁극기 스킬
}
}
시나리오 2: 건물(타워)도 스킬 사용
csharp
public class Tower : MonoBehaviour
{
public void UseDefenseSkill()
{
// 타워 방어 스킬
}
}
문제 발생!
- UnitBase, Player, Tower 모두 스킬을 사용
- 각각 스킬 생성 로직을 따로 구현?
- 스킬 데이터 관리는 어떻게?
- 코드 중복 증가
팩토리 패턴이 필요한 시점
상황 1: 여러 타입의 객체가 같은 스킬을 사용할 때
csharp
public interface ISkillUser
{
void UseSkill(SkillBase skill);
}
public class UnitBase : MonoBehaviour, ISkillUser { }
public class Player : MonoBehaviour, ISkillUser { }
public class Tower : MonoBehaviour, ISkillUser { }
상황 2: 스킬을 데이터 기반으로 생성할 때
json
{
"skills": [
{
"id": 1,
"type": "Attack",
"damage": 100,
"range": 5.0
},
{
"id": 2,
"type": "Heal",
"healAmount": 50
}
]
}
상황 3: 스킬을 동적으로 장착/해제할 때
csharp
// 스킬 장착 시스템
public void EquipSkill(int skillID)
{
SkillBase skill = skillFactory.CreateSkill(skillID);
equippedSkills.Add(skill);
}
리팩토링 시점: 유닛 + 유저 + 건물
미래에 이렇게 확장될 때:
공통 인터페이스
csharp
public interface ISkillUser
{
Transform Transform { get; }
void UseSkill(SkillBase skill);
}
유닛 구현
csharp
public class UnitBase : MonoBehaviour, ISkillUser
{
public Transform Transform => transform;
private List<SkillBase> skills;
public void Init(SkillFactory factory, int[] skillIDs)
{
foreach (int id in skillIDs)
{
skills.Add(factory.CreateSkill(id));
}
}
public void UseSkill(SkillBase skill)
{
skill.Execute(this);
}
}
플레이어 구현
csharp
public class Player : MonoBehaviour, ISkillUser
{
public Transform Transform => transform;
private SkillBase ultimateSkill;
public void Init(SkillFactory factory, int ultimateID)
{
ultimateSkill = factory.CreateSkill(ultimateID);
}
public void UseSkill(SkillBase skill)
{
skill.Execute(this);
}
public void UseUltimate()
{
UseSkill(ultimateSkill);
}
}
타워 구현
csharp
public class Tower : MonoBehaviour, ISkillUser
{
public Transform Transform => transform;
private SkillBase defenseSkill;
public void Init(SkillFactory factory, int defenseSkillID)
{
defenseSkill = factory.CreateSkill(defenseSkillID);
}
public void UseSkill(SkillBase skill)
{
skill.Execute(this);
}
public void ActivateDefense()
{
UseSkill(defenseSkill);
}
}
이렇게 하면:
- 모든 엔티티가 SkillFactory를 통해 스킬 생성
- 스킬 생성 로직이 한 곳에 집중
- 각 엔티티는 자신의 역할에만 집중
팩토리 패턴 적용 시점 판단 기준
적용하면 좋을 때
- 2개 이상의 클래스가 같은 타입의 객체를 생성할 때
- 객체 생성 로직이 복잡할 때
- 데이터 기반으로 객체를 생성해야 할 때
- 생성할 객체 타입이 런타임에 결정될 때
적용하지 않아도 될 때
- 한 곳에서만 객체를 생성할 때 (현재 상황)
- 생성 로직이 매우 단순할 때
- 확장 계획이 없을 때
- 과도한 추상화가 오히려 복잡도를 높일 때
결론: 현재와 미래의 균형
- 유저, 건물 등 다양한 엔티티가 스킬 사용
- 스킬을 데이터 기반으로 관리
- 동적 스킬 장착 시스템 추가
이럴 때 팩토리 패턴으로 리팩토링하면 좋다!
핵심 교훈
"지금 당장 필요 없으면 추가하지 말되, 나중에 필요할 때를 대비해 알아는 두자"
실무 개발 원칙
- YAGNI (You Aren't Gonna Need It) - 필요하지 않으면 만들지 마라
- KISS (Keep It Simple, Stupid) - 단순하게 유지하라
- 하지만 확장성은 염두에 두라 - 리팩토링 시점을 판단할 줄 알아야 함
'TIL' 카테고리의 다른 글
| [2025_12_23] 에셋 정하기 (0) | 2025.12.23 |
|---|---|
| [2025_12_22] 모의면접 피드백 (1) | 2025.12.22 |
| [2025_12_17] 유닛 이동 관련 최적화 (0) | 2025.12.17 |
| [2025_12_16] 오토배틀 게임 유닛 AI 구현하며 마주친 문제들 (1) | 2025.12.16 |
| [2025_12_15] SpreadSheet에서 배열 파싱 오류 (0) | 2025.12.15 |