[xml] 의존성 주입 컨테이너의 이점은 무엇입니까?

의존성 주입 자체의 이점을 이해합니다. 예를 들어 Spring을 보자. 또한 AOP, 다른 종류의 도우미 등과 같은 다른 Spring 기능의 이점을 이해합니다. 다음과 같은 XML 구성의 이점이 무엇인지 궁금합니다.

<bean id="Mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
  <property name="girlfriend" ref="Mary"/>
</bean>

다음과 같은 일반 오래된 자바 코드와 비교 :

Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);

디버깅이 더 쉽고 컴파일 시간이 확인되며 java 만 아는 사람이라면 누구나 이해할 수 있습니다. 그렇다면 의존성 주입 프레임 워크의 주요 목적은 무엇일까요? (또는 그 이점을 보여주는 코드입니다.)


UPDATE :
의에서 케이스

IService myService;// ...
public void doSomething() {
  myService.fetchData();
}

IoC 프레임 워크가 둘 이상의 myService 구현을 주입 할 것인지 추측 할 수 있습니까? 주어진 인터페이스의 구현이 하나 뿐이고 IoC 컨테이너가 자동으로이를 사용하도록 결정하면 두 번째 구현이 나타난 후 중단됩니다. 그리고 의도적으로 인터페이스의 가능한 구현이 하나만있는 경우이를 삽입 할 필요가 없습니다.

IoC의 이점을 보여주는 작은 구성을 보는 것은 정말 흥미로울 것입니다. 나는 한동안 Spring을 사용 해왔고 그러한 예를 제공 할 수 없다. 그리고 내가 사용하는 최대 절전 모드, dwr 및 기타 프레임 워크의 이점을 보여주는 단일 행을 표시 할 수 있습니다.


업데이트 2 :
IoC 구성을 다시 컴파일하지 않고도 변경할 수 있음을 알고 있습니다. 정말 좋은 생각인가요? 누군가가 재 컴파일하지 않고 DB 자격 증명을 변경하려는 경우 이해할 수 있습니다. 개발자가 아닐 수도 있습니다. 실제로 개발자가 아닌 다른 사람이 IoC 구성을 얼마나 자주 변경합니까? 개발자에게는 구성을 변경하는 대신 특정 클래스를 다시 컴파일하려는 노력이 없다고 생각합니다. 그리고 개발자가 아닌 경우에는 그의 삶을 더 쉽게 만들고 더 간단한 구성 파일을 제공하고 싶을 것입니다.


업데이트 3 :

인터페이스와 구체적인 구현 간의 매핑에 대한 외부 구성

그것을 확장하는 데 무엇이 그렇게 좋은가요? 모든 코드를 외부로 만들지는 않지만, 확실히 할 수 있습니다. ClassName.java.txt 파일에 넣고 즉시 수동으로 읽고 컴파일합니다. 와우, 재 컴파일을 피했습니다. 왜 컴파일을 피해야합니까?!

절차 코드가 아닌 선언적으로 매핑을 제공하므로 코딩 시간이 절약됩니다.

때때로 선언적 접근 방식이 시간을 절약한다는 것을 이해합니다. 예를 들어 빈 속성과 DB 열 사이의 매핑을 한 번만 선언하고 최대 절전 모드에서는 HSQL을 기반으로 SQL을로드, 저장, 빌드하는 동안이 매핑을 사용합니다. 여기서 선언적 접근 방식이 작동합니다. Spring의 경우 (제 예에서) 선언에는 더 많은 줄이 있고 해당 코드와 동일한 표현력을 가졌습니다. 이러한 선언이 코드보다 짧은 예가 있다면보고 싶습니다.

Inversion of Control 원칙을 사용하면 실제 구현을 가짜 구현으로 대체 할 수 있기 때문에 쉽게 단위 테스트가 가능합니다 (예 : SQL 데이터베이스를 메모리 내 구현으로 대체).

제어의 역전 이점을 이해합니다 (여기서 논의한 디자인 패턴을 종속성 주입이라고 부르는 것을 선호합니다. 왜냐하면 IoC가 더 일반적이기 때문입니다-많은 종류의 제어가 있고 그중 하나만 반전하고 있습니다-초기화 제어). 나는 왜 누군가가 그것을 위해 프로그래밍 언어가 아닌 다른 것을 필요로하는지 물었다. 코드를 사용하여 실제 구현을 가짜 구현으로 대체 할 수 있습니다. 그리고이 코드는 구성과 동일한 것을 표현합니다. 단지 가짜 값으로 필드를 초기화합니다.

mary = new FakeFemale();

DI의 이점을 이해합니다. 동일한 작업을 수행하는 코드를 구성하는 것과 비교하여 외부 XML 구성에 의해 추가되는 이점을 이해하지 못합니다. 나는 컴파일을 피해야한다고 생각하지 않는다. 나는 매일 컴파일하고 여전히 살아있다. DI의 구성은 선언적 접근의 나쁜 예라고 생각합니다. 선언은 한 번 선언하고 다른 방식으로 여러 번 사용하면 유용 할 수 있습니다.-hibernate cfg와 같이 빈 속성과 DB 컬럼 간의 매핑이 검색 쿼리 저장,로드, 빌드 등에 사용됩니다. Spring DI 구성은 쉽게 번역 할 수 있습니다. 이 질문의 시작 부분과 같이 코드를 구성 할 수 있습니까? 그리고 빈 초기화를 위해서만 사용되는 것 아닌가요? 이것은 선언적 접근 방식이 여기에 아무것도 추가하지 않는다는 것을 의미합니까?

hibernate mapping을 선언 할 때, hibernate에 몇 가지 정보를 제공하고이를 기반으로 작동합니다. 무엇을해야하는지 알려주지 않습니다. 봄의 경우, 내 선언은 봄에 정확히 무엇을해야하는지 알려줍니다. 왜 그것을 선언하고, 왜 그렇게하지 않습니까?


마지막 업데이트 :
여러분, 많은 답변이 의존성 주입에 대해 말해주고 있습니다. 문제는 코드를 초기화하는 대신 DI 구성의 목적에 관한 것입니다. 코드 초기화가 더 짧고 명확하다고 생각하는 경향이 있습니다. 지금까지 내 질문에 대한 유일한 대답은 구성이 변경 될 때 재 컴파일을 피한다는 것입니다. 나는 다른 질문을 게시해야한다고 생각한다. 그것은 나에게 큰 비밀이기 때문에 왜이 경우 컴파일을 피해야 하는가.



답변

나 자신을 위해 IoC를 사용하고 외부 구성을 사용하는 주된 이유 중 하나는 다음 두 영역에 있습니다.

  • 테스팅
  • 생산 유지

테스팅

테스트를 3 개의 시나리오로 분할하는 경우 (대규모 개발에서는 매우 일반적 임) :

  1. 단위 테스트
  2. 통합 테스트
  3. 블랙 박스 테스트

마지막 두 테스트 시나리오 (통합 및 블랙 박스)에 대해 수행하려는 작업은 응용 프로그램의 어떤 부분도 다시 컴파일하지 않는 것입니다.

테스트 시나리오에서 구성을 변경해야하는 경우 (예 : 다른 구성 요소를 사용하여 뱅킹 통합을 모방하거나 성능 부하를 수행) 이는 쉽게 처리 할 수 ​​있습니다 (이는 DI 측면을 구성하는 이점이 있습니다. 그래도 IoC.

또한 앱이 여러 사이트 (서버 및 구성 요소 구성이 서로 다름)에서 사용되거나 라이브 환경에서 구성이 변경된 경우 이후 테스트 단계를 사용하여 앱이 이러한 변경 사항을 처리하는지 확인할 수 있습니다.

생산

개발자는 프로덕션 환경 (특히 앱이 여러 고객 또는 별도의 사이트에 배포되는 경우)을 제어 할 수 없으며 (이어서도 안됩니다) IoC 및 외부 구성을 모두 사용하는 것이 나에게 진정한 이점입니다. , 개발자에게 돌아가 테스트를 거치지 않고 라이브 환경을 조정하고 조정하는 것은 인프라 / 프로덕션 지원에 달려 있기 때문입니다 (구성 요소를 이동하는 것뿐이면 비용이 더 많이 듭니다).

요약

IoC의 외부 구성이 다른 (개발자가 아닌)에게 응용 프로그램을 구성 할 수있는 권한을 제공함으로써 얻을 수있는 주요 이점은 내 경험상 제한된 상황에서만 유용합니다.

  • 응용 프로그램은 환경이 다른 여러 사이트 / 클라이언트에 배포됩니다.
  • 프로덕션 환경 및 설정에 대한 제한된 개발 제어 / 입력.
  • 시나리오 테스트.

실제로 나는 환경을 제어 할 수있는 무언가를 개발할 때에도 실행될 것임을 발견했습니다. 시간이 지남에 따라 다른 사람에게 구성을 변경할 수있는 기능을 제공하는 것이 더 좋습니다.

  • 개발할 때 언제 변경 될지 알 수 없습니다 (앱이 너무 유용해서 회사에서 다른 사람에게 판매합니다).
  • 좋은 구성 모델을 설정하고 사용하여 처리 할 수있는 약간의 변경이 요청 될 때마다 코드를 변경하는 데 집착하고 싶지 않습니다.

참고 : 응용 프로그램은 실행 파일뿐 아니라 전체 솔루션을 참조하므로 응용 프로그램을 실행하는 데 필요한 모든 파일이 있습니다 .


답변

의존성 주입은 객체 위임이 일반적으로 객체 상속보다 더 유용한 디자인 패턴이라는 관찰에 뿌리를 둔 코딩 스타일입니다 (즉, 객체가있는 관계가 객체가있는 관계보다 더 유용합니다). 그러나 DI가 작동하려면 객체 인터페이스를 만드는 다른 요소가 필요합니다. 이 두 가지 강력한 디자인 패턴을 결합한 소프트웨어 엔지니어는 유연하게 느슨하게 결합 된 코드를 생성 할 수 있다는 것을 빠르게 깨달았고 따라서 종속성 주입 개념이 탄생했습니다. 그러나 DI가 실제로 시작된 것은 특정 고급 언어에서 객체 반사를 사용할 수있게되기 전까지는 아니 었습니다. 반사 구성 요소는 오늘날 대부분의 핵심입니다.

언어는 일반적인 객체 지향 프로그래밍 기술과 객체 인터페이스 및 객체 리플렉션 (예 : Java 및 C #)에 대한 지원을 모두 제공해야합니다. C ++ 시스템에서 DI 패턴을 사용하여 프로그램을 빌드 할 수 있지만 적절한 언어 내에서 리플렉션 지원이 부족하여 애플리케이션 서버 및 기타 DI 플랫폼을 지원하지 못하므로 DI 패턴의 표현성이 제한됩니다.

DI 패턴을 사용하여 구축 된 시스템의 강점 :

  1. DI 코드는 ‘종속’기능이 잘 정의 된 인터페이스로 외삽되기 때문에 재사용이 훨씬 더 쉬워서 적절한 애플리케이션 플랫폼에서 구성을 처리하는 별도의 객체를 마음대로 다른 객체에 연결할 수 있습니다.
  2. DI 코드는 테스트하기가 훨씬 쉽습니다. 객체가 표현하는 기능은 애플리케이션 로직에서 기대하는 인터페이스를 구현하는 ‘모의’객체를 빌드하여 블랙 박스에서 테스트 할 수 있습니다.
  3. DI 코드가 더 유연합니다. 그것은 본질적으로 느슨하게 결합 된 코드입니다. 이를 통해 프로그래머는 한쪽 끝에서 필요한 인터페이스와 다른 쪽 끝에서 표현 된 인터페이스를 기반으로 객체가 연결되는 방식을 선택하고 선택할 수 있습니다.
  4. DI 개체의 외부 (Xml) 구성은 다른 사람들이 예상치 못한 방향으로 코드를 사용자 지정할 수 있음을 의미합니다.
  5. 외부 구성은 또한 객체 초기화 및 객체 상호 의존성 관리의 모든 문제를 애플리케이션 서버에서 처리 할 수 ​​있다는 점에서 관심 패턴의 분리입니다.
  6. DI 패턴을 사용하기 위해 외부 구성이 필요하지 않습니다. 단순한 상호 연결의 경우 작은 빌더 개체가 종종 적합합니다. 둘 사이의 유연성에는 절충안이 있습니다. 빌더 개체는 외부에서 볼 수있는 구성 파일만큼 유연하지 않습니다. DI 시스템 개발자는 구성 파일에 표현 된 개체 구성에 대한 소규모의 미세한 입자 제어가 혼란과 유지 관리 비용을 증가시킬 수 있다는 점에주의하면서 편의성보다 유연성의 장점을 평가해야합니다.

확실히 DI 코드는 더 복잡해 보이지만, 객체가 다른 객체에 주입되도록 구성하는 모든 XML 파일을 갖는 단점은 어려워 보입니다. 그러나 이것이 DI 시스템의 핵심입니다. 일련의 구성 설정으로 코드 개체를 혼합하고 일치시키는 기능을 사용하면 최소한의 코딩으로 타사 코드를 사용하여 복잡한 시스템을 구축 할 수 있습니다.

질문에 제공된 예제는 적절하게 팩터링 된 DI 개체 라이브러리가 제공 할 수있는 표현력의 표면에 대해서만 다룹니다. 약간의 연습과 많은 자기 훈련을 통해 대부분의 DI 실무자들은 애플리케이션 코드의 100 % 테스트 범위를 갖는 시스템을 구축 할 수 있다는 것을 알게됩니다. 이 원 포인트만으로는 대단합니다. 이것은 수백 줄의 코드로 이루어진 소규모 애플리케이션의 100 % 테스트 범위가 아니라 수십만 줄의 코드로 구성된 애플리케이션의 100 % 테스트 범위입니다. 이 수준의 테스트 가능성을 제공하는 다른 디자인 패턴을 설명 할 수 없습니다.

단 10 줄의 코드를 적용하는 것이 여러 객체와 일련의 XML 구성 파일보다 이해하기 쉽다는 점에서 맞습니다. 그러나 가장 강력한 디자인 패턴과 마찬가지로 시스템에 새로운 기능을 계속 추가하면 이점을 얻을 수 있습니다.

요컨대 대규모 DI 기반 애플리케이션은 디버그하기 쉽고 이해하기 쉽습니다. Xml 구성은 ‘컴파일 시간 검사’가 아니지만이 작성자가 알고있는 모든 응용 프로그램 서비스는 호환되지 않는 인터페이스를 가진 개체를 다른 개체에 삽입하려고하면 개발자에게 오류 메시지를 제공합니다. 그리고 대부분은 알려진 모든 개체 구성을 다루는 ‘확인’기능을 제공합니다. 이는 주입 될 개체 A가 구성된 모든 개체 주입에 대해 개체 B에 필요한 인터페이스를 구현하는지 확인하여 쉽고 빠르게 수행됩니다.


답변

이것은 약간의로드 된 질문이지만, 엄청난 양의 xml 구성이 실제로 많은 이점을 얻지 못한다는 데 동의하는 경향이 있습니다. 나는 내 애플리케이션이 무거운 프레임 워크를 포함하여 가능한 한 종속성에 대해 가벼워지는 것을 좋아합니다.

그들은 코드를 많이 단순화하지만 문제를 추적하는 것을 다소 어렵게 만드는 복잡성에 오버 헤드가 있습니다 (나는 이러한 문제를 직접 본 적이 있으며 곧바로 Java를 처리하는 것이 훨씬 더 편안 할 것입니다).

스타일에 따라 조금씩 다르고, 어떤 것에 익숙한 지 … 자신 만의 솔루션을 사용하고 내부를 아는 이점이 있습니까? 아니면 구성이 어려울 때 어려울 수있는 기존 솔루션을 활용하는 것을 좋아합니까? 옳지? 모두 절충안입니다.

그러나 XML 구성은 제 생각에 약간의 관심을 불러 일으키는 부분입니다. 저는 어떤 대가를 치르더라도이를 피하려고합니다.


답변

코드를 데이터로 변경할 수있을 때마다 올바른 방향으로 나아가고 있습니다.

무엇이든 데이터로 코딩한다는 것은 코드 자체가 더 일반적이고 재사용 가능하다는 것을 의미합니다. 또한 데이터가 정확히 맞는 언어로 지정 될 수 있음을 의미합니다.

또한 XML 파일을 GUI 또는 기타 도구로 읽어서 실용적으로 쉽게 조작 할 수 있습니다. 코드 예제로 어떻게 하시겠습니까?

저는 대부분의 사람들이 코드로 구현하는 것을 데이터에 지속적으로 고려하고 있습니다. 그러면 코드가 훨씬 더 깔끔해집니다. 사람들이 데이터가 아닌 코드로 메뉴를 생성한다는 것은 상상할 수없는 일입니다. 코드에서 메뉴를 만드는 것은 상용구 때문에 명백히 잘못된 것입니다.


답변

DI 컨테이너를 사용하는 이유는 단순히 getter 및 setter 인 코드에 수십억 개의 속성을 미리 구성 할 필요가 없기 때문입니다. 정말 new X ()로 모든 것을 하드 코딩 하시겠습니까? 물론 기본값을 가질 수 있지만 DI 컨테이너를 사용하면 싱글 톤을 매우 쉽게 생성 할 수 있으며 코드를 초기화하는 기타 작업이 아니라 코드의 세부 사항에 집중할 수 있습니다.

예를 들어 Spring은 InitializingBean 인터페이스를 구현하고 afterPropertiesSet 메소드를 추가 할 수 있도록합니다 (코드를 Spring에 연결하지 않도록 “init-method”를 지정할 수도 있습니다). 이러한 메서드를 사용하면 클래스 인스턴스의 필드로 지정된 인터페이스가 시작시 올바르게 구성되었는지 확인할 수 있으며 더 이상 getter 및 setter를 null 검사 할 필요가 없습니다 (싱글 톤이 스레드로부터 안전하게 유지되도록 허용한다고 가정). ).

또한 DI 컨테이너를 사용하여 직접 수행하는 대신 복잡한 초기화를 수행하는 것이 훨씬 쉽습니다. 예를 들어 XFire 사용을 지원합니다 (CeltiXFire가 아니라 Java 1.4 만 사용). 이 앱은 Spring을 사용했지만 안타깝게도 XFire의 services.xml 구성 메커니즘을 사용했습니다. 요소 모음이 하나 이상의 인스턴스 대신 0 개 이상의 인스턴스를 가지고 있음을 선언해야 할 때이 특정 서비스에 대해 제공된 XFire 코드 중 일부를 재정의해야했습니다.

Spring Bean 스키마에 정의 된 특정 XFire 기본값이 있습니다. 따라서 Spring을 사용하여 서비스를 구성했다면 Bean을 사용할 수 있습니다. 대신 빈을 사용하는 대신 services.xml 파일에 특정 클래스의 인스턴스를 제공해야했습니다. 이렇게하려면 생성자를 제공하고 XFire 구성에 선언 된 참조를 설정해야했습니다. 내가 변경해야하는 실제 변경은 단일 클래스를 오버로드해야했습니다.

그러나 services.xml 파일 덕분에 4 개의 새 클래스를 생성하여 생성자의 Spring 구성 파일에있는 기본값에 따라 기본값을 설정해야했습니다. Spring 구성을 사용할 수 있었다면 다음과 같이 말할 수 있습니다.

<bean id="base" parent="RootXFireBean">
    <property name="secondProperty" ref="secondBean" />
</bean>

<bean id="secondBean" parent="secondaryXFireBean">
    <property name="firstProperty" ref="thirdBean" />
</bean>

<bean id="thirdBean" parent="thirdXFireBean">
    <property name="secondProperty" ref="myNewBean" />
</bean>

<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />

대신 다음과 같이 보입니다.

public class TheFirstPointlessClass extends SomeXFireClass {
    public TheFirstPointlessClass() {
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    }
}

public class TheSecondPointlessClass extends YetAnotherXFireClass {
    public TheSecondPointlessClass() {
        setFirstProperty(TheThirdPointlessClass());
    }
}

public class TheThirdPointlessClass extends GeeAnotherXFireClass {
    public TheThirdPointlessClass() {
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    }
}

public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
    public WowItsActuallyTheCodeThatChanged() {
    }

    public overrideTheMethod(Object[] arguments) {
        //Do overridden stuff
    }
}

따라서 최종 결과는 하나의 추가 클래스와 몇 가지 간단한 종속성 컨테이너 정보를 달성하는 데 영향을 미치기 위해 코드베이스에 4 개의 추가, 대부분 무의미한 Java 클래스를 추가해야한다는 것입니다. 이것은 “규칙을 증명하는 예외”가 아닙니다. 이것은 규칙입니다. 속성이 DI 컨테이너에 이미 제공되고 특수한 상황에 맞게 속성을 변경하는 경우 코드의 단점 처리가 훨씬 더 깔끔합니다. 더 자주 발생합니다.


답변

나는 당신의 대답을 가지고 있습니다

각 접근 방식에는 분명히 상충 관계가 있지만 외부화 된 XML 구성 파일은 IDE가 아닌 빌드 시스템이 코드를 컴파일하는 데 사용되는 엔터프라이즈 개발에 유용합니다. 빌드 시스템을 사용하여 코드에 특정 값을 삽입 할 수 있습니다. 예를 들어 빌드 버전 (컴파일 할 때마다 수동으로 업데이트해야하는 것이 고통 스러울 수 있음). 빌드 시스템이 일부 버전 제어 시스템에서 코드를 가져 오면 고통이 더 커집니다. 컴파일 타임에 간단한 값을 수정하려면 파일을 변경하고 커밋하고 컴파일 한 다음 각 변경 사항에 대해 매번 되돌려 야합니다. 이는 버전 제어에 커밋하려는 변경 사항이 아닙니다.

빌드 시스템 및 외부 구성과 관련된 기타 유용한 사용 사례 :

  • 다른 빌드를위한 단일 코드베이스에 스타일 / 스타일 시트 삽입
  • 단일 코드베이스에 대해 서로 다른 동적 콘텐츠 세트 (또는 이에 대한 참조) 삽입
  • 다른 빌드 / 클라이언트에 대한 현지화 컨텍스트 삽입
  • 웹 서비스 URI를 백업 서버로 변경 (기본 서버가 다운되는 경우)

업데이트 : 위의 모든 예제는 반드시 클래스에 대한 종속성이 필요하지 않은 것에 대한 것입니다. 그러나 복잡한 개체와 자동화가 모두 필요한 경우를 쉽게 구축 할 수 있습니다. 예를 들면 다음과 같습니다.

  • 웹 사이트의 트래픽을 모니터링하는 시스템이 있다고 상상해보십시오. 동시 사용자 수에 따라 로깅 메커니즘을 켜거나 끕니다. 메커니즘이 꺼져있는 동안 스텁 개체가 그 자리에 놓일 수 있습니다.
  • 사용자 수에 따라 참가자 수에 따라 P2P를 수행하는 기능을 전환하려는 웹 회의 시스템이 있다고 가정 해보십시오.

답변

구성을 변경할 때마다 코드를 다시 컴파일 할 필요가 없습니다. 프로그램 배포 및 유지 관리를 단순화합니다. 예를 들어 구성 파일을 한 번만 변경하면 한 구성 요소를 다른 구성 요소로 바꿀 수 있습니다.