IoC, 컨테이너를 어디에 두나요?
-
02-07-2019 - |
문제
저는 제가 진행 중인 애완동물 프로젝트에 캐슬 윈저를 사용하고 있습니다.새 객체를 생성하려면 코드의 여러 위치에서 IoC 컨테이너를 호출해야 한다는 것을 깨닫기 시작했습니다.컨테이너에 대한 이러한 종속성은 내 코드를 유지 관리하기 어렵게 만듭니다.
이 문제를 해결하기 위해 제가 사용한 두 가지 솔루션이 있습니다.
저는 객체를 생성해야 하는 애플리케이션 부분에 삽입할 수 있는 컨테이너 주변의 래퍼로 추상 팩토리를 생성하려고 했습니다.이것은 작동하지만 Castle이 자신의 컨테이너를 종속성으로 주입하는 데 어려움을 겪기 때문에 몇 가지 단점이 있습니다.그래서 저는 그것을 손으로 해야 합니다. 이런 종류의 작업은 IoC 컨테이너의 전체 목적을 무효화합니다.
저는 IoC 컨테이너를 래핑하고 중앙 팩토리/저장소로 작동하기 위해 기본 applicationcontroller 클래스를 사용했습니다.이것은 매우 성공적이었지만 이 클래스는 너무 커지고 중앙 신 개체처럼 작동하며 거의 모든 다른 개체에 대한 참조가 있습니다.
두 솔루션 모두 일종의 작업이지만 둘 다 단점이 있습니다.그래서 다른 사람들도 같은 문제를 겪고 더 나은 해결책을 찾았는지 궁금합니다.
편집하다문제는 객체 B에 의존하는 객체 A에 대한 것이 아닙니다.여기서는 일반적으로 생성자 주입을 사용하고 모든 것이 작동합니다.때로는 수명 동안 다양한 수의 B 유형 개체를 생성해야 하는 A 유형 개체가 있습니다.이 작업을 수행하는 방법을 잘 모르겠습니다.
@블레어 콘래드:현재까지 유지보수 문제는 심각하지 않습니다.일부 클래스는 Container.Resolve<>를 호출하는 컨테이너 개체에 의존했습니다.그리고 나는 인프라라고 생각하는 것에 따라 코드를 갖고 싶지 않습니다.나는 아직 시도 중이어서 이 프로젝트를 위해 ninject에서 castle로 전환할 때 많은 코드를 변경해야 한다는 것을 알았습니다.
@꽃들:흠.나는 당신의 주먹 솔루션을 좋아합니다.제가 시도한 두 가지 솔루션에서 작동하는 기능을 결합한 것입니다.나는 여전히 객체에 대해 너무 많이 생각하고 인터페이스/책임에 대해서는 충분하지 않다고 생각합니다.나는 목적에 맞게 구축된 공장을 시도했지만 배후에서 컨테이너를 사용하여 객체를 생성하게 하고 싶었지만 컨테이너를 깨끗한 방식으로 객체로 DI할 수 있는 방법을 찾지 못했습니다.
해결책
적어도 내 애플리케이션에서 종속성 주입의 주요 이점은 상황에 구애받지 않는 코드를 작성할 수 있다는 것입니다.그러한 관점에서 볼 때 두 번째 솔루션은 DI가 제공할 수 있는 이점을 실제로 전복시키는 것처럼 보입니다.'신 객체'가 이를 참조하는 각 클래스에 서로 다른 인터페이스를 노출한다면 그다지 나쁘지 않을 수도 있습니다.하지만 그렇게까지 갔다면 왜 골대까지 끝까지 가져가지 않는지 모르겠습니다.
예:God 객체에는 getFoo() 메소드와 getBar() 메소드가 있습니다.객체 A에는 Foo가 필요하고 객체 B에는 Bar가 필요합니다.A가 Foo 하나만 필요하다면 Foo는 A에 직접 주입되어야 하며 A는 신을 전혀 인식해서는 안 됩니다.그러나 A가 Foos를 계속 생성해야 한다면 A에게 신에 대한 참조를 제공하는 것은 거의 불가피합니다.그러나 하나님에 대한 언급의 유형을 좁힘으로써 하나님을 우회함으로써 입는 피해로부터 자신을 보호할 수 있습니다.God이 FooFactory를 구현하도록 하고 A에 God이 구현한 FooFactory에 대한 참조를 제공하면 여전히 컨텍스트 중립적인 방식으로 A에 코드를 작성할 수 있습니다.이는 코드 재사용 기회를 높이고 God으로의 변경이 예상치 못한 부작용을 일으키지 않을 것이라는 확신을 높여줍니다.예를 들어, God에서 getBar()를 제거할 때 클래스 A가 중단되지 않을 것임을 확신할 수 있습니다.
하지만 ...어쨌든 이러한 모든 인터페이스를 가지려면 컨테이너를 전혀 래핑하는 것보다 특별히 제작된 팩토리 클래스를 작성하고 팩토리를 포함한 모든 객체를 컨테이너 내에 함께 연결하는 것이 더 나을 것입니다.컨테이너는 여전히 팩터리를 구성할 수 있습니다.
다른 팁
IoC.Container.Resolve 또는 ContainerFactory.GetContainer와 같은 정적 클래스를 절대 사용하지 마십시오!
이로 인해 코드가 더 복잡해지고 테스트, 유지 관리, 재사용 및 읽기가 더 어려워집니다.
일반적으로 모든 단일 구성 요소 또는 서비스에는 단 하나의 단일 주입 지점, 즉 생성자(선택적 속성 포함)만 있습니다.그리고 일반적으로 구성 요소나 서비스 클래스는 컨테이너와 같은 존재에 대해 알 수 없습니다.
구성요소가 실제로 내부에 동적 해상도를 가져야 하는 경우(예:이름을 기준으로 예외 처리 정책이나 워크플로를 해결하는 경우) 고려하는 것이 좋습니다. 매우 구체적인 공급자를 통해 IoC 권한을 빌려줍니다.
이에 대한 Nick Blumhardt의 미니 시리즈를 확인해 보는 것이 좋습니다.
"목적에 맞게 구축된 팩토리"의 명확성을 높이 평가하고 직접 사용하기도 하지만 공용 인터페이스(작은 "i")가 새 팩토리 및/또는 새 GetX 메서드에 따라 계속 변경되기 때문에 내 디자인에서는 코드 냄새처럼 느껴집니다. 각 구현마다.제레미 밀러의 글을 읽은 후 이제 IoC 컨테이너 데탕트(Detente)가 시작됩니다., 나는 제네릭을 의심하고 컨테이너 자체를 주입하는 것이 갈 길입니다.
Jeremy의 기사에서 제안한 것과 같은 일종의 IServiceLocator 인터페이스로 Ninject, StructureMap 또는 Windsor를 래핑하겠습니다.그런 다음 원래 제안한 루프에서도 코드의 어느 위치에서나 IServiceLocator를 간단히 반환하는 컨테이너 팩토리를 만드세요.
IServiceLocator container = ContainerFactory.GetContainer();
while( keepLooping )
{
IExample example = container.GetInstance<IExample>();
keepLooping = example.DoWork();
}
컨테이너 팩토리는 항상 동일한 인스턴스를 반환할 수 있으며 IoC 프레임워크 등을 교체할 수 있습니다.
후속조치로 @플립다우트
서비스 로케이터 유형 패턴을 사용하게 된다면 확인해 보세요. http://www.codeplex.com/CommonServiceLocator.여기에는 도움이 될 수 있는 여러 인기 있는 IoC 프레임워크(windsor, 구조 맵)에 사용할 수 있는 일부 바인딩이 있습니다.
행운을 빌어요.
이 경우 주입되는 강력한 형식의 팩토리를 사용하는 것이 좋습니다.이러한 팩토리는 컨테이너를 래핑할 수 있지만 추가 컨텍스트 전달을 허용하고 추가 처리를 수행할 수 있습니다.예를 들어 OrderFactory의 Create는 상황별 매개변수를 허용할 수 있습니다.
일반 서비스 로케이터에 대한 정적 종속성을 갖는 것은 의도와 컨텍스트를 잃어버리기 때문에 나쁜 생각입니다.IoC가 인스턴스를 구축할 때 큰 그림이 있으므로 프로필, 컨텍스트 등과 같은 다양한 요소를 기반으로 올바른 종속성을 제공할 수 있습니다.
CommonServiceLocator는 이 목적을 위한 것이 아니지만 사용하고 싶은 유혹을 받을 수도 있습니다.CommonServiceLocator의 주요 목적은 IoC 컨테이너 간 규격을 원하는 앱/프레임워크를 위한 것입니다.그러나 를 사용하는 앱은 구성 요소 및 종속 항목의 계층 구조를 구축하기 위해 최적으로 한 번만 로케이터를 호출해야 합니다.다시는 직접 호출하면 안 됩니다.우리가 이를 시행할 수 있는 방법이 있었다면 그렇게 했을 것입니다.인 프리즘(http://www.microsoft.com/compositewpf) 모듈 구축을 위해 IContainerFacade를 도입했습니다.그것은 낮은 수준의 서비스 로케이터입니다.돌이켜보면 아마도 ModuleFactory 같은 것을 생성하고 IContianerFacade를 사용하여 이를 확보한 다음 Facade로 직접 이동하는 대신 모듈을 해결하는 데 사용했어야 했습니다.돌이켜 보면 20/20입니다.레벨이 너무 낮아서 실제로 영향을 미치지는 않습니다.
CSL에서는 네이밍에 혼란을 줄 수 있기 때문에 고민을 많이 했습니다.기술적으로 인터페이스가 DI를 수행하는 데 적합하지 않았기 때문에 결국 우리는 CSL을 선택했습니다.
정말 흔한 문제죠.Windsor가 내장되어 있습니다. 형식화된 공장시설 언급된 단점 없이 공장을 사용하는 이점을 얻을 수 있습니다.