의존성 주입을 사용할 때 한 클래스에서 허용되는 주입 횟수


9

종속성 주입을 위해 C #에서 Unity를 사용하고 있지만 종속성 주입을 사용하는 모든 언어 및 프레임 워크에 질문이 적용되어야합니다.

나는 SOLID 원칙을 따르려고 노력하고 있으므로 많은 추상화를 얻었습니다. 그러나 이제 한 클래스에 몇 번의 주사를 주사 해야하는지에 대한 모범 사례가 있는지 궁금합니다.

예를 들어, 9 개의 주사가있는 저장소가 있습니다. 다른 개발자에게는 읽기 어려울까요?

주사에는 다음과 같은 책임이 있습니다.

  • IDbContextFactory-데이터베이스에 대한 컨텍스트 작성
  • IMapper-엔터티에서 도메인 모델로 매핑
  • IClock-요약 DateTime. 이제 단위 테스트에 도움을줍니다.
  • IPerformanceFactory-특정 메소드의 실행 시간을 측정합니다.
  • ILog-로깅을위한 Log4net.
  • ICollectionWrapperFactory-IEnumerable을 확장하는 컬렉션을 만듭니다.
  • IQueryFilterFactory-db를 쿼리 할 입력을 기반으로 쿼리를 생성합니다.
  • IIdentityHelper-로그인 한 사용자를 검색합니다.
  • IFaultFactory-다른 FaultException을 작성합니다 (WCF 사용).

나는 책임을 위임 한 방법에 대해 정말로 실망하지는 않지만 가독성에 대해 걱정하기 시작했습니다.

그래서 내 질문 :

수업에 필요한 주사 횟수에 제한이 있습니까? 그렇다면 어떻게 피할 수 있습니까?

많은 주사가 가독성을 제한합니까, 아니면 실제로 개선합니까?


2
숫자로 품질을 측정하는 것은 일반적으로 한 달에 작성한 코드로 지불하는 것만 큼 좋지 않습니다. 그러나 종속성의 실제 예를 포함 시켰습니다. 이는 훌륭한 아이디어입니다. 내가 당신이라면, 나는 세는 개념과 엄격한 제한 개념을 제거하고 그 자체로 품질 대신 초점을 맞추기 위해 질문을 재구성 할 것입니다. 그것을 재구성하는 동안 질문을 너무 구체적으로하지 않도록주의하십시오.
Arseni Mourzenko 2016 년

3
허용되는 생성자 인수는 몇 개입니까? IoC 는이를 변경하지 않습니다 .
Telastyn

사람들이 항상 절대 한계를 원하는 이유는 무엇입니까?
Marjan Venema

1
@Telastyn : 반드시 그런 것은 아닙니다. IoC가 변경하는 것은 정적 클래스 / 단일 변수 / 글로벌 변수에 의존하는 대신 모든 종속성이보다 중앙 집중식이며보다 "가시적"이라는 것입니다.
Arseni Mourzenko

@MarjanVenema : 인생이 훨씬 쉬워지기 때문입니다. 메소드 당 최대 LOC 또는 클래스의 최대 메소드 수 또는 메소드의 최대 변수 수를 정확히 알고 이것이 중요한 것이라면 코드를 양호 또는 불량으로 규정하는 것이 쉬워집니다. 잘못된 코드를 "수정"합니다. 실생활이 그것보다 훨씬 더 복잡하고 많은 메트릭이 대부분 관련이 없다는 것은 불행한 일입니다.
Arseni Mourzenko 12

답변:


11

종속성이 너무 많으면 클래스 자체가 너무 많이 수행하고 있음을 나타낼 수 있습니다. 너무 많이하지 않는지 확인하려면 :

  • 수업 자체를보십시오. 그것을 2, 3, 4로 나누는 것이 합리적입니까? 전체적으로 의미가 있습니까?

  • 종속성 유형을보십시오. 어느 것이 도메인에 특정한 것이며 어떤 것이 "글로벌"입니까? 예를 들어, 나는 ILog같은 수준에서 고려하지 않을 것입니다 IQueryFilterFactory: 첫 번째 클래스는 로깅을 사용하는 경우 대부분의 비즈니스 클래스에서 사용할 수 있습니다. 반면에 도메인 관련 종속성이 많으면 클래스가 너무 많은 작업을 수행하고 있음을 나타낼 수 있습니다.

  • 값으로 대체 될 수있는 종속성을보십시오.

    IClock-요약 DateTime. 이제 단위 테스트에 도움을줍니다.

    DateTime.Now현재 시간을 알아야하는 메소드로 직접 전달 하여 쉽게 대체 할 수 있습니다 .

실제 종속성을 살펴보면 나쁜 일이 발생했음을 나타내는 것을 볼 수 없습니다.

  • IDbContextFactory-데이터베이스에 대한 컨텍스트 작성

    우리는 아마도 클래스가 데이터 액세스 계층과 상호 작용하는 비즈니스 계층 내에있을 것입니다. 좋아 보인다.

  • IMapper-엔터티에서 도메인 모델로 매핑

    전체적인 그림이 없으면 아무 말도하기 어렵습니다. 아키텍처가 잘못되었거나 데이터 액세스 계층에서 직접 매핑을 수행해야하거나 아키텍처가 완벽 할 수 있습니다. 모든 경우에, 여기에 이러한 의존성을 갖는 것이 합리적입니다.

    또 다른 선택은 클래스를 두 가지로 나누는 것입니다. 하나는 매핑을 다루고 다른 하나는 실제 비즈니스 논리를 다루는 것입니다. 이것은 사실상 BL을 DAL로부터 분리시키는 사실상의 계층을 생성 할 것이다. 매핑이 복잡하면 좋은 생각 일 수 있습니다. 그러나 대부분의 경우 쓸모없는 복잡성을 추가합니다.

  • IClock-요약 DateTime. 이제 단위 테스트에 도움을줍니다.

    현재 시간을 얻기 위해 별도의 인터페이스 (및 클래스)를 갖는 것이별로 유용하지 않을 수 있습니다. DateTime.Now현재 시간이 필요한 메소드에 간단히 전달합니다 .

    시간대 나 날짜 범위 등과 같은 다른 정보가있는 경우 별도의 클래스가 적합 할 수 있습니다.

  • IPerformanceFactory-특정 메소드의 실행 시간을 측정합니다.

    다음 요점을 참조하십시오.

  • ILog-로깅을위한 Log4net.

    이러한 초월 기능은 프레임 워크에 속해야하며 실제 라이브러리는 런타임시 상호 교환 및 구성 가능해야합니다 (예 : .NET의 app.config를 통해).

    불행히도, 이것은 (아직) 그렇지 않습니다. 라이브러리를 고르고 고수하거나 추상화 레이어를 만들어 필요할 경우 나중에 라이브러리를 교환 할 수 있습니다. 당신의 의도가 특별히 도서관의 선택과 무관 한 것이면 그것을 찾으십시오. 몇 년 동안 라이브러리를 계속 사용한다고 확신한다면 추상화를 추가하지 마십시오.

    라이브러리가 사용하기에 너무 복잡하면 파사드 패턴이 적합합니다.

  • ICollectionWrapperFactory-IEnumerable을 확장하는 컬렉션을 만듭니다.

    이것이 도메인 논리에 의해 사용되는 매우 구체적인 데이터 구조를 생성한다고 가정합니다. 유틸리티 클래스처럼 보입니다. 대신 관련 생성자와 함께 데이터 구조 당 하나의 클래스를 사용하십시오. 초기화 논리가 생성자에 맞게 약간 복잡하면 정적 팩토리 메소드를 사용하십시오. 논리가 더 복잡한 경우 팩토리 또는 빌더 패턴을 사용하십시오.

  • IQueryFilterFactory-db를 쿼리 할 입력을 기반으로 쿼리를 생성합니다.

    왜 데이터 액세스 계층에 있지 않습니까? 왜 Filter이름이 있습니까?

  • IIdentityHelper-로그인 한 사용자를 검색합니다.

    Helper접미사 가 있는지 잘 모르겠습니다 . 모든 경우에 다른 접미사는 특히 명시 적이 지 않습니다 ( IIdentityManager?).

    어쨌든 여기에 이러한 의존성을 갖는 것이 완벽합니다.

  • IFaultFactory-다른 FaultException을 작성합니다 (WCF 사용).

    논리가 너무 복잡하여 팩토리 패턴을 사용해야합니까? 왜 의존성 주입이 사용됩니까? 프로덕션 코드와 테스트간에 예외 생성을 바꾸시겠습니까? 왜?

    나는 그것을 간단하게 리팩토링하려고 노력할 것이다 throw new FaultException(...). 클라이언트에 전파하기 전에 모든 예외에 일부 글로벌 정보를 추가해야하는 경우 WCF에는 처리되지 않은 예외를 잡아서이를 클라이언트에 다시 던질 수있는 메커니즘이있을 수 있습니다.

수업에 필요한 주사 횟수에 제한이 있습니까? 그렇다면 어떻게 피할 수 있습니까?

숫자로 품질을 측정하는 것은 일반적으로 한 달에 작성한 코드로 지불하는 것만 큼 나쁩니다. 소수의 종속성을 사용하여 크 래피 클래스를 가질 수 있으므로 잘 설계된 클래스에는 많은 종속성이있을 수 있습니다.

많은 주사가 가독성을 제한합니까, 아니면 실제로 개선합니까?

많은 종속성으로 인해 논리를 따르기가 더 어려워집니다. 논리를 따르기가 어려우면 클래스가 너무 많은 작업을 수행 중이므로 분할해야합니다.


의견과 시간을 보내 주셔서 감사합니다. 대체로 FaultFactory에 대해서는 WCF-logic으로 옮겨 질 것입니다. 응용 프로그램을 TDD 할 때 생명을 구하기 때문에 IClock을 유지합니다. 특정 시간이 특정 값으로 설정되도록하려는 경우가 있습니다. 그런 다음 DateTime.Now는 조롱 할 수 없기 때문에 항상 충분하지는 않습니다.
smoksnes

7

이것은 학급이 단일 학급이 되기에는 너무 커지고 있음을 알려주는 DI의 전형적인 예입니다. 이것은 종종 "와우, DI가 내 생성자를 목성보다 크게 만듭니다.이 기술은 끔찍합니다"라고 해석되지만 실제로는 "클래스에 의존성이 많이 있습니다"라고 말합니다. 이것을 알면, 우리는

  • 대신 새로운 의존성을 시작하여 양탄자 아래에서 문제를 청소하십시오.
  • 우리의 디자인을 재고하십시오. 어쩌면 일부 종속성은 항상 함께 제공되며 다른 추상화 뒤에 숨겨져 있어야합니다. 어쩌면 클래스는 2로 나뉘어 야 할 수도 있습니다. 어쩌면 클래스마다 종속성의 작은 하위 집합이 필요한 여러 클래스로 구성되어야 할 수도 있습니다.

의존성을 관리하는 방법은 셀 수없이 많으며, 코드와 응용 프로그램을 알지 못하면 자신의 경우에 가장 적합한 것을 말할 수 없습니다.

최종 질문에 대답하려면 :

  • 클래스에 필요한 종속성 수에 대한 상한이 있습니까?

예, 상한은 "너무 많습니다". "너무 많이"몇 개입니까? "너무 많음"은 클래스의 결속력이 "너무 낮아"집니다. 모든 것이 다릅니다. 일반적으로 수업에 대한 당신의 반응이 "와우,이 일에는 많은 의존성이 있습니다", 그것은 너무 많습니다.

  • 의존성 주입은 가독성을 향상 시키거나 손상 시키는가?

이 질문이 오도 된 것 같습니다. 대답은 예 또는 아니오 일 수 있습니다. 그러나 가장 흥미로운 부분은 아닙니다. 의존성을 주입하는 요점은 그것들을 보이게 만드는 것입니다. 거짓말하지 않는 API를 만드는 것입니다. 지구 상태를 막는 것입니다. 코드를 테스트 할 수있게 만드는 것입니다. 커플 링 감소에 관한 것입니다.

합리적으로 디자인되고 명명 된 메소드가있는 잘 설계된 클래스는 잘못 설계된 클래스보다 더 읽기 쉽습니다. DI는 그 자체로 가독성을 향상 시키거나 손상시키지 않으며 단지 디자인 선택을 돋보이게하며, 나쁘면 눈을 찌를 것입니다. 이것은 DI가 코드를 읽을 수 없게 만든다는 의미는 아닙니다. 코드가 이미 엉망이고 방금 숨겼다는 것을 보여줍니다.


1
좋은 피드백. 감사합니다. 예, 상한은 "너무 많습니다". -정말 환상적입니다.
smoksnes

3

유비쿼터스 "Code Complete"의 작성자 인 Steve McConnel에 따르면 경험상 7 개가 넘는 코드 냄새가 유지 관리 능력을 저하시키는 것입니다. 개인적으로 대부분의 경우 숫자가 더 낮다고 생각하지만 DI를 올바르게 수행 하면 컴포지션 루트에 매우 가까이있을 때 주입해야 할 많은 종속성이 있습니다. 이것은 정상이며 예상됩니다. IoC 컨테이너가 유용하고 프로젝트에 복잡성을 더할만한 가치가있는 이유 중 하나입니다.

따라서 프로그램의 진입 점에 매우 가까운 경우 이는 정상이며 수용 가능합니다. 프로그램의 논리에 대해 더 깊이 알고 있다면 해결해야 할 냄새 일 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.