[Unity] Unity에서의 ServiceLocator패턴
이번에 Unity로 “Mega Crit Games”에서 개발한 “Slay The Spire”모작하면서
많은 manager를 관리해야할 일이 있습니다.
모든 manager를 static으로 하면 쓸데없이 메모리를 많이 잡아먹을 수 있고,
singleton으로 다 구현하면 관리하거나 사용하는 것이 복잡합니다.
또한 직접적인 의존으로 인해 코드간 결합도가 높아져 유지보수에도 문제가 생길 수 있습니다.
그래서 ServiceLocator라는 패턴을 사용해보기로 했습니다!!
ServiceLocator 패턴
ServiceLocator 패턴은 소프트웨어 디자인 패턴 중 하나로, 서비스 객체를 생성하고 관리하는데 사용합니다.
이 패턴은 각 서비스 객체에 대한 의존성을 갖지 않으며, 이들 서비스를 사용할 수 있는 중앙 집중화된 위치를 제공합니다.
ServiceLocator 패턴은 다음과 같은 구성요소로 이루어집니다.
Service : 인터페이스로 정의된 서비스 객체입니다.
Service Implementation : Service 인터페이스를 구현한 서비스 객체입니다.
Service Locator : 서비스 객체의 인스턴스를 찾고 반환하는데 사용되는 클래스입니다.
Client : ServiceLocator를 사용하여 필요한 서비스 객체를 얻는 클라이언트 코드입니다.
보통 ServiceLocator패턴은 Singleton 패턴과 함께 사용됩니다.
이를 통해 ServiceLocator 인스턴스를 하나만 만들어 전체 애플리케이션에서 공유할 수 있으며,
각 서비스 객체는 ServiceLocator를 통해 참조됩니다.
ServiceLocator 패턴은 서비스 객체를 중앙 집중화하는 데 사용됩니다. 이를 통해 서비스 객체의
생성과 구성을 단순화하고, 코드의 유연성과 재사용성을 높일 수 있습니다.
그러나 이 패턴은 ServiceLocator 자체에 대한 의존성이 생길 수 있으며,
이를 사용하는 코드가 복잡해질 수 있습니다.
유니티에서 활용
public interface IRegisterable
{
public void Init();
}
public class ServiceLocator : MonoBehaviour
{
public static ServiceLocator instance;
public static ServiceLocator Instance
{
get
{
if(instance == null)
{
instance = FindObjectOfType<ServiceLocator>();
instance.Init();
}
return instance;
}
}
private IDictionary<object, IRegisterable> services;
private void Init()
{
services = new Dictionary<object, IRegisterable>();
}
public T GetService<T>() where T : MonoBehaviour, IRegisterable
{
if (!services.ContainsKey(typeof(T)))
{
T manager = FindObjectOfType<T>();
if(manager != null)
{
services[typeof(T)] = manager;
manager.Init();
return (T)services[typeof(T)];
}
Debug.LogError("ServiceLocator::GetService 없는 키입니다.");
return default(T);
}
return (T)services[typeof(T)];
}
}
우선 서비스로 등록할 메니저들은 IRegisterable 인터페이스를 상속받도록 합니다.
ServiceLocator 클래스는 Singleton으로 만들어줍니다.
Singleton은 간단하게 만들어줬습니다.
클라이언트에서 가져올 메니저(서비스)들을 가져올 때는 GetService 메소드로 가져옵니다.
Monobehaviour과 IRegisterable를 상속받는 제네릭만 가져올 수 있게 합니다.
GetService
메니저들을 가져올 때는 services dictionary에 있으면 그대로 가져오고
없다면 찾아서 등록해주고 가져옵니다.
여기서 초기화 작업을 해줘야 한다면 IRegisterable 인터페이스에 Init()함수를 강제로 만들게 하여 실행시킬 수도 있습니다.
Comments