2009-12-12 6 views
8

의존성 주입에 꽤 익숙하지 않은데 이것이 반 패턴인지 알아 내려고합니다.종속성 주입을 사용하여 종속성 인젝터 주입

의 내가 3 어셈블리 있다고 가정 해 봅시다 :

Foo.Shared - this has all the interfaces 
Foo.Users - references Foo.Shared 
Foo.Payment - references Foo.Shared 

Foo.Users는 Foo.Payment 내에 구축 된 객체를 필요로하고 Foo.Payment도 Foo.Users에서 물건을 필요로한다. 이것은 일종의 순환 종속성을 만듭니다.

내가 사용하고있는 Dependency Injection 프레임 워크를 프록시하는 Foo.Shared에 인터페이스를 정의했습니다 (이 경우 NInject).

public class DependencyResolver:IDependencyResolver 
{ 
    private readonly IKernel _kernel; 

    public DependencyResolver(IKernel kernel) 
    { 
     _kernel = kernel; 
    } 

    public T Get<T>() 
    { 
     return _kernel.Get<T>(); 
    } 
} 

구성은 다음과 같습니다 :

public class MyModule:StandardModule 
{ 
    public override void Load() 
    { 
     Bind<IDependencyResolver>().To<DependencyResolver>().WithArgument("kernel", Kernel); 
     Bind<Foo.Shared.ISomeType>().To<Foo.Payment.SomeType>(); // <- binding to different assembly 
     ... 
    } 
} 

이 날 Foo.Payment.SomeType의 새로운 객체를 인스턴스화 할 수 있습니다 컨테이너 응용 프로그램에서

public interface IDependencyResolver 
{ 
    T Get<T>(); 
} 

는,이 인터페이스의 구현이 Foo에서 직접 참조 할 필요가없는 사용자 :

public class UserAccounts:IUserAccounts 
{ 
    private ISomeType _someType; 
    public UserAccounts(IDependencyResolver dependencyResolver) 
    { 
     _someType = dependencyResolver.Get<ISomeType>(); // <- this essentially creates a new instance of Foo.Payment.SomeType 
    } 
} 

이렇게하면 UserAccounts 클래스의 정확한 종속성이이 인스턴스에 어떤 영향을 미치는지 명확하지 않으므로 이것이 좋지 않다고 생각됩니다.

어떻게 그럴 수 있습니까?

의견이 있으십니까?

+1

+1 혀 트위스터 제목 전용입니다. – womp

+0

여기에 같은 제목을 붙입니다 : –

답변

7

다소 논란의 여지가 있음 : 예, 이것은 반 패턴입니다. 서비스 로케이터으로 알려져 있으며 일부는 적절한 디자인 패턴으로 간주되지만 반 패턴이라고 생각합니다.

이 문제는 예 : 귀하의 UserAccounts 클래스는 대신 암시 적이 아닌 이됩니다. 생성자는 IDependencyResolver가 필요하다고 말하지만, 무엇이 들어가야하는지에 대해서는 명시하지 않습니다. 당신이 ISomeType을 해결할 수없는 IDependencyResolver를 전달한다면, 그것은 던질 것입니다.

나중에 반복 할 때 다른 유형을 UserAccounts에서 해결하려고 할 수 있습니다. 그것은 정상적으로 컴파일 될 것이지만, 타입을 해석 할 수없는 경우 런타임에 던질 가능성이 있습니다.

경로를 지정하지 마십시오.

주어진 정보에서 순환 종속성으로 특정 문제를 해결하는 방법을 정확하게 설명하는 것은 불가능하지만 설계를 다시 생각해 보시기 바랍니다. 많은 경우 순환 참조는 누설 추상화의 증상이므로 API를 약간 개조하면 사라질 수 있습니다. 작은 변경이 필요한 경우가 종종 있습니다.

일반적으로 문제의 해결책은 간접 참조의 다른 계층을 추가하는 것입니다. 두 라이브러리의 개체를 긴밀하게 공동 작업해야하는 경우 일반적으로 중간 브로커를 소개 할 수 있습니다.

  • 많은 경우에 게시/구독 모델이 잘 작동합니다.
  • 중재자 패턴은 통신 양방향으로되어야하는 경우 대안을 제공 할 수 있습니다.
  • 추상 팩토리을 도입하여 필요할 때 바로 인스턴스화해야하는 대신 필요한 인스턴스를 검색 할 수도 있습니다.
+0

그게 종속성이 명확하지 않다고 생각했기 때문에 반 패턴이어야합니다. :) 추상적 인 공장이 나의 첫 번째 선택 이었지만 코드를 복잡하게 만들었습니다. 실제 응용 프로그램에서는 하나뿐 아니라 많은 수의 유형을 만들어야합니다. 각기 다른 팩토리 메소드를 하드 코딩하거나 제네릭을 사용하여 인터페이스를 구체적인 클래스 (하드 코딩 된)에 연관시킵니다. 하지만 이런 식으로 의존성 주입 프레임 워크의 힘을 잃어 버리며, 리플렉션을 사용하여 수동 종속성 주입/유형 확인자 코드가 필요할 때조차도 바인딩을 구성하는 것이 정말 지저분해질 것입니다. – andreialecu

+0

항상 지불해야 할 가격이 있습니다. 컨테이너를 구성하는 것이 지저분 해지는 것에 동의하지 않습니다. 상당히 길고 장황해질 수는 있지만 거의 선언적 인 코드로 구성 될 것입니다. 탁월한 절충점. 컨벤션 기반 구성으로 해결할 수있는 많은 긴장감 - 특히 비슷한 추상 팩터 리가 모두 같은 방식으로 구성되어야하는 경우. Castle Windsor에는 간단한 문장으로 규칙을 구성 할 수있는 기능이 있습니다. NInject가 이것을 할 수 있는지 잘 모르겠습니다 ... –

1

나에게 이상한 것처럼 보입니다. 종속성을 깨고 위험을 피하기 위해 두 참조를 모두 필요로하는 논리를 세 번째 어셈블리로 분리 할 수 ​​있습니까?

2

ForeverDebugging에 동의합니다. 순환 의존성을 제거하는 것이 좋습니다.

  • Foo.Payment.dll :없는 사용자와 만 지불과
  • Foo.Users.dll을 처리하는 클래스 : 사용자 만 처리하는 클래스,하지와 당신이 이런 식으로 클래스를 분리 할 수 ​​있는지 확인 지불
  • Foo.UserPayment.dll : 모두 지불 처리하는 클래스와 사용자

그럼 당신은 하나 둘 개 다른 사람을 참조 조립하지만, 의존성없이 원이있다.

어셈블리간에 순환 종속성이있는 경우 반드시 클래스간에 순환 종속성이 있음을 의미하지는 않습니다. 예를 들어, 이러한 종속성이 있다고 가정

  • Foo.Users.UserAccounts은 Foo.Payment.PaymentHistory에 의해 구현된다 Foo.Shared.IPaymentHistory에 따라 달라집니다.
  • 다른 지불 클래스 인 Foo.Payment.PaymentGateway는 Foo.Shared.IUserAccounts에 의존합니다. IUserAccounts는 Foo.Users.UserAccounts에 의해 구현됩니다.

다른 종속성이 없다고 가정합니다.

여기에는 응용 프로그램의 런타임에 서로 의존하는 어셈블리 서클이 있습니다 (공유 DLL을 통과하기 때문에 컴파일 할 때 서로 의존하지는 않지만). 그러나 컴파일 타임이나 런타임에 서로 의존하는 클래스의 원이 없습니다.

이 경우, 추가 수준의 간접 참조를 추가하지 않고도 IoC 컨테이너를 정상적으로 사용할 수 있어야합니다. MyModule에서 각 인터페이스를 적절한 구체 유형에 바인딩하십시오. 각 클래스가 생성자에 대한 인수로 종속성을 허용하도록 만듭니다. 최상위 레벨 애플리케이션 코드가 클래스의 인스턴스를 필요로 할 때 IoC 컨테이너에 클래스를 요청하게한다. IoC 컨테이너가 클래스가 의존하는 모든 것을 찾는 것에 대해 걱정하게하십시오. 당신은 클래스 사이의 순환 종속성으로 끝낼 할 경우



, 당신은 아마 대신 생성자 주입의, 클래스 중 하나에 속성 주입 (일명 세터 주입)를 사용합니다. 나는 Ninject를 사용하지 않지만 속성 주입을 지원합니다 - here is the documentation.

일반적으로 IoC 컨테이너는 생성자 삽입을 사용합니다. 일반적으로 IoC 컨테이너는 종속성을 의존하는 클래스의 생성자에 전달합니다. 그러나 순환 종속성이있는 경우에는 작동하지 않습니다. 클래스 A와 B가 서로 의존하면 클래스 A의 인스턴스를 클래스 B의 생성자에 전달해야합니다. 그러나 A를 만들려면 클래스 B의 인스턴스를 생성자에 전달해야합니다. 닭고기와 달걀 문제입니다.

속성 주입을 사용하면 IoC 컨테이너에 먼저 생성자를 호출 한 다음 생성 된 개체에 속성을 설정하도록 지시합니다. 일반적으로이 옵션은 로거와 같은 선택적 종속성에 사용됩니다. 그러나 이것을 사용하여 서로를 필요로하는 두 클래스 사이의 순환 종속성을 깨뜨릴 수도 있습니다.

이것은 꽤 아니지만 원형 의존성을 제거하기 위해 클래스를 리팩터링하는 것이 좋습니다.

관련 문제