Dependency Injection을 사용하면 어떤 단점이 있습니까? [닫은]


336

내가 알고 싶습니다 여기에 작업과 우리의 리드 개발자 중 하나의 패턴으로 DI를 소개하려고 해요 : 무엇 - 어떤 경우 -이다 단점 의존성 삽입 패턴을 사용하는가?

참고 주제에 대한 주관적인 논의가 아니라 가능한 경우 전체 목록을 찾고 있습니다.


설명 : XML 기반 (Spring과 같은) 또는 코드 기반 (Guice와 같은) 또는 "자가 롤링"여부와 상관없이 특정 프레임 워크가 아닌 Dependency Injection 패턴 ( Martin Fowler 의이 기사 참조) 에 대해 이야기하고 있습니다. .


편집 : 여기 에 / r / programming 에 대한 추가 토론 / ranting / 토론이 있습니다.


DI 자체에 대해 논의해야하는지 또는이를 지원하는 특정 유형의 도구 (XML 기반 여부)를 지정하는 것이 좋습니다.
Jesse Millikan

5
차이점은 "XML sucks"와 같은 특정 프레임 워크에만 적용되는 답변을 찾지 않는다는 것입니다. :) 나는 Fowler가 설명한 DI 개념에 적용되는 답변을 찾고 있습니다 : martinfowler.com/articles/injection.html
Epaga

3
DI (일반적으로 생성자, 설정자 또는 방법)에 대한 단점은 전혀 없습니다. 오버 헤드없이 분리하고 단순화합니다.
Kissaki

2
@ kissaki 세터와는 별도로 물건을 주입하십시오. 시공시 사용할 수있는 물체를 만드는 것이 가장 좋습니다. 당신이 나를 믿지 않는다면, 단지 '세터 (setter)'주입으로 다른 협력자를 객체에 추가해보십시오. 당신은 확실히 NPE를 얻게 될 것입니다 ....
time4tea

답변:


209

몇 가지 사항 :

  • DI는 책임이 더 분리되어 있기 때문에 일반적으로 클래스 수를 늘림으로써 복잡성을 증가시킵니다.
  • 코드는 사용하는 의존성 주입 프레임 워크 (또는 더 일반적으로 DI 패턴을 구현하기로 결정한 방법)와 결합됩니다.
  • 유형 해석을 수행하는 DI 컨테이너 또는 접근 방식은 일반적으로 약간의 런타임 페널티가 발생합니다 (매우 무시할 수는 있지만)

일반적으로 디커플링의 이점으로 인해 각 작업을 읽고 이해하기가 더 간단 해지지 만보다 복잡한 작업을 조율하는 복잡성이 증가합니다.


72
-클래스를 분리하면 복잡성이 줄어 듭니다. 많은 클래스가 응용 프로그램을 복잡하게 만들지 않습니다. -응용 프로그램 루트의 DI 프레임 워크에만 의존해야합니다.
Robert

91
우리는 인간입니다. 단기 메모리는 제한적이며 많은 <xxx>를 동시에 처리 할 수 ​​없습니다. 메소드, 함수, 파일 또는 프로그램을 개발하는 데 사용하는 구성과 동일하게 클래스와 동일합니다.
Håvard S

45
@Havard S : 그렇습니다. 그러나 5 개의 복잡한 클래스는 15 개의 간단한 클래스보다 단순하지 않습니다. 복잡성을 줄이는 방법은 프로그래머가 코드에서 작업하는 데 필요한 작업 세트를 줄이는 것입니다. 복잡하고 상호 의존성이 높은 클래스는이를 달성하지 못합니다.
kyoryu

35
@kyoryu 나는 우리 모두가 그것에 동의한다고 생각합니다. 있다는 사실을 숙지 복잡 커플 링과 동일하지 않습니다 . 커플 링을 줄이면 복잡성이 증가 할 수 있습니다. DI가 클래스 수를 증가시키기 때문에 DI가 나쁜 것이라고 말하지는 않습니다. DI와 관련된 잠재적 단점을 강조하고 있습니다. :)
Håvard S

96
"DI는 복잡성을 증가시킨다"에 대해 +1 DI와 그 이상에 해당된다. (거의) 유연성을 높일 때마다 복잡성이 증가합니다. 그것은 모든 장점과 단점을 아는 것으로 시작되는 균형에 관한 것입니다. 사람들이 '단점은 없다'고 말할 때, 아직 그 사실을 완전히 이해하지 못했다는 확실한 지표입니다.
돈 브랜슨

183

객체 지향 프로그래밍, 스타일 규칙 및 기타 모든 것에서 흔히 발생하는 것과 동일한 기본 문제입니다. 사실 매우 일반적인 추상화는 너무 많은 추상화를하고 너무 많은 간접 성을 추가하고 일반적으로 좋은 기술을 과도하고 잘못된 곳에 적용하는 것이 가능합니다.

적용하는 모든 패턴 또는 기타 구성은 복잡성을 가져옵니다. 추상화와 간접 정보는 정보를 분산 시키며 때로는 관련이없는 세부 사항을 방해하지 않지만 때로는 정확히 무슨 일이 일어나고 있는지 이해하기 어렵게 만듭니다. 적용하는 모든 규칙은 융통성이 없어 최상의 방법 일 수있는 옵션을 배제합니다.

요점은 작업을 수행하고 강력하고 읽기 쉽고 유지 관리가 가능한 코드를 작성하는 것입니다. 아이보리 타워 빌더가 아닌 소프트웨어 개발자입니다.

관련 링크

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx

http://www.joelonsoftware.com/articles/fog0000000018.html


아마도 가장 간단한 형태의 의존성 주입 (웃지 마)이 매개 변수 일 것입니다. 종속 코드는 데이터에 의존하며 해당 데이터는 매개 변수를 전달하는 수단으로 주입됩니다.

그렇습니다. 어리 석고 객체 지향 의존성 주입 포인트를 다루지 않지만 함수형 프로그래머는 (첫 번째 클래스 함수가있는 경우) 이것이 필요한 유일한 의존성 주입이라고 알려줍니다. 여기서 중요한 것은 간단한 예를 들어 잠재적 인 문제를 보여주는 것입니다.

이 간단한 전통적인 함수를 생각해 봅시다-C ++ 구문은 중요하지 않지만 어떻게 든 철자를 써야합니다 ...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

텍스트 "Hello World"를 추출하고 주입하려는 종속성이 있습니다. 충분히 쉬운 ...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

원래보다 어떻게 융통성이 없습니까? 출력이 유니 코드 여야한다고 결정하면 어떨까요? 아마도 std :: cout에서 std :: wcout으로 전환하고 싶을 것입니다. 그러나 그것은 내 문자열이 char가 아니라 wchar_t이어야 함을 의미합니다. 모든 호출자를 변경하거나 (더 합리적으로) 이전 구현은 문자열을 변환하고 새 구현을 호출하는 어댑터로 대체됩니다.

그것은 우리가 원본을 유지한다면 필요하지 않은 유지 보수 작업입니다.

그리고 사소한 것처럼 보이면 Win32 API 에서이 실제 기능을 살펴보십시오 ...

http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx

그것은 다루어야 할 12 가지 "종속성"입니다. 예를 들어, 화면 해상도가 너무 커지면 64 비트 좌표 값과 다른 버전의 CreateWindowEx가 필요할 수 있습니다. 그리고 예, 이미 구식 버전이 남아 있습니다. 아마도 이전 버전의 새로운 버전에 매핑 될 것입니다 ...

http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx

이러한 "종속성"은 원래 개발자에게는 문제가 아닙니다. 해당 인터페이스를 사용하는 모든 사람은 종속성이 무엇인지, 지정 방법 및 의미가 무엇인지, 응용 프로그램을 위해 수행 할 작업을 찾아야합니다. 이곳에서 "기본 설정"이라는 단어가 삶을 훨씬 간단하게 만들 수 있습니다.

객체 지향 의존성 주입은 원칙적으로 다르지 않습니다. 클래스 작성은 소스 코드 텍스트와 개발자 시간 모두에서 오버 헤드이며, 해당 클래스가 일부 종속 객체 사양에 따라 종속성을 제공하도록 작성된 경우 종속 객체는 필요한 경우에도 해당 인터페이스를 지원하도록 잠겨 있습니다. 해당 객체의 구현을 대체합니다.

의존성 주입이 잘못되었다고 주장하는 것은 아닙니다. 그러나 어떤 좋은 기술이라도 과도하게 잘못 적용 할 수 있습니다. 모든 문자열을 추출하여 매개 변수로 변환 할 필요는 없지만 모든 하위 수준 동작을 고급 개체에서 추출하여 주입 가능한 종속성으로 변환 할 필요는 없습니다.


3
의존성 주입에는 이러한 단점이 없습니다. 객체에 대한 의존성을 전달하는 방식 만 변경하므로 복잡성이나 유연성이 추가되지 않습니다. 꽤 대조적 인 것.
Kissaki

24
@Kissaki-예, 의존성을 전달하는 방식이 바뀝니다. 그리고 의존성 전달을 처리하는 코드를 작성해야합니다. 이를 수행하기 전에 전달할 종속성을 해결하고 종속 코드를 코딩하지 않은 사람에게 적합한 방식으로 정의해야합니다. 런타임에 영향을 피할 수 있습니다 (예 : 정책 매개 변수를 템플릿에 사용) C ++에서),하지만 여전히 작성하고 유지 해야하는 코드입니다. 그것이 정당화된다면, 그것은 큰 보상을위한 아주 작은 가격이지만, 당신이 지불하는 가격이 없다고 주장한다면 그것은 이득이 없을 때 그 가격을 지불 할 것임을 의미합니다.
Steve314

6
@Kissaki-융통성에 관해서는 일단 의존성을 지정하면 다른 사람들이 그 스펙에 따라 주입하기 위해 의존성을 작성했습니다. 약간 다른 종속성을 필요로하는 종속 코드에 대한 새로운 구현이 필요한 경우 어려움을 겪게됩니다. 대신에 이러한 종속성에 대한 일부 어댑터를 작성하는 시간 (그리고 약간 더 많은 오버 헤드와 다른 추상화 계층)을 시작하십시오.
Steve314

오버로드를 고려하면이 예제를 이해하지 못합니다. 리터럴 "Hello World"를 출력하기 위해 () 서명을 사용합니다. 8 비트 문자열을 출력하려면 (char) 서명을 사용하십시오. 유니 코드를 출력하려면 과부하 (wchar_t)입니다. 분명히이 경우에, (wchar_t)는 다른 사람들이 무대 뒤에서 부르는 것입니다. 많은 코드 재 작성이 필요하지 않았습니다. 뭔가 빠졌습니까?
ingredient_15939

2
@ ingredient_15939-예, 이것은 간단한 장난감 예입니다. 실제로이 작업을 수행하는 경우 코드 복제 비용이 어댑터 비용보다 저렴하므로 어댑터 대신 오버로드를 사용합니다. 그러나 코드 복제는 일반적으로 나쁜 일이며 코드 복제를 피하기 위해 어댑터를 사용하는 것도 또 다른 좋은 기술입니다.
Steve314

77

여기 내 자신의 초기 반응이 있습니다 : 기본적으로 모든 패턴의 동일한 단점.

  • 배우는데 시간이 걸린다
  • 잘못 이해하면 선보다 더 많은 해로 이어질 수 있습니다
  • 극단으로 가져 가면 이익을 정당화하는 것보다 더 많은 일이 될 수 있습니다

2
왜 배우는데 시간이 걸리나요? 나는 그것이 ejb에 비해 일을 간단하게 만든다고 생각했습니다.
fastcodejava

8
이것은 주관적인 토론을 원하지 않는다는 점을 고려할 때 수동으로 나타납니다. 나는 시험을위한 현실적인 예를 (집합 적으로 필요한 경우) 구축하는 것을 제안한다. DI 없이 샘플 만드는 것이 좋은 출발점이 될 것입니다. 그런 다음 요구 사항을 변경하고 그 영향을 조사 할 수 있습니다. (내가 잠자리에
들자 마자

4
이것은 실제로 그것을 뒷받침하기위한 몇 가지 예가 필요하며, 수십 명의 사람들에게 DI를 가르쳤습니다. 나는 그것이 실제로 배우고 실천하기 시작하는 매우 간단한 개념이라고 말할 수 있습니다.
chillitom

4
나는 DI를 패턴으로 생각하지 않습니다. 그것은 (IoC와 결합 된) 실제로 프로그래밍 모델에 가깝습니다. 완전히 따르는 경우 코드는 "일반적인"의사 절차 OO보다 훨씬 더 액터처럼 보입니다.
kyoryu

1
+1 Symfony 2를 사용하고 있습니다. DI는 사용자 코드 전체에 있습니다. 그냥 사용하고 있습니다.
MGP

45

Inversion of Control의 가장 큰 단점은 (DI는 아니지만 충분히 가깝습니다) 알고리즘의 개요를 볼 때 단일 지점을 갖는 것을 제거하는 경향이 있다는 것입니다. 기본적으로 코드를 분리 할 때 발생하는 현상입니다. 한 곳에서 볼 수있는 기능은 긴밀한 결합의 인공물입니다.


2
그러나 "단점"은 우리가 해결하고있는 문제의 본질로 인해 구현을 쉽게 변경할 수 있도록 구현할 수있는 곳이 없다는 것을 의미하며, 어떤 관련성을 볼 수 있는가? 내가 생각할 수있는 유일한 경우는 디버깅에 있으며 디버깅 환경은 구현을 시작할 수 있어야합니다.
vickirk

3
이것이 "다운 사이드"라는 단어가 인용 부호 안에있는 이유입니다. 느슨한 결합과 강력한 캡슐화는 "모든 것을 볼 수있는 한 곳"을 정의상으로 배제합니다. DI / IoC에 반대하는 느낌이들 경우 Havard S의 답변에 대한 나의 의견을 참조하십시오.
kyoryu

1
동의합니다 (나는 심지어 당신에게 투표했습니다). 나는 단지 요점을 지적했다.
vickirk

1
@ vickirk : 내가 추측하는 단점은 인간이 이해하기 어렵다는 것입니다. 사물을 분리하면 인간이 이해하기가 더 어렵다는 의미에서 복잡성이 증가하고 완전히 이해하는 데 시간이 더 걸립니다.
Bjarke Freund-Hansen

2
반대로 DI의 한 가지 장점은 개별 클래스의 생성자를보고 어떤 클래스에 의존하는지 (즉, 어떤 클래스가 작업을 수행해야하는지) 즉시 해결할 수 있다는 것입니다. 코드가 willy-nilly 클래스를 만들 수 있기 때문에 다른 코드에서는 훨씬 어렵습니다.
Contango

42

7
누군가가 원 사이드를 요구할 때 왜 "아무것도 없다"는 정신으로 답이 올라가고 질문과 관련된 정보가 포함되어 있지 않은지 궁금합니다.
Gabriel Ščerbák

12
-1 링크 모음을 찾지 않기 때문에 실제 답변을 찾고 있습니다. 각 기사의 요점을 요약하면 어떻습니까? 그런 다음 대신 투표했습니다. 처음 두 기사가 실제로 DI의 부정을 해제하는 것처럼 보이지만 기사 자체는 매우 흥미 롭습니다.
Epaga

4
가장 인기있는 답변도 다운 투표를 받았는지 궁금합니다. 왜냐하면 실제로 모든 imho의 질문에 대답하지 못하기 때문입니다. DI의 단점은 너무 시원하다는 것이 분명히 도움이되는 반면 대답은 도움이되지 않습니다.
Gabriel Ščerbák

41

지난 6 개월 동안 Guice (Java DI 프레임 워크)를 광범위하게 사용하고 있습니다. 전반적으로 나는 그것이 (특히 테스트 관점에서) 훌륭하다고 생각하지만, 특정 단점이 있습니다. 가장 주목할만한 점 :

  • 코드를 이해하기가 더 어려워 질 수 있습니다.의존성 주입은 매우 독창적 인 방법으로 사용될 수 있습니다. 예를 들어 특정 IOStream (예 : @ Server1Stream, @ Server2Stream)을 삽입하기 위해 사용자 지정 주석을 사용하는 일부 코드를 발견했습니다. 이것이 효과가 있지만 특정 우아함을 인정할 것이지만, 코드를 이해하기 위해서는 Guice 주입을 이해하는 것이 전제 조건이됩니다.
  • 프로젝트를 학습 할 때 더 높은 학습 곡선.이것은 포인트 1과 관련이 있습니다. 의존성 주입을 사용하는 프로젝트의 작동 방식을 이해하려면 의존성 주입 패턴과 특정 프레임 워크를 모두 이해해야합니다. 내가 현재 직장에서 시작했을 때 나는 Guice가 무대 뒤에서 무엇을하고 있는지에 대해 혼란스러워하는 몇 시간을 보냈다.
  • 생성자가 커집니다. 기본 생성자 또는 팩토리를 사용하여 대부분 해결할 수 있습니다.
  • 오류가 난독 화 될 수 있습니다. 이것의 가장 최근의 예는 2 개의 깃발 이름에 충돌이 있었다는 것입니다. Guice가 자동으로 오류를 삼키고 내 플래그 중 하나가 초기화되지 않았습니다.
  • 런타임에 오류가 발생합니다. Guice 모듈을 잘못 구성하면 (원형 참조, 잘못된 바인딩 등) 컴파일시 대부분의 오류가 발견되지 않습니다. 대신 프로그램이 실제로 실행될 때 오류가 노출됩니다.

이제 내가 불평했습니다. 현재 프로젝트 및 다음 프로젝트에서 Guice를 계속 사용하겠다고 말하겠습니다. 의존성 주입은 훌륭하고 믿을 수 없을만큼 강력한 패턴입니다. 그러나 확실히 혼란 스러울 수 있으며 선택한 의존성 주입 프레임 워크에서 무엇이든 저주하는 데 거의 시간이 걸릴 것입니다.

또한 의존성 주입이 과도하게 사용될 수 있다는 다른 포스터에 동의합니다.


14
나는 사람들이 왜 더 많은 "오류가 런타임으로 푸시되는지"를 이해하지 못한다는 것을 이해하지 못한다. 그들을 위해 멀리
Richard Tingle

2
앱 시작시 @RichardTingle, IMO DI 모듈이 먼저 초기화되므로 며칠 또는 몇 시간이 아닌 앱이 시작 되 자마자 모듈의 잘못된 구성이 표시됩니다. 모듈을 증 분식으로로드하는 것도 가능하지만 응용 프로그램 논리를 초기화하기 전에 모듈 로딩을 제한하여 DI의 정신을 고수하면 잘못된 바인딩을 앱 시작에 성공적으로 격리 할 수 ​​있습니다. 그러나 서비스 로케이터 안티 패턴으로 구성하면 이러한 잘못된 바인딩은 놀랍습니다.
k4vin

@RichardTingle 컴파일러에서 제공하는 안전망과 결코 유사하지는 않지만 상자에 표시된대로 올바르게 사용되는 도구 인 DI는 해당 런타임 오류가 앱 초기화로 제한된다는 것을 이해합니다. 그런 다음 앱 초기화를 DI 모듈에 대한 일종의 컴파일 단계로 볼 수 있습니다. 내 경험에 따르면 대부분의 경우 응용 프로그램이 시작되면 바인딩이 잘못되거나 참조가 잘못되지 않습니다. 추신 - 전 C #으로해서 Ninject을 사용하고
k4vin

@RichardTingle-나는 당신에게 동의하지만 느슨하게 결합되어 테스트 가능한 코드를 얻는 것은 절충입니다. k4vin이 말했듯이 초기화시 누락 된 종속성이 발견되고 인터페이스를 사용하면 컴파일 시간 오류가 여전히 도움이됩니다.
안드레이 에퓨 어

1
목록에 "코드를 깨끗하게 유지해야합니다"를 추가하겠습니다. 코드없이 코드를 광범위하게 테스트하기 전에 등록을 삭제할 수 있는지 알 수 없습니다.
페라 콜로

24

DI가없는 코드는 스파게티 코드에 얽히게 될 위험이 잘 알려져 있습니다. 일부 증상은 클래스와 방법이 너무 커서 너무 많고 쉽게 변경, 분류, 리팩토링 또는 테스트 할 수 없다는 것입니다.

DI가 많이 사용 된 코드 는 각각의 작은 클래스가 개별 라비올리 너겟과 같은 라비올리 코드 일 수 있습니다. 하나의 작은 일만하고 단일 책임 원칙 을 고수하는 것이 좋습니다. 그러나 스스로 클래스를 살펴보면 시스템 전체가 무엇을하는지 알기가 어렵습니다. 왜냐하면 이것은 수많은 작은 부분이 모두 어떻게 맞는지에 달려 있기 때문입니다. 단지 작은 것의 큰 더미처럼 보입니다.

큰 클래스 내에서 큰 비트의 결합 된 코드의 스파게티 복잡성을 피함으로써 간단한 작은 클래스가 많고 이들 간의 상호 작용이 복잡한 또 다른 종류의 복잡성의 위험이 있습니다.

나는 이것이 치명적인 단점이라고 생각하지 않습니다-DI는 여전히 매우 가치가 있습니다. 한 가지만하는 작은 클래스를 가진 어느 정도의 라비올리 스타일이 좋을 것입니다. 초과하여도 스파게티 코드만큼 나쁘지 않다고 생각합니다. 그러나 너무 멀리 갈 수 있다는 것을 아는 것이 피하는 첫 단계입니다. 그것을 피하는 방법에 대한 토론 링크를 따르십시오.


1
그렇습니다. 나는 "라비올리 코드"라는 용어를 좋아합니다. 나는 DI의 단점에 대해 이야기 할 때 더 오래 표현합니다. 여전히 DI가없는 Java로 실제 프레임 워크 또는 응용 프로그램을 개발하는 것을 상상할 수 없습니다.
Howard M. Lewis Ship 18

13

자체 개발 솔루션이있는 경우 생성자의 얼굴에 종속성이 있습니다. 또는 메서드 매개 변수로 다시 발견하기가 어렵지 않을 수도 있습니다. 프레임 워크 관리 종속성은 극단적으로 취하면 마술처럼 보일 수 있습니다.

그러나 너무 많은 클래스에 종속성이 너무 많으면 클래스 구조가 망가 졌다는 명백한 신호입니다. 따라서 의존성 주입 (자체 또는 프레임 워크 관리)을 통해 어두운 곳에 숨겨져있을 수있는 눈부신 디자인 문제를 해결할 수 있습니다.


두 번째 요점을 더 잘 설명하기 위해이 기사 ( 원본 ) 에서 발췌 한 내용은 컴퓨터 시스템뿐만 아니라 시스템 구축의 근본적인 문제라고 진심으로 믿습니다.

대학 캠퍼스를 설계하려고한다고 가정하십시오. 디자인의 일부를 학생과 교수에게 위임해야합니다. 그렇지 않으면 물리학 건물이 물리학 사람들에게 적합하지 않습니다. 어떤 물리학 자들이 스스로 그것을 스스로해야하는지에 대해 아는 건축가는 없습니다. 그러나 모든 방의 디자인을 탑승자에게 위임 할 수는 없습니다. 왜냐하면 거대한 잔해 더미를 얻을 수 있기 때문입니다.

전반적인 디자인의 일관성과 조화를 유지하면서 모든 수준의 대규모 계층 구조를 통해 디자인에 대한 책임을 어떻게 분배 할 수 있습니까? 이것은 Alexander가 해결하려는 건축 설계 문제이지만 컴퓨터 시스템 개발의 근본적인 문제이기도합니다.

DI가이 문제를 해결합니까? 없음 . 그러나 모든 방을 설계하는 책임을 입주자에게 위임하려는 경우 명확하게 볼 수 있습니다.


13

나 DI와 조금 어색해 만드는 것은 모든 주입 객체가 있다는 가정이다 인스턴스화에 도착하는 저렴한부작용을 생산하지 의존성이 너무 자주는 연관된 인스턴스 비용을 능가하는 사용 또는 -.

이것이 중요한 경우 는 소비 클래스 내에서 종속성이 자주 사용 되지 않는 경우입니다 . 같은 것 IExceptionLogHandlerService입니다. 분명히, 이와 같은 서비스는 클래스 내에서 거의 호출되지 않습니다. (아마도 :)) 아마도 로그 될 필요가있는 예외에 대해서만 가능합니다. 그러나 정식 생성자 주입 패턴 ...

Public Class MyClass
    Private ReadOnly mExLogHandlerService As IExceptionLogHandlerService

    Public Sub New(exLogHandlerService As IExceptionLogHandlerService)
        Me.mExLogHandlerService = exLogHandlerService
    End Sub

     ...
End Class

...이 서비스의 "실시간"인스턴스를 제공해야하며, 해당 서비스를 제공하는 데 필요한 비용 / 부수 효과를 손상시킵니다. 그렇지 않을 수도 있지만,이 종속성 인스턴스를 구성 할 때 서비스 / 데이터베이스 적중 또는 구성 파일 조회가 포함되거나 폐기 될 때까지 리소스를 잠그는 경우 어떻게해야합니까? 이 서비스가 필요에 따라, 서비스 위치 또는 공장에서 생성 된 경우 (모두 자체 문제가있는 경우) 필요한 경우에만 건설 비용을 부담하게됩니다.

이제는 객체를 구성하는 것이 저렴하고 부작용을 일으키지 않는다는 것이 일반적으로 인정되는 소프트웨어 설계 원칙입니다 . 그리고 그것은 좋은 개념이지만 항상 그런 것은 아닙니다. 그러나 전형적인 생성자 주입을 사용하려면 기본적으로 이것이 필요합니다. 종속성 구현을 만들 때 DI를 염두에두고 디자인해야합니다. 어딘가에 이점을 얻기 위해 객체 구성을 더 비싸게 만들었을 수도 있지만,이 구현을 도입하려는 경우 해당 디자인을 다시 고려해야 할 수도 있습니다.

그건 그렇고, 특정 기술은 주입 된 종속성의 지연로드 (lazy-loading)를 허용하여 클래스를 Lazy<IService>종속성으로 클래스에 제공 함으로써이 정확한 문제를 완화시킬 수 있습니다 . 이것은 의존적 인 객체의 생성자를 변경하고 객체 생성 비용과 같은 구현 세부 사항을 훨씬 더 잘 인식하게 할 것입니다.


1
나는 당신이 무슨 말을하는지 이해하지만, 당신이 "의존 구현을 만들 때, 당신은 DI를 염두에두고 디자인해야합니다"라고 말하는 것이 공정하다고 생각하지 않습니다-더 정확한 진술은 "DI 작품 인스턴스화 비용, 건설 중 부작용 또는 고장 모드 부족에 관한 모범 사례로 구현 된 클래스와 함께 사용하는 경우 가장 좋습니다. " 최악의 경우, 항상 처음 사용할 때까지 실제 객체의 할당을 지연시키는 지연 프록시 구현을 삽입 할 수 있습니다.
Jolly Roger

1
최신 IoC 컨테이너를 사용하면 특정 추상 유형 / 인터페이스 (항상 고유, 싱글 톤, http 범위 등)에 대해 객체의 수명을 지정할 수 있습니다. Func <T> 또는 Lazy <T>를 사용하여 느리게 인스턴스화하기위한 팩토리 메소드 / 대리자를 제공 할 수도 있습니다.
Dmitry S.

에 딱 맞다. 종속성을 인스턴스화하면 메모리 비용이 불필요합니다. 나는 보통 호출 될 때만 클래스를 인스턴스화하는 게터와 함께 "lazy-loading"을 사용합니다. 인스턴스화 된 종속성을 허용하는 후속 생성자와 매개 변수없이 기본 생성자를 제공 할 수 있습니다. 첫 번째 경우, 예를 들어 IErrorLogger를 구현하는 클래스 this.errorLogger.WriteError(ex)는 try / catch 문에서 오류가 발생할 때만 인스턴스화됩니다 .
존 Bonfardeci

12

실제로 코드를 분리하지 않고 의존성 주입을 구현하여 코드를 분리했다는 환상. 나는 그것이 DI에 대해 가장 위험한 것이라고 생각합니다.


11

이것은 더 이상 이쑤시개입니다. 그러나 의존성 주입의 단점 중 하나는 개발 도구가 코드를 추론하고 탐색하는 것이 조금 더 어렵다는 것입니다.

특히 코드에서 메소드 호출을 Control-Click / Command-Click하면 구체적인 구현 대신 인터페이스에서 메소드 선언으로 이동합니다.

이것은 느슨하게 결합 된 코드 (인터페이스에 의해 설계된 코드)의 단점이며, 의존성 주입을 사용하지 않더라도 (즉, 팩토리를 사용하는 경우에도) 적용됩니다. 그러나 의존성 주입의 출현은 실제로 느슨하게 결합 된 코드를 대중에게 장려했기 때문에 언급 할 것이라고 생각했습니다.

또한 느슨하게 결합 된 코드의 이점이 이보다 훨씬 크기 때문에이를 nitpick이라고 부릅니다. 나는 이것이 의존성 주입을 도입하려고 할 때 얻을 수있는 일종의 푸시 백이라는 것을 알기 위해 오랫동안 노력했지만.

사실, 나는 의존성 주입을 위해 찾을 수있는 모든 "다운 사이드"에 대해 그보다 훨씬 더 많은 업사이드를 찾을 수 있다고 추측하고 싶었다.


5
resharper가있는 Ctrl-shift-B를 사용하면 구현할 수 있습니다
adrianm

1
그러나 다시 이상적인 세계에서 모든 것의 적어도 두 가지 구현, 즉 하나 이상의 실제 구현과 단위 테스트를위한 모의 구현을
갖지 않을 것입니다.

1
Eclipse에서 Ctrl을 누른 상태에서 메소드 호출 코드를 가리키면 선언 또는 구현을 여는 메뉴가 표시됩니다.
Sam Hasler 2019

인터페이스 정의에있는 경우 메소드 이름을 강조 표시하고 Ctrl + T를 눌러 해당 메소드의 유형 계층 구조를 불러 오십시오. 해당 메소드의 모든 구현자가 유형 계층 구조 트리에 표시됩니다.
qualidafial

10

생성자 기반 의존성 주입 (마법의 "frameworks"의 도움없이)은 OO 코드를 구성하는 깨끗하고 유익한 방법입니다. 내가 본 최고의 코드베이스에서 Martin Fowler의 다른 전 동료와 함께 보낸 몇 년 동안이 방법으로 작성된 대부분의 좋은 수업에는 단일 doSomething방법 이 있음을 알게되었습니다 .

따라서 주요 단점은 일단 함수 프로그래밍의 이점을 얻기 위해 클래스로 클로저를 작성하는 어수선한 OO 방식의 OO 방식이라는 것을 알게되면 OO 코드를 작성하려는 동기가 빠르게 증발 할 수 있다는 것입니다.


1
생성자 기반의 문제점은 항상 생성자 인수를 더 추가해야한다는 것입니다.
Miguel Ping

1
ㅋ. 그렇게 많이하면 곧 코드가 깨져서 리팩토링 될 것입니다. 게다가 ctrl-f6는 그렇게 어렵지 않습니다.
time4tea

물론 그 반대의 경우도 마찬가지입니다. OO 프로그래밍의 이점을 얻기 위해 클래스를 클로저로 작성하는
번거롭고

몇 년 전 저는 Perl에서 클로저를 사용하여 객체 시스템을 구축했습니다. 따라서 당신이 말하는 것을 얻을 수 있지만 데이터 캡슐화 는 OO 프로그래밍 의 독특한 이점 이 아니므 로 OO의 이러한 유익한 기능이 무엇인지 명확하지 않습니다. 기능적인 언어로 얻을 수있는
sanityinc

9

생성자 주입이 큰 추악한 생성자로 이어질 수 있다는 것을 알았습니다 (그리고 코드베이스 전체에서 사용합니다-아마도 객체가 너무 세분화되어 있습니까?). 또한 생성자 주입을 사용하면 끔찍한 순환 종속성으로 끝나기 때문에 (매우 드물지 만) 좀 더 복잡한 시스템에서 여러 번의 종속성 주입으로 준비 상태 수명주기가 필요하다는 것을 알 수 있습니다.

그러나 일단 객체가 구성되면 단위 테스트 환경에 있는지 또는 일부 IOC 컨테이너에로드되었는지 여부에 관계없이 setter 주입보다 construtor 주입을 선호합니다. 로터리의 일종으로, 내가 느끼는 것이 세터 주입의 주요 단점이라고 말하는 것입니다.

(주석으로, 나는 전체 주제가 상당히 "종교적"이라고 생각하지만, 당신의 마일리지는 개발팀의 기술 열렬한 수준에 따라 달라질 것입니다!)


8
큰 추악한 생성자가있는 경우 클래스가 커지고 종속성이 많을 수 있습니까?
Robert

4
그것은 내가 즐겁게 기꺼이 할 수있는 가능성입니다! ... 슬프게도, 나는 동료 검토를 할 수있는 팀이 없습니다. 바이올린은 배경에서 부드럽게 연주 한 다음 화면이 검게 변합니다.
James B

1
Mark Seeman이 "당신의 경력에 ​​위험 할 수 있습니다"위에서 슬프 듯이 ...
Robert

나는 여기에 표현이 실마리가 될 수있는 단서가 없었습니다. : P
Anurag

@ 로버트 나는 견적을 찾으려고 노력하고 있는데, 위의 그는 어디에서 말했습니까?
liang

8

IOC 컨테이너없이 DI를 사용하는 경우 가장 큰 단점은 코드에 실제로 얼마나 많은 종속성이 있는지와 모든 것이 실제로 얼마나 밀접하게 결합되어 있는지 빠르게 확인할 수 있다는 것입니다. ( "그러나 나는 그것이 좋은 디자인이라고 생각했다!") 자연스럽게 진보하는 것은 배우고 구현하는데 약간의 시간이 걸릴 수있는 IOC 컨테이너를 향한 것이다. (WPF 학습 곡선만큼 나쁘지는 않지만 무료는 아니다 어느 한 쪽). 마지막 단점은 일부 개발자가 선량 단위 테스트에 정직하게 글을 작성하기 시작하고이를 파악하는 데 시간이 걸린다는 것입니다. 반나절 만에 무언가를 알아낼 수 있었던 개발자들은 모든 의존성을 조롱하는 방법을 알아 내기 위해 갑자기 이틀을 보낼 것입니다.

Mark Seemann의 답변과 마찬가지로 결론은 약간의 코드를 함께 해킹하여 문 밖으로 / 생산하기 전에 더 나은 개발자가되는 데 시간을 소비한다는 것입니다. 당신의 사업은 어느 쪽이겠습니까? 당신 만이 대답 할 수 있습니다.


좋은 지적. 품질이 낮거나 빠른 배송 대 품질이 좋지 않은 배송입니다. 단점은? DI는 마법이 아니며, 그것이 기대되는 단점입니다.
liang

5

DI는 기술 또는 패턴이며 프레임 워크와 관련이 없습니다. 종속성을 수동으로 연결할 수 있습니다. DI는 SR (단일 책임) 및 SoC (문제 분리)를 도와줍니다. DI는 더 나은 디자인으로 이어집니다. 내 관점과 경험에서 단점은 없습니다 . 다른 패턴과 마찬가지로 잘못하거나 오용 할 수 있습니다 (그러나 DI의 경우는 매우 어렵습니다).

프레임 워크를 사용하여 DI를 레거시 응용 프로그램에 원칙으로 도입하는 경우 가장 큰 실수는 서비스 로케이터로 오용하는 것입니다. DI + Framework 자체는 훌륭하고 내가 본 모든 곳에서 상황을 개선했습니다! 조직의 관점에서 볼 때, 모든 새로운 프로세스, 기술, 패턴, ...에 공통적 인 문제가 있습니다.

  • 당신은 당신이 팀을 훈련해야
  • 애플리케이션을 변경해야합니다 (위험 포함)

일반적으로 당신은 시간과 돈투자 해야합니다. 그 옆에 실제로 단점이 없습니다!


3

코드 가독성. 종속성이 XML 파일에 숨겨져 있으므로 코드 흐름을 쉽게 파악할 수 없습니다.


9
의존성 주입을 위해 XML 구성 파일을 사용할 필요가 없습니다. 코드에서 구성을 지원하는 DI 컨테이너를 선택하십시오.
Håvard S

8
의존성 주입이 반드시 XML 파일을 의미하지는 않습니다. Java에서는 여전히 그렇습니다.하지만 .NET에서는 몇 년 전에이 커플 링을 포기했습니다.
Mark Seemann

6
또는 DI 컨테이너를 전혀 사용하지 마십시오.
Jesse Millikan

코드가 DI를 사용하고 있음을 알고 있으면 누가 종속성을 설정하는지 쉽게 알 수 있습니다.
Bozho

Java에서 Google의 Guice에는 XML 파일이 필요하지 않습니다.
Epaga

2

두가지:

  • 구성이 유효한지 확인하기 위해 추가 도구 지원이 필요합니다.

예를 들어, IntelliJ (Commercial Edition)는 스프링 구성의 유효성 검사를 지원하며 구성의 유형 위반과 같은 오류를 표시합니다. 이러한 종류의 도구 지원이 없으면 테스트를 실행하기 전에 구성이 유효한지 확인할 수 없습니다.

이것이 '케이크'패턴 (스칼라 커뮤니티에 알려진 것처럼)이 좋은 아이디어 인 이유 중 하나입니다. 구성 요소 간의 배선은 유형 검사기로 확인할 수 있습니다. 주석이나 XML에는 이점이 없습니다.

  • 프로그램의 전역 정적 분석은 매우 어렵습니다.

Spring이나 Guice와 같은 프레임 워크는 컨테이너가 생성 한 객체 그래프의 모양을 정적으로 결정하기 어렵습니다. 컨테이너가 시작될 때 오브젝트 그래프를 작성하지만 / w // 작성 될 오브젝트 그래프를 설명하는 유용한 API는 제공하지 않습니다.


1
이것은 절대 황소입니다. 의존성 주입은 개념으로, 까다로운 프레임 워크가 필요하지 않습니다. 당신이 필요한 전부는 새로운 것입니다. 도랑 스프링과 그 쓰레기를 모두 사용하면 도구가 제대로 작동하고 코드를 훨씬 더 잘 리팩토링 할 수 있습니다.
time4tea

사실 좋은 지적입니다. 패턴이 아닌 DI 프레임 워크의 문제에 대해 이야기하고 있다는 것이 더 분명했습니다. 내가 대답했을 때 질문에 대한 설명을 놓쳤을 수도 있습니다 (당시 거기에 있다고 가정).
Martin Ellis

아 어떤 경우에 나는 행복하게 나의 성가심을 철회한다. 사과.
time4tea

2

정적 타이핑 문제를 해결하기 위해 기술을 지속적으로 사용하는 경우 정적 타입 언어의 이점이 크게 줄어들 것 같습니다. 방금 인터뷰 한 대형 Java 상점 중 하나는 정적 코드 분석으로 빌드 종속성을 매핑하는 것입니다.


1

IoC 컨테이너는 종속성을 적절한 방식으로 해결해야하고 때로는 여러 번 반복해야하기 때문에 앱 시작 시간을 늘릴 수 있습니다.


3
DI 컨테이너는 수치를 나타 내기 위해 초당 수천 개의 종속성을 해결해야합니다. DI 컨테이너를 사용하면 지연된 인스턴스화가 가능합니다. ( codinginstinct.com/2008/05/… ) 성능이 큰 응용 프로그램에서도 문제가되지 않아야합니다. 또는 최소한 성능 문제를 해결할 수 있어야하며 IoC 및 해당 프레임 워크에 대해 결정해야하는 이유가되어서는 안됩니다.
Robert
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.