문제 상황
RPG 게임을 만드는 개발자가 있습니다. 이때 각 지형 계절별로 생성되는 몬스터가 다르고, 몬스터들의 각 능력치, 행동패턴, 특성이 조금씩 변형이 이루어져야 한다고 가정할 때 몬스터를 생성하는 스포너부터 시작해서, 각 변형된 몬스터들을 전부 정의해야 하는 상황입니다.
문제 해결
몬스터를 스폰하는 스포너에서 생성하는 몬스터를 추상화하여, 각 타입에 맞게 나누고, 서브 클래스에서 재정의하여, 몬스터들의 각기 다른 변형(재정의)에 대해 정의하고, 이를 스폰하는 방식으로 구조를 설계해보려고 합니다. 아래와 같은 클래스 다이어그램으로 진행해 보겠습니다.
- Factory 이를 이용하는 Client 혹은 Manager에서 IMonsterFactory를 참조받습니다.
- Client에서 CreateZombie, CreateCreeper, CreateSpider를 호출합니다.
- 호출하는 시점의 Factory를 보고, 각 타입에 맞게 재정의된 Ice, Normal을 Create 하고, 반환합니다.
이제 실제 스크립트에서 호출되고 정의되는 흐름을 알아보겠습니다.
클래스 정의
NameSpace Factory
// Base Interface Factory
public interface IMonsterFactory
{
ICreeper CreateCreeper();
IZombie CreateZombie();
ISpider CreateSpider();
}
// override Factory Normal
public class NormalMonsterFactory : MonoBehaviour, IMonsterFactory
{
[SerializeField] private NormalCreeper Creeper;
[SerializeField] private NormalSpider Spider;
[SerializeField] private NormalZombie Zombie;
public ICreeper CreateCreeper()
{
return Instantiate(Creeper);
}
public ISpider CreateSpider()
{
return Instantiate(Spider);
}
public IZombie CreateZombie()
{
return Instantiate(Zombie);
}
}
// override Factory Ice
public class ICEMonsterFactory : MonoBehaviour, IMonsterFactory
{
[SerializeField] private IceCreeper Creeper;
[SerializeField] private IceSpider Spider;
[SerializeField] private IceZombie Zombie;
public ICreeper CreateCreeper()
{
return Instantiate(Creeper);
}
public ISpider CreateSpider()
{
return Instantiate(Spider);
}
public IZombie CreateZombie()
{
return Instantiate(Zombie);
}
}
Client에서 활용할 객체 생성 Factory를 추상화하여, 특정 형태로 변형된(override) 객체를 생성할 수 있는 클래스들입니다. 여기서 생성되는 ICreeper, IZombie, ISpider는 각 타입에 맞게 동작(행위)들을 추상화하고 있습니다.
NameSpace Monster
// Base Zombie Interface
public interface IZombie
{
float Speed { get; set; }
float Health { get; set; }
float Strength { get; set; }
void SomeOperation();
}
// override Ice Zombie
public class IceZombie : MonoBehaviour, IZombie
{
public float Speed { get; set; }
public float Health { get; set; }
public float Strength { get; set; }
public void SomeOperation()
{
Debug.Log($"Operation {nameof(IceZombie)}");
}
}
// Override Normal Zombie
public class NormalZombie : MonoBehaviour, IZombie
{
public float Speed { get; set; }
public float Health { get; set; }
public float Strength { get; set; }
public void SomeOperation()
{
Debug.Log($"Some Operation {nameof(NormalZombie)}");
}
}
예시로 좀비 객체로만 업로드하였는데 패키지를 통해 전체 객체를 확인할 수있습니다. 이를 활용한 Samples 코드를 확인해 보면, Client에서 IMonsterFactory를 통해, 객체 생성을 호출할 때의 MonterFactory의 의존성에 맞추어 Ice 또는 Normal의 몬스터를 생성하고, 반환합니다.
아래 코드는 Client(실제 호출자)의 소스입니다.
void Start()
{
//Normal
InitializeFactory(NormalMonsterFactory);
CreateMonsters();
//Ice
InitializeFactory(IceMonsterFactory);
CreateMonsters();
}
private void InitializeFactory(IMonsterFactory monsterFactory)
{
this.monsterFactory = monsterFactory;
}
private void CreateMonsters()
{
ICreeper creeper = monsterFactory.CreateCreeper();
creeper.SomeOperation();
ISpider spider = monsterFactory.CreateSpider();
spider.SomeOperation();
IZombie zombie = monsterFactory.CreateZombie();
zombie.SomeOperation();
}
현재는 Start에서 의존성 주입과, 생성을 하고 있지만, 실제 Spawner 혹은 스테이지 시작 시에 맞추어, IMonsterFactory의 의존성을 주입하고, 이를 통해 추상화되어 있는 객체들을 생성할 수 있습니다.
'C#' 카테고리의 다른 글
[디자인 패턴] 생성 패턴 - 팩토리 메서드 (0) | 2025.10.11 |
---|---|
[문법] ReadOnlySpan<char> 자료형 (2) | 2025.08.12 |
[문법] IFormatProvider (0) | 2025.08.04 |
[문법] String To int Parser (0) | 2025.07.15 |