항상 그렇듯이 It Depends ™. 답은 해결하려는 문제에 달려 있습니다. 이 답변에서 나는 일반적인 동기 부여를 다루려고 노력할 것이다.
더 작은 코드 기반 선호
4,000 줄의 Spring 구성 코드가 있다면 코드베이스에 수천 개의 클래스가 있다고 가정합니다.
사실 후에 해결할 수있는 문제는 아니지만 일반적으로 코드베이스가 더 작은 더 작은 응용 프로그램을 선호하는 경향이 있습니다. 예를 들어 Domain-Driven Design을 사용하는 경우 경계 컨텍스트별로 코드베이스를 만들 수 있습니다.
경력의 대부분을 위해 웹 기반 업무용 코드를 작성했기 때문에 제한된 경험을 바탕으로이 조언을하고 있습니다. 데스크톱 응용 프로그램이나 임베디드 시스템 또는 기타를 개발하는 경우 문제를 해결하기가 더 어렵다고 생각할 수 있습니다.
이 첫 번째 조언이 가장 실용적이지 않다는 것을 잘 알고 있지만, 그것이 가장 중요하다고 생각합니다. 코드의 복잡성은 코드베이스의 크기에 따라 비선형 적으로 (지수 적으로) 변합니다.
순수한 DI 선호
이 질문이 기존 상황에 해당한다는 것을 여전히 알고 있지만 Pure DI를 권장 합니다. DI 컨테이너를 사용하지 말고 사용하는 경우 최소한 컨벤션 기반 컴포지션을 구현하는 데 사용하십시오 .
Spring에 대한 실질적인 경험은 없지만 구성 파일 에 의해 XML 파일이 암시 된다고 가정합니다 .
XML을 사용하여 종속성을 구성하는 것은 두 세계에서 최악입니다. 첫째, 컴파일 타임 유형의 안전을 잃지 만 아무것도 얻지 못합니다. XML 구성 파일은 대체하려는 코드만큼 쉽게 커질 수 있습니다.
의존성 주입 구성 파일은 해결해야 할 문제와 비교할 때 구성 복잡성 시계 에서 잘못된 위치를 차지합니다 .
거친 의존성 주입의 경우
거친 의존성 주입의 경우를 만들 수 있습니다. 또한 세분화 된 종속성 주입에 대한 사례를 만들 수도 있습니다 (다음 섹션 참조).
몇 가지 '중앙'종속성을 주입하면 대부분의 클래스는 다음과 같습니다.
public class Foo
{
private readonly Bar bar;
public Foo()
{
this.bar = new Bar();
}
// Members go here...
}
이것은 작성 하기 때문에 클래스 상속보다 Design Patterns 의 선호 오브젝트 구성에 여전히 적합 Foo
합니다 Bar
. 구성을 변경해야하는 경우에 대한 소스 코드를 편집하기 때문에 유지 관리 측면에서 유지 관리가 가능한 것으로 간주 될 수 있습니다 Foo
.
이것은 의존성 주입보다 유지 관리가 거의 불가능합니다. 사실, Bar
의존성 주입 고유의 간접 지시를 따르지 않고 를 사용하는 클래스를 직접 편집하는 것이 더 쉽다고 말하고 싶습니다 .
의존성 주입에 관한 저의 책 의 첫 번째 판에서 , 나는 휘발성과 안정적인 의존성을 구별합니다.
휘발성 종속성은 주입을 고려해야하는 종속성입니다. 그들은 포함
- 컴파일 후 재구성 할 수있는 종속성
- 다른 팀과 병행하여 개발 한 종속성
- 비 결정적 행동 또는 부작용이있는 행동의 의존성
반면에 안정적인 종속성은 잘 정의 된 방식으로 동작하는 종속성입니다. 어떤 의미에서, 당신은이 차이로 인해 거친 의존성 주입이 가능하다고 주장 할 수 있지만, 나는이 책을 썼을 때 그것을 완전히 깨닫지 못했다는 것을 인정해야합니다.
그러나 테스트 관점에서는 단위 테스트가 더 어렵습니다. 당신은 더 이상 단위 테스트 할 수있는 Foo
독립적 Bar
. JB Rainsberger가 설명 했듯이 통합 테스트는 복잡한 조합의 폭발로 인해 어려움을 겪습니다. 4-5 클래스의 통합을 통해 모든 경로를 다루려면 문자 그대로 수만 개의 테스트 사례를 작성해야합니다.
이에 대한 반론은 종종 당신의 임무는 수업을 프로그래밍하지 않는 것입니다. 당신의 임무는 몇 가지 특정 문제를 해결하는 시스템을 개발하는 것입니다. 이것이 BDD ( Behaviour-Driven Development )의 동기 입니다.
이에 대한 또 다른 견해는 DDD가 제시 한 것으로 TD는 테스트로 인한 설계 손상을 초래 한다고 주장합니다 . 그는 또한 거친 통합 테스트를 선호합니다.
소프트웨어 개발에 대한 이러한 관점을 취한다면, 대략적인 의존성 주입이 의미가 있습니다.
세분화 된 의존성 주입의 경우
반면에 세밀한 의존성 주입은 모든 것을 주입하는 것으로 설명 될 수 있습니다 !
거친 의존성 주입에 대한 나의 주요 관심사는 JB Rainsberger가 표현한 비판입니다. 모든 코드 경로를 포괄하기 위해 문자 그대로 수천 또는 수만 개의 테스트 사례를 작성해야하므로 통합 테스트로 모든 코드 경로를 처리 할 수는 없습니다.
BDD의 지지자들은 테스트로 모든 코드 경로를 다룰 필요는 없다는 주장에 대응할 것이다. 비즈니스 가치를 창출하는 제품 만 커버하면됩니다.
그러나 내 경험상 모든 '이국적인'코드 경로는 대량 배포에서도 실행되며 테스트하지 않으면 많은 결함이 발생하여 런타임 예외 (종종 null 참조 예외)가 발생합니다.
이로 인해 모든 객체의 불변량을 분리하여 테스트 할 수 있기 때문에 세분화 된 종속성 주입을 선호했습니다.
기능성 프로그래밍 선호
세밀한 의존성 주입에 의존하는 동안 함수 프로그래밍에 중점을 두었습니다. 그 이유 는 본질적으로 테스트 가능 하기 때문 입니다.
SOLID 코드로 이동 하면할수록 기능이 향상됩니다 . 조만간, 당신도 뛰어들 수 있습니다. 기능적 아키텍처는 포트 및 어댑터 아키텍처 이며 종속성 주입도 시도이며 포트 및 어댑터 입니다. 그러나 Haskell과 같은 언어는 유형 시스템을 통해 해당 아키텍처를 적용한다는 점이 다릅니다.
정적으로 유형화 된 기능 프로그래밍 선호
이 시점에서 필자는 OOP (Object-Oriented Programming)를 기본적으로 포기했지만 OOP의 많은 문제는 본질적으로 개념 자체보다 Java 및 C #과 같은 주류 언어에 본질적으로 연결되어 있습니다.
주류 OOP 언어의 문제점은 테스트되지 않은 런타임 예외로 이어지는 조합 폭발 문제를 피하는 것이 거의 불가능하다는 것입니다. 반면에 Haskell 및 F #과 같이 정적으로 형식이 지정된 언어를 사용하면 형식 시스템에서 많은 결정 지점을 인코딩 할 수 있습니다. 즉, 수천 개의 테스트를 작성하는 대신 컴파일러가 가능한 모든 코드 경로를 처리했는지 여부를 알 수 있습니다 (은색 총알이 아님).
또한 의존성 주입은 작동하지 않습니다 . 진정한 함수형 프로그래밍 은 종속성의 전체 개념을 거부 해야합니다 . 결과는 더 간단한 코드입니다.
요약
C #으로 작업해야한다면, 세밀한 의존성 주입을 선호하는데, 그 이유는 관리 가능한 수의 테스트 사례로 전체 코드베이스를 커버 할 수 있기 때문입니다.
결국 제 동기는 빠른 피드백입니다. 그럼에도 불구하고 단위 테스트 만이 피드백을 얻는 유일한 방법은 아닙니다 .