기존 ServiceLocator의 경우 서비스를 등록하고 사용해야하는 방법으로 사용하고 있었는데 이번 프로젝트를 진행하면서, GatOrAddComponent 처럼 동적으로 사용할 수 있을 것 같아 코드를 변경한 리팩토링을 진행 합니다.

기존 ServiceLocator

using System;  
using System.Collections.Generic;  
  
public static class ServiceLocator  
{     
	private static readonly Dictionary<Type, object> Services = new(); 
	public static RegisteService<T>()    
    {     
        Services[typeof(T)] = service;    
	}    
	 
    public static T GetService<T>()    
    {          
	    return (T)Services[typeof(T)];    
	}  
}
  • 일일이 서비스를 등록하고 사용을 해야 하지만, 서비스 등록을 하기 전에 서비스에 접근하는 경우에 대해서 확인하고 컨텍스트 순서에 맞추어 등록을 해주어야 하는 불편함이 있습니다.
  • 이를 서비스 검색 시 등록이 되어 있다면 사용하고, 등록이 되어 있지 않다면 등록하여 사용하는 방법으로 리팩토링 합니다.

개선된 ServiceLocator

using System;  
using System.Collections.Generic;  
  
public static class ServiceLocator  
{  
    private static readonly Dictionary<Type, object> Services = new();  
  
    public static T GetService<T>() where T : class  
    {  
        Type serviceType = typeof(T);  
        
        if (Services.TryGetValue(serviceType, out object service))  
        {            
	        return (T)service;  
        }        
        
        return TryCreateService<T>(serviceType);  
    }  
    private static T TryCreateService<T>(Type serviceType) where T : class  
    {  
        T serviceInstance = Activator.CreateInstance<T>();  
        Services[serviceType] = serviceInstance;  
        return serviceInstance;  
    }
}
  • 개선된 코드에서는 T serviceIntance = Activator.CreateIntance<T>()지정된 타입 T의 새 인스턴스를 동적으로 생성하는데 사용됩니다.
  • 프로그램 실행 중 타입이 결정되는 경우, 이 메서드를 사용하여 객체를 동적으로 생성하여 사용이 가능해 집니다.
  • 이 방식은 리플렉션을 사용하기 때문에 직접 인스턴스를 생성하는 것 보다는 느릴 수 있지만, 일반적으로는 별로 성능의 차이가 크지 않습니다.
  • ServiceLocator를 사용하면 싱글턴을 사용하지 않고각 Manager, Data, Resource에 접근이 가능해집니다.

BaseScene.cs 사용 예

using System;  
using Manager;  
using UnityEngine;  
using UnityEngine.EventSystems;  
using Object = UnityEngine.Object;  
  
public class BaseScene : MonoBehaviour  
{  
	public static Label CurrentScene { get; set; }  
	protected ResourceManager resourceManager;  
	protected UIManager uiManager;  
	protected ScenesManager scenesManager;  
	
	private void Awake()  
	{            
		resourceManager = ServiceLocator.GetService<ResourceManager>();  
		uiManager = ServiceLocator.GetService<UIManager>();  
		scenesManager = ServiceLocator.GetService<ScenesManager>();  
	}  
	
	private void Start()  
	{            
		if (resourceManager.Preload) Initialize();  
		else  
		{  
			resourceManager.AllLoadResource<Object>("Preload", (key, count, totalCount) =>  
			{  
				Debug.Log($"[Preload] Load asset {key} ({count}/{totalCount})");  
				if (count < totalCount) return;  
				resourceManager.Preload = true;  
				Initialize();  
			});            
		}        
	}  
	
	protected virtual void Initialize()  
	{            
		Object eventSystem = FindObjectOfType<EventSystem>();  
		if (eventSystem == null) resourceManager.InstantiateObject("EventSystem").name = "@EventSystem";  
	}    
}