6

나는 의존성 주입을위한 생성자 주입을 좋아합니다. 이것은 유형에 대한 명확한 선언을 강요하고 테스트 가능성에 도움이됩니다.생성자 주입 대체물 (Castle Windsor)

나는 생성자 가장 장소에서 분사, ... 나는 그것을 좋아하지 않아 예를 들어

로깅을 좋아합니다. 많은 다른 클래스가 상속하는 기본 클래스가 있고 모든 클래스가 ILogger (또는 무엇이든)의 인스턴스를 사용하기를 원합니다. 정적 팩터 리 (Logger.Instance)를 원하지 않습니다. ILogger를 취하는 모든 하위 클래스에 생성자를 선언하고 싶지는 않습니다.

그래서, 난 내 기본 클래스 속성으로 로거를 선언하고 그 방법

public class MyBaseClass 
{ 
    public ILogger Logger { get; set; } 
} 

주입이 가질 수 ...하지만

  1. 그 로거 저를 보장하지 않는 사실이 주입되고 null이 아닙니다.
  2. 나는 공공 세트

그래서 ... 내가해야합니까 어떤 다른 옵션과 함께하는 ILogger을 가진 좋아하지 않아? (캐슬 윈저를 사용하고 있습니다).

내가 인터페이스

public interface IInitializable<T> 
{ 
    void Initialize(T instance); 
} 

public class MyBaseClass : IInitializable<ILogger>, ...could have other IInitializables too... 
{ 
    protected ILogger Logger { get; private set; } 

    public void Initialize(ILogger instance) 
    { 
     Logger = instance; 
    } 
} 
다음

자동 형 구조에 따라 IInitializable<T>의 모든 구현을 호출 내 컨테이너에 시설을 가진를 만들기 고려했습니다 ...

그러나 나는 무엇을 다른 사람을 궁금하네요 '생각이 그 길을 가기 전에 ...

+0

왜 정적 팩토리 패턴을 사용하지 않으시겠습니까? –

+0

Ctor 주입 등은 테스트를위한 다양한 구현에 매우 유용합니다. 그러나 개별적으로 로깅을 위해 실제로 필요합니까? 인스턴스 수준에서 로깅 구현을 변경하는 기능이 정말로 필요합니까? 정적 팩토리 패턴은 여전히 ​​필요한 전반적인 * 로깅 구현을 변경할 수있는 기능을 제공합니다. –

+0

반환 할 Logger의 구현이 동적이어야합니다. 특히 Logger.Instance는 컨텍스트 (예 : WCF 작업, WPF 클라이언트, SL 클라이언트 등)에 따라 달라야합니다. 나는 서비스 내역을 .Current 내부에서 처리 할 수있을 것 같지만, 이해할 수있는 반 패턴입니다. – Jeff

답변

1

나는 당신의 경우 속성 주입을 사용합니다. 여기에 설명 된대로

재산권 주입은 필수로 전환 할 수 있습니다 : 당신은이과 복잡함을하고 http://www.mail-archive.com/[email protected]/msg08163.html

+0

수정을 방지하는 방법에 대한 제안 사항이 있으십니까? – Jeff

3

. recommended and documented pattern to inject ILogger은 기본값으로 NullLogger.Instance (예 : null object pattern)이어야하며 Logger은 선택적인 종속성을 유지해야합니다. 로거에 대한 공개 설정자가있는 데는 아무런 문제가 없습니다. 사용자 정의 IInitializable과 같은 사용자 정의를 사용하면 실제로 복잡하게 보이고 실제 값을 제공하지 않을 수 있습니다.

내가 쉽게 참조 할 수 있도록 여기에 문서에서 샘플을 복사 할 수 있습니다

:

using Castle.Core.Logging; 

public class CustomerService 
{ 
    private ILogger logger = NullLogger.Instance; 

    public CustomerService() 
    { 
    } 

    public ILogger Logger 
    { 
     get { return logger; } 
     set { logger = value; } 
    } 

    // ... 
} 

편집 :이 질문은과 거의가 컨텍스트 (에 따라 다른 로거 구현 문제에 대해 실제로 보인다 원래의 질문). 이 경우 서비스 오버라이드 또는 핸들러 선택기를 사용하십시오.

+0

수정을 방지하는 방법에 대한 제안 사항이 있으십니까? 클래스를 상속하여 서비스를 활용할 수 있지만 구현을 변경하지 않으려면 어떻게해야합니까? – Jeff

+1

@ JeffN825 : 왜 처음에는 누구나 수정할 수 있습니까? –

+0

@ JeffN825 : BTW 두 가지 방법이 있습니다. 그것은'private readonly'와 생성자 삽입이거나 변경 가능한 속성입니다. 내가 아는 .net에는 다른 옵션이 없다. –

0

기본 클래스 자체가 ILogger에 의존하지 않는 경우 기본 클래스에서 속성을 제거해야합니다. 이렇게하면 기본 클래스 생성자를 깨끗하게 유지할 수 있습니다.

그렇지 않으면 MyBaseClass의 자손을 만들 수있는 팩토리를 만들 수 있습니다.이것은 다음과 같습니다

이제
public interface IMyBaseClassFactory 
{ 
    MyBaseClass CreateNew<TMyBaseClass>() where TMyBaseClass : MyBaseClass; 
} 

는 당신이이 옵션 종속성을 새로운 인스턴스를 생성하고 등록 할 수 있습니다 IMyBaseClassFactory의 구현을 등록 만들 수 있습니다

public MyBaseClassFactoryImpl : IMyBaseClassFactory 
{ 
    public MyBaseClass CreateNew<TMyBaseClass>() 
    { 
     // Call your IoC container to get a descendant. 
     var instance = ServiceLocator.GetInstance<TMyBaseClass>(); 

     // Register missing dependencies 
     instance.Logger = ServiceLocator.GetInstance<ILogger>(); 

     return instance; 
    } 
} 

대부분의 IoC 컨테이너는 주사 장식 할 수 있도록 속성을 가진 속성. 그러나 팩토리를 사용하면 응용 프로그램이 사용 된 IoC 프레임 워크를 전혀 모르고있을 수 있다는 장점이 있습니다. 단점으로는 할 일이 더 많습니다 (팩토리 만들기, 인스턴스 자체 대신 팩토리 삽입 및 팩토리 호출을 통해 새 인스턴스 만들기).

주사 가능한 속성을 정의 할 때 읽기/쓰기가 필요합니다. 물론 나중에 다른 사람이 실수로 해당 자산을 재설정하지 못하게 할 수 있습니다. 생성자 삽입이 없으면 컴파일 타임 지원으로이를 달성하기가 어렵습니다. 그러나 런타임 검사는 쉽게 이루어집니다.

private ILogger logger; 

public ILogger Logger 
{ 
    get { return this.logger; } 
    set 
    { 
     if (this.logger != null) 
     { 
      throw new InvalidOperationException("Logger has already been set."); 
     } 

     this.logger = value; 
    } 
} 

이 정보가 도움이되기를 바랍니다.

관련 문제