2011-04-26 4 views
3

첫째 날 (의존 관계 역전 원칙을 깰 것이다) 의존성 삽입 (Dependency Injection)없이 구현을 소개하자 : 이제의존성 주입 및 특정 종속 구현

public class MyValidator 
{ 
    private readonly IChecksumGenerator _checksumGenerator; 

    public MyValidator(IChecksumGenerator checksumGenerator) 
    { 
    _checksumGenerator = checksumGenerator; 
    } 

    ... 
} 

:

public class MyValidator 
{ 
    private readonly IChecksumGenerator _checksumGenerator; 

    public MyValidator() 
    { 
    _checksumGenerator = new MyChecksumGenerator(); 
    } 

    ... 
} 

이 코드 검증하자가 IChecksumGenerator 주입하려면 필요한 경우 MyValidator 및 스텁 checksumGenerator를 쉽게 테스트 할 수 있습니다. 그러나 MyValidator 구현은 특정 IChecksumGenerator 구현에 알고리즘 적으로 결합되어 있습니다 (다른 구현에서는 작동하지 않습니다). 그래서 몇 가지 문제가 나타납니다

  1. 우리는 우리가 MyValidator (MyChecksumGenerator에 커플 링)의 개인 구현 세부로 캡슐화 휴식
  2. 잘못된 IChecksumGenerator가 (때문에 IOC의 컨테이너 구성 오류의 예를 들어) 주입 될 가능성을 소개 간다

    public class MyValidator 
    { 
        private readonly IChecksumGenerator _checksumGenerator; 
    
        public MyValidator() 
        { 
        _checksumGenerator = new MyChecksumGenerator; 
        } 
    
        internal MyValidator(IChecksumValidator checksumValidator) 
        { 
        _checksumValidator = checksumValidator; 
        } 
    
        ... 
    } 
    

    그는 : 클래스의 외부

내가에 와서 가장 좋은 해결책은 다음과 같다 내가 테스트 목적으로 (그래서 내가 IChecksumValidator를 테스트에서 스터핑 할 수있는) 특별한 생성자를 소개하지만, 공용 생성자는 결합 된 구현을 생성한다 (그래서 캡슐화는 깨지지 않는다). 테스트 목적으로 약간의 코드를 작성하는 것은 약간 추한 일이지만,이 경우에는 의미가있는 것처럼 보입니다.

이 문제를 어떻게 해결하겠습니까?

+1

MyValidator가 IChecksumGenerator의 특정 구현에서만 작동하는 경우 어떻게 Test Double로 바꾸시겠습니까? 어쨌든 당신은 LSP를 위반할 것입니다 ... –

+0

@Mark : LSP에 관한 좋은 지적입니다. 대답으로 공식화하십시오. 하지만 Test Double에 대한 요점을 찾지 못했습니다. – SiberianGuy

+0

@ldsa : 대답을 추가했습니다. –

답변

4

생성자로 리 팩터링 (Refactoring to Constructor) 주입은 아주 좋은 생각이지만 이상한 질문에 제약 조건이 있습니다. 디자인을 다시 생각해 보는 것이 좋습니다.

MyValidator가 IChecksumGenerator의 특정 구현에서만 작동하는 경우 Liskov Substitution Principle (LSP)을 위반합니다. 본질적으로 이는 또한 스텁/모의/위조/뭐든간에 '올바른'IChecksumGenerator의 인스턴스가 아니기 때문에 Test Double을 삽입 할 수 없다는 것을 의미합니다. 현실에서 그것은 단지 하나 개의 특정 유형으로 작동하는 동안 당신이 말할 수있는 의미에서

는 API, 그 요구 사항이 어떤 IChecksumGenerator 처리 할 수 ​​그것 때문에 주장에 대해 거짓말을 - 현실을 OneAndOnlyChecksumGenerator 호출 할 수 있습니다.

public class MyValidator 
{ 
    private readonly OneAndOnlyChecksumGenerator checksumGenerator; 

    public MyValidator(OneAndOnlyChecksumGenerator checksumGenerator) 
    { 
     this.checksumGenerator = checksumGenerator; 
    } 

    // ... 
} 

하는 당신은 여전히 ​​전략적하여 테스트를 두 번에 OneAndOnlyChecksumGenerator을 켤 수 있습니다 나는 LSP을 준수하도록 응용 프로그램을 재 설계하는 것이 좋습니다 동안, 당신은 또한 요구 사항에 대한 정직하게 생성자 서명을 변경할 수 있습니다 테스트 가상 클래스를 만들 수있는 가상 멤버.

+0

당신은 LSP에 대해 절대적으로 맞습니다 - 좀 더 구체적인 의존성을 전달할 필요가 있습니다. – SiberianGuy

0

테스트 코드를 제품과 분리해야합니다.

제품 코드에 다음을 사용할 수 있습니다

var validator = new MyValidator(new MyChecksumGenerator()); 

을 테스트 코드 :

var validator = new MyValidator(new MyChecksumGeneratorStub()); 

을 MyChecksumGeneratorStub이 IChecksumGenerator를 구현하는 곳.

3

캡슐화에 위배되지 않습니다. 유효성 검사에는 종종 체크섬이 포함됩니다.

가짜 또는 모의 구현물이 제작에 포함 된 제품에 존재하지 않기 때문에 잘못 구성된 ioc 컨테이너에 대해 걱정할 필요가 없습니다. 연기 테스트는 즉시이를 잡아낼 것입니다.

희망이 도움이됩니다.

+0

캡슐화에 위배되지 않는 이유는 무엇입니까? – SiberianGuy

+0

의존성이 드러났습니다. 의존성이 구현되는 방식이 아닙니다. –

1

MyValidator가 알고리즘에 따라 IChecksumGenerator에 연결되는 것을 보지 못했습니다. IChecksumGenerator는 특정 입력 집합에 특정 출력 집합이 반환되는 MyValidator에 계약을 제공합니다.

IChecksumGenerator의 구현에서 이러한 출력을 계산하는 방법은 이며 MyValidator에 영향을주지 않습니다. 이것이 테스트 스텁을 제공 할 수있는 이유입니다. 테스트 스텁에는 사용자가 테스트 할 수 있도록 입력과 출력간에 하드 코딩 된 매핑이 있습니다. 실제 구현에서는 알고리즘을 사용합니다.

알고리즘에는 여러 가지 구현이있을 수 있습니다. 메모리 사용을 최적화하고 속도를 높이기위한 구현이있을 수 있습니다.

가능한 각 입력에 대해 올바른 출력이 제공되는 한 MyValidator는 구현을 신경 쓰지 않아야합니다. 이런 일이 일어나는지 확인하는 것은 모든 테스트가 무엇인지에 대한 것입니다.

실제로 알고리즘 적으로 결합되어 있고 두 가지를 구분할 방법이없는 경우 별도의 클래스가 아니어야합니다.

2

Dependency Injection in .Net의 서적에서 Mark Seemann은이 마지막 해결책 인 Bastard Injection을 호출하여 안티 패턴으로 간주합니다. 이것은 당신이 아직 구체적인 구현에 의존하고 있기 때문에 주로 발생합니다. 자세한 내용은이 책을 확인하십시오.

여기에서 지정한 경우에는 다른 곳에서 다른 코드를 사용하여 유효성 검사가 무엇이든 생성 할 가능성이 매우 높습니다. 나는 이것을 창조주라고 부를 것이다. 차례 차례로이 작성자는 또한 IChecksumGenerator가 필요할 것입니다. 어떤 경우에는 DI 컨테이너가 종속성에 대한 완전한 제어권을 갖도록 할 것입니다.

IChecksumGenerator에 대해 다른 구현으로 전환하려고한다고 가정 해보십시오. 위에서 말한 것이 사실이라고 가정하면, Bastard Injection으로 2 곳에서 이것을 바꿀 필요가 있습니다. 창조주와 검증 자. DI 컨테이너를 제어하게되면 컨테이너 구성이 1 개 밖에 없다는 것을 의미합니다.

DI 컨테이너가 제어 할 수있는 또 다른 이점은 LSP 위반을 도입하여 향후 변경으로 인해 MyValidator와 구체적인 IChecksumValidator가 더 밀접하게 연결될 가능성이 줄어 듭니다.