프레임 워크 작성시 Dependency Injection / IoC 컨테이너 실습


18

많은 프로젝트에서 .Net에 다양한 IoC 컨테이너 (Castle.Windsor, Autofac, MEF 등)를 사용했습니다. 나는 그들이 자주 학대받는 경향이 있으며 여러 가지 나쁜 관행을 장려합니다.

특히 플랫폼 / 프레임 워크를 제공 할 때 IoC 컨테이너 사용에 대해 확립 된 관행이 있습니까? 프레임 워크 작성자로서의 목표는 코드를 가능한 한 간단하고 사용하기 쉽게 만드는 것입니다. 오히려 한 줄의 코드를 작성하여 10 개 또는 2 개가 아닌 객체를 구성합니다.

예를 들어, 몇 가지 코드 냄새가 나는 것을 눈치 채고 좋은 제안이 없습니다.

  1. 생성자를위한 많은 수의 매개 변수 (> 5). 서비스 작성은 복잡한 경향이 있습니다. 구성 요소가 거의 선택적이지 않다는 사실에도 불구하고 모든 종속성은 생성자를 통해 주입됩니다 (아마도 테스트는 제외).

  2. 개인 및 내부 수업 부족; 이것은 C # 및 Silverlight 사용에 대한 특정 제한 사항 일 수 있지만 해결 방법에 관심이 있습니다. 모든 클래스가 퍼블릭이라면 프레임 워크 인터페이스가 무엇인지 말하기 어렵다. 그것은 내가 만져서는 안될 개인 부품에 액세스 할 수있게합니다.

  3. 객체 수명주기를 IoC 컨테이너에 연결 객체를 생성하는 데 필요한 종속성을 수동으로 구성하는 것이 어려운 경우가 많습니다. 개체 수명주기는 너무 자주 IoC 프레임 워크에서 관리합니다. 대부분의 클래스가 싱글 톤으로 등록 된 프로젝트를 보았습니다. 명시적인 통제력이 부족하고 내부를 관리해야합니다 (위의 점과 관련이 있으며 모든 클래스는 공개되어 있으므로 주입해야합니다).

예를 들어 .Net 프레임 워크에는 많은 정적 메소드가 있습니다. DateTime.UtcNow와 같은 여러 번 나는 이것을 구성 매개 변수로 감싸서 주입하는 것을 보았습니다.

구체적인 구현에 따라 코드를 테스트하기가 어렵습니다. 의존성을 주입하면 코드를 사용하기가 어렵습니다. 특히 클래스에 많은 매개 변수가있는 경우.

테스트 가능한 인터페이스와 사용하기 쉬운 인터페이스를 모두 제공하려면 어떻게합니까? 모범 사례는 무엇입니까?


1
그들은 어떤 나쁜 관행을 권장합니까? 개인 수업의 경우 캡슐화가 잘되어 있으면 수업을 많이 가질 필요는 없습니다. 우선 테스트하기가 훨씬 어렵습니다.
Aaronaught

Err, 1과 2는 나쁜 습관입니다. 대형 생성자와 캡슐화를 사용하여 최소 인터페이스를 설정하지 않습니까? 또한 개인 및 내부라고 말했습니다 (내부 테스트를 위해 내부를 사용하십시오). 필자는 특정 IoC 컨테이너를 사용하도록하는 프레임 워크를 사용한 적이 없습니다.
Dave Hillier

2
아마도 클래스 생성자에 많은 매개 변수가 필요하다는 신호 자체가 코드 냄새이고 DIP 가이를 더 분명하게 만드는 것일 수 있습니까?
dreza

3
프레임 워크에서 특정 IoC 컨테이너 를 사용하는 것은 일반적으로 좋은 습관이 아닙니다. 사용 의존성 주입하는 것입니다 좋은 연습. 차이점을 알고 있습니까?
Aaronaught

1
@Aaronaught (죽음에서 돌아왔다). "종속성 주입은 좋은 습관입니다"를 사용하면 거짓입니다. 의존성 주입은 적절한 경우에만 사용해야하는 도구입니다. 항상 의존성 주입을 사용하는 것은 나쁜 습관입니다.
Dave Hillier

답변:


27

내가 아는 유일한 합법적 의존성 주입 반 패턴은 DI 프레임 워크가 사용될 때 반 패턴 인 Service Locator 패턴입니다.

여기 또는 다른 곳에서 들었던 다른 소위 DI 안티 패턴은 일반적인 OO / 소프트웨어 디자인 안티 패턴의 약간 더 구체적인 사례입니다. 예를 들어 :

  • 생성자 과다 주입은 단일 책임 원칙을 위반하는 것입니다 . 생성자 인수가 너무 많으면 종속성이 너무 많음을 나타냅니다. 너무 많은 종속성은 클래스가 너무 많은 것을 시도하고 있음을 나타냅니다. 일반적으로이 오류는 비정상적으로 길거나 모호한 ( "관리자") 클래스 이름과 같은 다른 코드 냄새와 관련이 있습니다. 정적 분석 도구는 과도한 구 심성 / 구 심성 결합을 쉽게 감지 할 수 있습니다.

  • 행동과 반대로 데이터 주입은 poltergeist anti-pattern 의 하위 유형이며, 이 경우에는 geist가 컨테이너입니다. 클래스가 현재 날짜와 시간을 알고 있어야 DateTime하는 경우 데이터 인을 삽입하지 않습니다 . 대신 시스템 시계에 추상화를 주입합니다 ( SystemWrappers 프로젝트 ISystemClock에 더 일반적인 것이 있다고 생각하지만 일반적으로 mine 이라고 부릅니다 ). DI에만 해당되는 것은 아닙니다. 테스트 가능성에 절대적으로 필요하므로 실제로 기다릴 필요없이 시변 함수를 테스트 할 수 있습니다.

  • 싱글턴으로서 모든 라이프 사이클을 선언하는 것은 화물 컬트 프로그래밍 의 완벽한 예이며 구어체로 명명 된 " 객체 cesspool "의 정도는 적습니다 . 나는 기억해야 할 것보다 더 많은 싱글 톤 학대를 보았으며, 그 중 DI는 거의 포함되지 않습니다.

  • 또 다른 일반적인 오류는 IOracleRepository컨테이너에 등록 할 수 있도록 구현 관련 인터페이스 유형 (예 : 이상한 이름 )입니다. 이것은 그 자체가 의존성 역전 원칙을 위반하는 것입니다 (인터페이스이기 때문에 실제로 추상적임을 의미하지는 않습니다). 또한 종종 인터페이스 분리 원칙 을 위반하는 인터페이스 팽창 을 포함 합니다.

  • 내가 일반적으로 보는 마지막 오류 는 NerdDinner에서 수행 한 "선택적 종속성" 입니다. 다시 말해, 의존성 주입을 허용 하는 생성자가 있지만 "기본"구현을 사용하는 다른 생성자가 있습니다. 이것은 또한 딥을 위반 경향 으로 이어질 LSP의 , 시간이 지남에 따라, 개발자로서뿐만 아니라 위반 기본 구현 주위에 가정을 만들기 시작, 및 / 또는 기본 생성자를 사용하여 새로운 보내고 최대 인스턴스를 시작합니다.

이전의 말처럼, 당신은 모든 언어로 FORTRAN을 쓸 수 있습니다 . 의존성 주입은 의존성 관리를 나사에서 개발을 방지하는 묘책은 아니지만 않는 일반적인 오류 / 안티 패턴의 번호를 방지 :

...등등.

분명히 Unity 또는 AutoFac과 같은 특정 IoC 컨테이너 구현 에 의존하는 프레임 워크를 설계하고 싶지는 않습니다 . 다시 한번 DIP를 위반하는 것입니다. 당신은 자신도 그런 일을 생각 발견하면 의존성 삽입 (Dependency Injection)는 범용 종속성 관리 기술하고 있기 때문에, 그때 당신은 이미 여러 설계 오류를 만든 있어야 하지 IOC는 컨테이너의 개념에 묶여.

무엇이든 종속성 트리를 구성 할 수 있습니다. 어쩌면 그것은 IoC 컨테이너 일 수도 있고, 모의 무리를 가진 단위 테스트 일 수도 있고, 더미 데이터를 제공하는 테스트 드라이버 일 수도 있습니다. 귀하의 프레임 워크는 신경 쓰지 않아야하며 내가 본 대부분의 프레임 워크는 신경 쓰지 않지만 여전히 최종 사용자의 IoC 컨테이너에 쉽게 통합 될 수 있도록 종속성 주입을 많이 사용합니다.

DI는 로켓 과학이 아닙니다. 그냥 피하려고 new하고 static거기에 사용할 수있는 강력한 이유가 래퍼 상호 운용성 (예 : 아마도 프레임 워크 외부의 어떤 목적을 가지고 수없는 외부 의존성이없는 유틸리티 메소드 또는 유틸리티 클래스로, 그리고 사전 키의 일반적인 예입니다 경우를 제외하고 이).

개발자가 프레임 워크를 사용하는 방법을 처음 배울 때 IoC 프레임 워크와 관련된 많은 문제가 발생하며, 실제로 IoC 모델에 맞게 종속성 및 추상화를 처리하는 방식을 변경하는 대신 IoC 컨테이너를 조작하여 자신의 기대에 부응합니다. 오래된 코딩 스타일. 종종 높은 커플 링과 낮은 응집력이 필요합니다. 잘못된 코드는 DI 기술 사용 여부에 관계없이 잘못된 코드입니다.


몇 가지 질문이 있습니다. NuGet에 배포 된 라이브러리의 경우 라이브러리 자체가 아니라 라이브러리를 사용하는 응용 프로그램에서 수행하는 IoC 시스템에 의존 할 수 없습니다. 대부분의 경우 라이브러리 사용자는 실제로 내부 종속성에 대해 전혀 신경 쓰지 않습니다. 내부 종속성을 시작하는 기본 생성자와 단위 테스트 및 / 또는 사용자 정의 된 구현을위한 더 긴 생성자가 문제가 있습니까? 또한 "신규"를 피하라고 말합니다. 이것은 StringBuilder, DateTime 및 TimeSpan과 같은 것에 적용됩니까?
Etienne Charland
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.