Poor Man 's Dependency Injection은 레거시 응용 프로그램에 테스트 기능을 도입하는 좋은 방법입니까?


14

작년에 Dependency Injection과 IOC 컨테이너를 사용하여 새로운 시스템을 만들었습니다. 이것은 나에게 DI에 대해 많은 것을 가르쳤다!

그러나 개념과 적절한 패턴을 학습 한 후에도 코드를 분리하고 IOC 컨테이너를 레거시 응용 프로그램에 도입하는 것은 어려운 일이라고 생각합니다. 응용 프로그램은 실제 구현이 압도적 일 정도로 충분히 큽니다. 가치를 이해하고 시간이 주어진 경우에도 마찬가지입니다. 누가 이런 시간을 주었을까요 ??

물론 목표는 비즈니스 테스트에 단위 테스트를 제공하는 것입니다!
테스트 방지 데이터베이스 호출과 관련이있는 비즈니스 로직.

기사를 읽었 으며이 Los Techies 기사에 설명 된대로 가난한 사람의 의존성 주입의 위험을 이해합니다 . 나는 그것이 실제로 어떤 것도 분리 하지 않는다는 것을 이해합니다 .
구현에는 새로운 종속성이 필요하므로 시스템 전체 리팩토링이 많이 필요할 수 있음을 이해합니다. 크기에 관계없이 새 프로젝트에서 사용하는 것은 고려하지 않습니다.

질문 : Poor Man 's DI를 사용 하여 레거시 응용 프로그램에 테스트 기능을 도입 하고 볼 롤링을 시작할 수 있습니까?

또한 Poor Man의 DI를 진정한 의존성 주입에 대한 풀뿌리 접근 방식으로 사용하여 원칙의 필요성과 이점을 교육하는 귀중한 방법입니까?

데이터베이스 호출 종속성이있는 메소드를 리팩터링하고 해당 호출을 인터페이스 뒤에서 추상화 할 수 있습니까? 간단히 추상화하면 모의 구현이 생성자 과부하를 통해 전달 될 수 있기 때문에 해당 메소드를 테스트 할 수 있습니다.

일단 노력이 지지자들을 확보하면, IOC 컨테이너를 구현하기 위해 프로젝트를 업데이트 할 수 있으며 생성자를 생성하여 추상화 할 수 있습니다.



2
그냥 참고하십시오. I consider it a challenge to decouple code and introduce an IOC container into a legacy application물론입니다. 기술 부채입니다. 이것이 대대적 인 개선 이전에 작고 지속적인 리팩터링이 선호되는 이유입니다. 주요 설계 결함을 줄이고 IoC로 전환하는 것은 쉽지 않습니다.
Laiv

답변:


25

NerdDinner의 Poor Man 's Injection에 대한 비판은 클래스를 올바르게 설정하는 것보다 DI 컨테이너를 사용하는지 여부와 관련이 없습니다 .

이 기사에서 그들은

public class SearchController : Controller {

    IDinnerRepository dinnerRepository;

    public SearchController() : this(new DinnerRepository()) { }

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

첫 번째 생성자는 클래스를 구성하는 편리한 폴백 메커니즘을 제공하지만에 대한 밀접한 바인딩을 생성하기 때문에 올바르지 않습니다 DinnerRepository.

물론 올바른 해결 방법은 Los Techies가 제안한 것처럼 DI 컨테이너를 추가하는 것이 아니라 문제가되는 생성자제거하는 것입니다.

public class SearchController : Controller 
{
    IDinnerRepository dinnerRepository;

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

나머지 클래스는 이제 종속성이 올바르게 반전되었습니다. 이제 원하는대로 종속성을 자유롭게 주입 할 수 있습니다.


당신의 생각에 감사드립니다! "불쾌한 생성자"와이를 피하는 방법을 이해합니다. 나는이 의존성을 갖는 것이 아무것도 분리하지 않지만 단위 테스트를 허용한다는 것을 이해합니다. 저는 DI를 도입하고 가능한 빨리 유닛 테스트를 시행하는 초기 단계에 있습니다. 게임 초반에 DI / IOC 컨테이너 또는 공장의 복잡성을 피하려고합니다. 언급했듯이 나중에 DI에 대한 지원이 커지면 컨테이너를 구현하고 해당 "불량 생성자"를 제거 할 수 있습니다.
Airn5475

단위 생성에는 기본 생성자가 필요하지 않습니다. 모의 객체를 포함하여 클래스에 관계없이 원하는 종속성을 전달할 수 있습니다.
Robert Harvey

2
Airn5475 @ 당신이되지 않도록주의 를 통해 너무 추상화. 이러한 테스트는 의미있는 동작을 테스트해야합니다.- XService매개 변수를 전달하고 XRepository다시 가져 오는 것을 반환 하는 테스트는 어느 누구에게나 유용 하지 않으며 확장 성도 많이 제공 하지 않습니다 . 의미있는 동작을 테스트하기 위해 단위 테스트를 작성하고 구성 요소가 너무 좁아서 실제로 모든 논리를 컴포지션 루트로 이동하지 않도록하십시오.
Ant P

나는 불쾌한 생성자를 포함시키는 것이 단위 테스트에 어떻게 도움이 될지 이해하지 못한다. DI가 올바르게 구현되어 오브젝트를 인스턴스화 할 때 모의 된 종속성을 전달하도록 문제가되는 생성자를 제거하십시오.
Rob

@Rob : 그렇지 않습니다. 기본 생성자가 유효한 객체를 세우는 편리한 방법입니다.
Robert Harvey

16

여기서 "가난한 사람의 DI"가 무엇인지에 대해 잘못된 가정을합니다.

여전히 커플 링을 생성하는 "바로 가기"생성자가있는 클래스를 만드는 것은 가난한 사람의 DI 가 아닙니다 .

컨테이너를 사용하지 않고 모든 주입 및 매핑을 수동으로 만드는 것은 가난한 사람의 DI입니다.

"가난한 사람의 DI"라는 용어는 나쁜 짓처럼 들립니다. 이러한 이유로, "순수한 DI"라는 용어는 요즘 더 긍정적으로 들리고 실제로 프로세스를보다 정확하게 설명하기 때문에 권장 됩니다.

가난한 사람의 / 순수한 DI를 사용하여 기존 응용 프로그램에 DI를 도입하는 것이 절대적으로 좋을뿐만 아니라 많은 새로운 응용 프로그램에 DI를 사용하는 유효한 방법이기도합니다. 그리고 말했듯이, 모든 사람은 IoC 컨테이너의 "마법"에 대한 책임을 처리하기 전에 최소한 하나의 프로젝트에서 순수 DI를 사용하여 DI의 작동 방식을 실제로 이해해야합니다.


8
"Poor man 's"또는 "Pure"DI 중 원하는 것을 선택하면 IoC 컨테이너의 많은 문제가 완화됩니다. 나는 모든 것에 IoC 컨테이너를 사용했지만 시간이 지날수록 컨테이너를 피하는 것이 더 좋습니다.
Ant P

7
@AntP, 나는 당신과 함께 있습니다 : 나는 요즘 100 % 순수 DI이며 적극적으로 용기를 피하십시오. 그러나 나는 (우리가) 내가 말할 수있는 한 그 점수에서 소수에 있습니다.
David Arno

2
너무 슬프게 보입니다-IoC 컨테이너의 "마법"캠프, 모의 라이브러리 등에서 멀어지고 OOP, 캡슐화, 수동 롤 테스트 및 상태 확인을 수용하는 것으로 나타났습니다. 마법의 추세는 여전히 쪽으로. 어쨌든 +1.
Ant P

3
@AntP & David : 'Pure DI'캠프에서 혼자가 아니라는 말을 듣는 것이 좋습니다. DI 컨테이너는 언어의 결함을 해결하기 위해 만들어졌습니다. 그런 의미에서 가치를 더합니다. 그러나 내 마음에 진정한 대답은 언어를 수정하거나 다른 언어로 옮기는 것입니다.
JimmyJames

1

레거시 팀 / 코드 기반의 패러다임 전환은 매우 위험합니다.

당신이 제안 할 때마다 "개선" 레거시 코드를하고있다 "레거시" 팀의 프로그래머는, 당신은 그냥 모두를 말하고있다 "그들이 잘못했다" 당신은 될 적의 회사와 / 자신의 시간의 나머지 부분에 대한.

DI 프레임 워크를 망치로 사용하여 모든 손톱을 부 수면 레거시 코드가 모든 경우보다 나빠질 수 있습니다. 또한 개인적으로 매우 위험합니다.

테스트 케이스에서 사용할 수있는 가장 제한된 경우라도 테스트 케이스를 "비표준"및 "외국"코드로 만들면 @Ignore중단되거나 악화 될 때 가장 잘 표시 될 수 있습니다. 관리에 대한 영향력이 가장 큰 레거시 프로그래머는이 "단위 테스트 시간 낭비"를 모두 "전적으로"다시 작성해야합니다.

DI 프레임 워크 또는 심지어 "Pure DI"개념을 비즈니스 응용 프로그램에 도입하면 경영진의 도움 없이는 기존 레거시 코드 기반이 훨씬 적습니다. 팀 및 특히 수석 개발자의 후원은 죽음의 곤경에 불과합니다. 팀 / 회사에서 사회적, 정치적으로 이와 같은 일을하는 것은 매우 위험하며 최악의 방법으로 정치적 자살이 될 수 있습니다.

의존성 주입은 문제를 찾는 솔루션입니다.

정의에 따라 생성자가있는 모든 언어는 수행중인 작업을 알고 생성자를 올바르게 사용하는 방법을 이해하는 경우 규칙에 따라 종속성 주입을 사용합니다. 이것은 단지 좋은 디자인입니다.

의존성 주입은 매우 좁은 범위의 물건에 대해서만 소량으로 만 유용합니다.

  • 정적으로 묶인 많은 대체 구현이 있거나 많이 변경되는 것.

    • JDBC 드라이버는 완벽한 예입니다.
    • 플랫폼마다 다를 수있는 HTTP 클라이언트.
    • 플랫폼마다 다른 로깅 시스템.
  • 구성 가능한 플러그인이있는 플러그인 시스템으로, 프레임 워크의 구성 코드에서 정의 할 수 있으며 시작시 자동으로 감지되며 프로그램 실행 중에 동적으로로드 / 재로드됩니다.


10
DI의 킬러 앱은 단위 테스트입니다. 지적했듯이 대부분의 소프트웨어에는 많은 DI가 필요하지 않습니다. 그러나 테스트도이 코드의 클라이언트이므로 테스트 가능성을 위해 디자인해야합니다. 특히 이는 테스트에서 동작을 관찰 할 수있는 디자인에 이음새 를 배치하는 것을 의미 합니다. 이것은 화려한 DI 프레임 워크를 필요로하지 않지만, 관련된 의존성을 명시적이고 대체 할 수 있어야합니다.
amon

4
우리가 발전 있는 것이 무엇인지 사전에 알았다면 , 그것은 반전 이 될 것이다. 나는 아마도 많은 "고정 된"것들이 바뀌는 긴 개발 에 있었다. DI를 사용한 미래의 증거는 나쁘지 않습니다. 화물 컬트 프로그래밍에서 항상 사용됩니다.
Robbie Dee

테스트를 너무 복잡하게하면 나중에 업데이트되지 않고 오래되고 무시되어 표시됩니다. 레거시 코드의 DI는 잘못된 경제입니다. 그들이 할 일은 코드베이스에 "아무도 이해하지 못하는 비표준적이고 지나치게 복잡한 코드"를 모두 넣는 "나쁜 녀석"이되는 것입니다. 경고를 받았습니다.

4
@JarrodRoberson, 그것은 정말 독성 환경입니다. 좋은 개발자의 핵심 징후 중 하나는 과거에 작성한 코드를 되돌아 보면서 "웃음, 내가 실제로 작성 했습니까? 너무 잘못되었습니다!"라는 생각입니다. 개발자로 성장하여 새로운 것을 배웠기 때문입니다. 5 년 전에 작성한 코드를보고 아무 문제가없는 사람은 5 년 동안 아무것도 배우지 못했습니다. 따라서 사람들에게 과거에 그들이 잘못했다고 말하면 당신을 적으로 만든 다음, 그 회사를 빠르게 도망 치십시오.
David Arno

1
내가 나쁜 녀석들에 동의하지는 않지만, 나에게 중요한 점 은 단위 테스트를 수행하기 위해 DI가 필요 없다는 것입니다. 코드를 더 테스트 가능하게 만들기 위해 DI를 구현하는 것은 문제를 패치하는 것입니다.
Ant P
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.