두 컨테이너의 차이점을 이해하는 가장 쉬운 방법과 DI 컨테이너가 서비스 로케이터보다 훨씬 나은 이유는 왜 의존성 반전을 먼저 수행하는지에 대한 생각입니다.
우리는 의존성 역전을 수행하여 각 클래스가 연산에 의존하는 것을 정확하게 명시 합니다. 우리가 달성 할 수있는 가장 느슨한 결합을 생성하기 때문에 그렇게합니다. 커플 링이 느슨할수록 테스트 및 리팩토링이 쉬워집니다 (일반적으로 코드가 더 깨끗하기 때문에 향후 리팩토링이 가장 적게 필요함).
다음 클래스를 보자.
public class MySpecialStringWriter
{
private readonly IOutputProvider outputProvider;
public MySpecialFormatter(IOutputProvider outputProvider)
{
this.outputProvider = outputProvider;
}
public void OutputString(string source)
{
this.outputProvider.Output("This is the string that was passed: " + source);
}
}
이 클래스에서는 IOutputProvider가 필요하며이 클래스를 작동시키기 위해 다른 것은 필요 없다고 명시 적으로 설명하고 있습니다. 이것은 완전히 테스트 가능 하며 단일 인터페이스에 의존합니다. 이 클래스를 다른 프로젝트를 포함하여 응용 프로그램의 어느 곳으로나 이동할 수 있으며 필요한 것은 IOutputProvider 인터페이스에 대한 액세스입니다. 다른 개발자가이 클래스에 새로운 것을 추가하고 싶을 때, 두 번째 의존성 이 필요한 경우, 생성자에 무엇이 필요한지 명시 해야합니다.
서비스 로케이터로 동일한 클래스를 살펴보십시오.
public class MySpecialStringWriter
{
private readonly ServiceLocator serviceLocator;
public MySpecialFormatter(ServiceLocator serviceLocator)
{
this.serviceLocator = serviceLocator;
}
public void OutputString(string source)
{
this.serviceLocator.OutputProvider.Output("This is the string that was passed: " + source);
}
}
이제 서비스 로케이터를 종속성으로 추가했습니다. 즉시 명백한 문제는 다음과 같습니다.
- 이것의 첫 번째 문제 는 동일한 결과를 달성하기 위해 더 많은 코드 가 필요 하다는 것 입니다. 더 많은 코드가 잘못되었습니다. 훨씬 더 많은 코드는 아니지만 여전히 더 많은 코드입니다.
- 두 번째 문제는 내 의존성이 더 이상 명백하지 않다는 것 입니다. 여전히 수업에 무언가를 주입해야합니다. 내가 원하는 것은 분명하지 않다. 내가 요청한 것의 속성에 숨겨져 있습니다. 클래스를 다른 어셈블리로 옮기려면 ServiceLocator와 IOutputProvider에 모두 액세스해야합니다.
- 세 번째 문제는 클래스에 코드를 추가 할 때 자신이 취하는 것을 깨닫지 못하는 다른 개발자 가 추가 종속성을 얻을 수 있다는 것 입니다.
- 마지막으로,이 코드는 IOutputProvider 대신 ServiceLocator와 IOutputProvider를 조롱해야하기 때문에 ServiceLocator가 인터페이스 인 경우에도 테스트하기 가 더 어렵 습니다.
그렇다면 서비스 로케이터를 정적 클래스로 만들지 않는 이유는 무엇입니까? 한 번 보자:
public class MySpecialStringWriter
{
public void OutputString(string source)
{
ServiceLocator.OutputProvider.Output("This is the string that was passed: " + source);
}
}
이것은 훨씬 더 간단합니다.
잘못된.
IOutputProvider는 전 세계 15 개의 서로 다른 데이터베이스에 문자열을 작성하고 완료하는 데 시간이 오래 걸리는 매우 오래 실행되는 웹 서비스로 구현되었다고 가정 해 보겠습니다.
이 수업을 테스트 해 봅시다. 테스트를 위해 다른 IOutputProvider 구현이 필요합니다. 시험은 어떻게 작성합니까?
테스트를 위해 호출 될 때 IOutputProvider의 다른 구현을 사용하려면 정적 ServiceLocator 클래스에서 멋진 구성을 수행해야합니다. 그 문장을 쓰는 것조차도 고통 스러웠습니다. 그것을 구현하는 것은 끔찍하고 유지 보수의 악몽 이 될 것 입니다 . 테스트 할 클래스, 특히 해당 클래스가 실제로 테스트하려는 클래스가 아닌 경우 클래스를 수정할 필요가 없습니다.
이제 a) 관련이없는 ServiceLocator 클래스에서 눈에 띄지 않는 코드 변경을 유발하는 테스트; 또는 b) 전혀 테스트하지 않습니다. 유연성이 떨어지는 솔루션도 있습니다.
따라서 서비스 로케이터 클래스 는 생성자에 주입되어야합니다. 즉, 앞에서 언급 한 특정 문제가 남아 있습니다. 서비스 로케이터는 더 많은 코드를 필요로하고, 다른 개발자에게 필요하지 않은 것을 필요로한다고 말하고, 다른 개발자가 더 나쁜 코드를 작성하도록 장려하며, 유연성이 떨어집니다.
간단히 말하면 서비스 로케이터는 응용 프로그램에서 커플 링을 증가 하고 매우 결합 된 코드를 작성하는 다른 개발자를 권장합니다 .