우선, 앞서서 Service Locator는 안티 패턴이라는 평가를 받고 있다는 것을 짚고 가겠다.
우선 Service Locator의 Service에 대해서 알아보겠다.
Service
인터페이스에 정의된 기능을 미리 구현하거나, 공통된 기능을 묶어서 만든 클래스이다.
Service Locator란?
- 인터페이스 구현부에서 요청한 서비스를 제공하는 정적 클래스(객체)이다.
- 필요에 의해서 서비스를 생성한다. 생성된 모든 서비스를 Service Locator에서 관리한다.
- 서비스는 타입당 1개만 생성되며, 같은 타입의 서비스는 기존에 생성된 객체(서비스)로 대체한다.
- 생성한 서비스를 이용하지 않더라도, 서비스 객체를 제거하지 않는다.
Service Locator의 단점
- Service Locator는 항상 사용되지 않는 서비스를 포함한, 모든 서비스를 가지고 있다는 것이다.
- 한개의 서비스가 많은 의존성을 가질 수 있다. 실행에 따라서 동작이 달라지는 기능과 같은 경우에 모두 똑같은 서비스에 의존함으로, 의도하지 않은 실행의 차이가 생길 수 있다.
- 외부에서 구현에 필요한 서비스를 제공하는 것이 아닌, 스스로 서비스를 가져오는 방식으로 구현되기 때문에, 결합도가 높다.
- 결합도와 의존성이 높아서, 수정이 어렵다.
물론 여기서 이런 생각이 들 수 있다.
"Service Locator객체를 만들고 외부에서 의존성을 주입해 주면 되는거 아닌가요?"
위의 질문에 대한 답으로, 그렇게 구현하게 된다면 아래와 같다.
(구현 클래스) <- (구현 클래스가 의존할 클래스), (구현 클래스의 의존성 주입을 위한 클래스)
결과적으로 의존성을 외부에 돌리는 것으로, 클래스 하나만 더 생기게 된다.
물론, 코드를 어떻게 작성하는지에 따라서 위의 단점이 나타나지 않을 수 있다. 하지만, 그렇다면 우리는 구현을 강제하는 인터페이스 또한 사용하는 의미가 퇴색될 것이다.
이미 위에서 나열된 단점들만 보더라도, 휴먼 에러의 가능성이 많으며, 많은 개발자가 참여하는 협업 환경에서는 더욱 알맞지 않다는 것을 알 수 있다. 심지어 강한 결합도와 의존성에 의해 수정이 어렵다는 부분은, 추후 기능의 변경이나 추가를 고려했다고 볼 수 없기 때문에, 객체지향의 5원칙 중에서 Open-Closed 원칙에 위배된다.
대안
Service Locator의 대안으로는 DI(Dependency Injection)를 사용하는 것이 일반적으로 자리 잡았다.
Dependency Injection
구현에서 사용된 서비스만 존재한다, 같은 서비스 인스턴스를 공유하지 않으므로, 실행결과가 확실하고 일관되게 유지할 수 있다. 강한 의존성과 높은 결합도는 의존성을 외부에서 받는 방식으로, 서비스에 구애받지 않도록 하였다.
확실하게 Service Locator의 단점을 장점으로 가지는 방식이다. 이로 인해서 Service Locator를 써야할 이유는 더 없다고 보아도 될 것이다.
Service Locator 구현 예시
간단히 구현한 예시로, 기본적인 구현만 포함하고 있습니다.
public static class ServiceLocatorContainer
{
private static Dictionary<Type, object> services = new();
public static void Register<T>() where T : new()
{
Type t = typeof(T);
if(!services.ContainsKey(t))
{
T instance = new();
services.Add(t, instance);
}
}
public static T Get<T>() where T : new()
{
Type t = typeof(T);
if(!services.ContainsKey(t))
{
Register<T>();
}
return (T)services[t];
}
}
public class Service : IServiceProvider
{
public void Method_A()
{
Debug.Log("Hello, ");
}
public void Method_B()
{
Debug.Log("World!");
}
}
public interface IServiceProvider
{
public void Method_A();
public void Method_B();
}
public class TestClass : MonoBehaviour, IServiceProvider
{
private IServiceProvider serviceProvider;
public void Method_A()
{
serviceProvider.Method_A();
}
public void Method_B()
{
serviceProvider.Method_B();
}
private void Awake()
{
ServiceLocatorContainer.Register<Service>();
serviceProvider = ServiceLocatorContainer.Get<Service>();
}
private void Start()
{
Method_A();
Method_B();
}
}
마무리하며
아무리 조심히 사용하더라도, 휴먼에러의 가능성을 무시할 수 없기 때문에, 저 같이 개발을 공부하는 학생이 사용하기에 좋지 않은 것 같다. 그저 ‘Service Locator라는 개념도 있구나.’ 정도로 받아들여 주셨으면 좋겠다.
현실적으로 문제 상황을 직면했을 때는 Dependency Injection을 이용해서 문제를 해결하기 바란다.
'유니티 > 개념 정리' 카테고리의 다른 글
| Internal (0) | 2025.09.29 |
|---|---|
| Dependency Injection (0) | 2025.07.10 |
| 확장 메서드 (3) | 2025.07.02 |
| 메서드 체이닝 (0) | 2025.06.30 |
| SerializedProperty (0) | 2025.04.28 |