[oop] 의존성 역전 원리는 무엇이며 왜 중요한가?

의존성 역전 원리는 무엇이며 왜 중요한가?



답변

이 문서를 확인하십시오 : Dependency Inversion Principle .

기본적으로 말합니다 :

  • 고수준 모듈은 저수준 모듈에 의존해서는 안됩니다. 둘 다 추상화에 의존해야합니다.
  • 추상화는 세부 사항에 의존해서는 안됩니다. 세부 사항은 추상화에 의존해야합니다.

중요한 이유는 간단히 말해서 변경이 위험하고 구현이 아닌 개념에 따라 콜 사이트에서 변경의 필요성이 줄어든다는 것입니다.

효과적으로, DIP는 상이한 코드 조각들 사이의 결합을 감소시킨다. 아이디어는 로깅 기능을 구현하는 여러 가지 방법이 있지만이를 사용하는 방식은 시간이 비교적 안정적이어야한다는 것입니다. 로깅 개념을 나타내는 인터페이스를 추출 할 수있는 경우이 인터페이스는 구현보다 훨씬 안정적이어야하며 호출 사이트는 해당 로깅 메커니즘을 유지 관리하거나 확장하는 동안 수행 할 수있는 변경의 영향을 훨씬 덜받습니다.

또한 구현이 인터페이스에 종속되도록함으로써 런타임시 특정 환경에 더 적합한 구현을 선택할 수 있습니다. 경우에 따라 이것 또한 흥미로울 수 있습니다.


답변

C #의 민첩한 소프트웨어 개발, 원칙, 패턴 및 사례와 민첩한 원칙, 패턴 및 사례는 Dependency Inversion Principle의 원래 목표와 동기를 완전히 이해하는 데 가장 적합한 리소스입니다. “The Dependency Inversion Principle”이라는 기사도 좋은 자료이지만, 이전에 언급 된 책으로 들어가는 초안의 요약 버전이라는 사실 때문에 이 원칙을보다 일반적인 조언과 구별하기위한 핵심 인 패키지 및 인터페이스 소유권은 Design Patterns (Gamma, et al.) 책에서 찾을 수있는 “구현이 아닌 인터페이스로의 프로그래밍”에 대한 조언입니다.

요약을 제공하기 위해 Dependency Inversion Principle은 주로 “상위 레벨”구성 요소가 “상위 레벨”구성 요소가 소유 한 인터페이스에 종속되도록 “상위 레벨”구성 요소에서 “하위 레벨”구성 요소로의 기존 종속성 방향을 반전시키는 것에 관한 것 입니다. . (참고 : 여기서 “높은 수준”구성 요소는 계층 구조 내에서 개념적 위치 일 필요는 없지만 외부 종속성 / 서비스가 필요한 구성 요소를 나타냅니다. 그렇게하면 이론적으로 구성 요소에서 이동 하는 것처럼 커플 링이 크게 줄어들지 는 않습니다. 이론적으로 더 가치가있는 구성 요소에는 덜 가치가 있습니다.

이것은 외부 의존성이 컴포넌트의 소비자에 의해 구현이 제공되어야하는 인터페이스의 관점에서 표현되는 컴포넌트를 설계함으로써 달성된다. 다시 말해, 정의 된 인터페이스는 컴포넌트 사용 방법이 아니라 컴포넌트에 필요한 것을 표현합니다 (예 : “IDoSomething”이 아닌 “INeedSomething”).

Dependency Inversion Principle에서 언급하지 않는 것은 인터페이스를 사용하여 종속성을 추상화하는 간단한 방법입니다 (예 : MyService → [ILogger ⇐ Logger]). 이렇게하면 종속성의 특정 구현 세부 사항에서 구성 요소가 분리되지만 소비자와 종속성 간의 관계는 반전되지 않습니다 (예 : [MyService → IMyServiceLogger] ⇐ Logger).

Dependency Inversion Principle의 중요성은 기능의 일부 (로깅, 유효성 검사 등)에 외부 종속성에 의존하는 소프트웨어 구성 요소를 재사용 할 수 있다는 단일 목표로 나눌 수 있습니다.

이 일반적인 재사용 목표 내에서 두 가지 하위 유형의 재사용을 설명 할 수 있습니다.

  1. 하위 종속성 구현으로 여러 응용 프로그램 내에서 소프트웨어 구성 요소 사용 (예 : DI 컨테이너를 개발하고 로깅을 제공하려고하지만 컨테이너를 사용하는 모든 사람이 특정 로거에 컨테이너를 연결하지는 않음) 선택한 로깅 라이브러리를 사용하십시오).

  2. 진화하는 상황에서 소프트웨어 구성 요소 사용 (예 : 구현 세부 사항이 발전하는 여러 버전의 응용 프로그램에서 동일하게 유지되는 비즈니스 논리 구성 요소를 개발했습니다).

인프라 라이브러리와 같이 여러 응용 프로그램에서 구성 요소를 재사용하는 첫 번째 경우 목표는 이러한 종속성에 의존하기 위해서는 소비자가 자신의 라이브러리의 하위 종속성에 연결하지 않고 소비자에게 핵심 인프라를 제공하는 것입니다. 소비자들도 같은 의존성을 요구해야합니다. 라이브러리 소비자가 동일한 인프라 요구 사항 (예 : NLog와 log4net)에 대해 다른 라이브러리를 사용하기로 선택하거나 이전 버전과 호환되지 않는 필수 라이브러리의 이후 버전을 사용하기로 선택한 경우 문제가 될 수 있습니다. 도서관에서 필요합니다.

비즈니스 논리 구성 요소 (예 : “상위 레벨 구성 요소”)를 재사용하는 두 번째 경우의 목표는 응용 프로그램의 핵심 도메인 구현을 구현 세부 사항의 변경 요구 (예 : 지속성 라이브러리, 메시징 라이브러리 변경 / 업그레이드)에서 분리하는 것입니다. , 암호화 전략 등). 이상적으로 응용 프로그램의 구현 세부 사항을 변경해도 응용 프로그램의 비즈니스 논리를 캡슐화하는 구성 요소가 손상되지 않아야합니다.

참고 : 일부는이 두 번째 사례를 실제 재사용으로 설명하는 데 반대 할 수 있습니다. 단일 진화하는 응용 프로그램 내에서 사용되는 비즈니스 논리 구성 요소와 같은 구성 요소는 단일 용도 일뿐입니다. 그러나 여기서 아이디어는 응용 프로그램의 구현 세부 사항을 변경할 때마다 새로운 컨텍스트와 다른 사용 사례를 렌더링하지만 궁극적 목표는 격리와 이식성으로 구분 될 수 있다는 것입니다.

이 두 번째 사례에서 Dependency Inversion Principle을 따르는 것이 약간의 이점을 제공 할 수 있지만 Java 및 C #과 같은 현대 언어에 적용되는 가치는 크게 관련이없는 수준으로 줄어든다는 점에 유의해야합니다. 앞에서 설명한 것처럼 DIP는 구현 세부 정보를 별도의 패키지로 완전히 분리합니다. 그러나 진화하는 애플리케이션의 경우, 비즈니스 도메인 측면에서 정의 된 인터페이스를 사용하면 구현 세부 사항 구성 요소의 변경 요구로 인해 더 높은 레벨의 구성 요소를 수정해야 할 필요가 없습니다. . 이 원칙의이 부분은 새로운 언어와 관련이없는 원칙이 체계화 될 때 (즉, C ++) 해당 언어와 관련된 측면을 반영합니다. 그것은 말했다

인터페이스의 간단한 사용, 종속성 주입 및 분리 된 인터페이스 패턴과 관련된이 원칙에 대한 자세한 설명은 여기를 참조하십시오 . 또한이 원칙이 JavaScript와 같은 동적 유형 언어와 어떤 관련이 있는지에 대한 설명은 여기를 참조하십시오 .


답변

소프트웨어 응용 프로그램을 설계 할 때는 기본 및 기본 작업 (디스크 액세스, 네트워크 프로토콜 등)을 구현하는 하위 수준 클래스와 복잡한 논리 (비즈니스 흐름 등)를 캡슐화하는 클래스를 고려할 수 있습니다.

마지막 클래스는 저급 클래스에 의존합니다. 이러한 구조를 구현하는 자연스러운 방법은 저수준 클래스를 작성하고 일단 복잡한 클래스를 작성하는 것입니다. 높은 수준의 클래스는 다른 클래스로 정의되므로 논리적 인 방법입니다. 그러나 이것은 유연한 디자인이 아닙니다. 저수준 클래스를 교체해야하는 경우 어떻게됩니까?

종속성 반전 원리는 다음과 같습니다.

  • 고수준 모듈은 저수준 모듈에 의존해서는 안됩니다. 둘 다 추상화에 의존해야합니다.
  • 추상화는 세부 사항에 의존해서는 안됩니다. 세부 사항은 추상화에 의존해야합니다.

이 원칙은 소프트웨어의 높은 수준의 모듈이 낮은 수준의 모듈에 의존해야한다는 기존의 개념을 “반전”시키려고합니다. 여기서 상위 레벨 모듈은 하위 레벨 모듈에 의해 구현되는 추상화 (예 : 인터페이스의 방법 결정)를 소유합니다. 따라서 하위 모듈을 상위 모듈에 의존하게합니다.


답변

종속성 반전이 잘 적용되면 애플리케이션의 전체 아키텍처 수준에서 유연성과 안정성이 제공됩니다. 이를 통해 애플리케이션이보다 안전하고 안정적으로 발전 할 수 있습니다.

전통적인 계층 구조

일반적으로 계층화 된 아키텍처 UI는 비즈니스 계층에 의존하고 이는 데이터 액세스 계층에 의존합니다.

계층, 패키지 또는 라이브러리를 이해해야합니다. 코드가 어떻게 작동하는지 봅시다.

데이터 액세스 계층을위한 라이브러리 또는 패키지가 있습니다.

// DataAccessLayer.dll
public class ProductDAO {

}

그리고 데이터 액세스 계층에 의존하는 다른 라이브러리 또는 패키지 계층 비즈니스 로직.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
    private ProductDAO productDAO;
}

종속성 반전을 사용하는 계층화 된 아키텍처

종속성 반전은 다음을 나타냅니다.

고수준 모듈은 저수준 모듈에 의존해서는 안됩니다. 둘 다 추상화에 의존해야합니다.

추상화는 세부 사항에 의존해서는 안됩니다. 세부 사항은 추상화에 따라 달라집니다.

고수준 모듈과 저수준은 무엇입니까? 라이브러리 또는 패키지와 같은 모듈, 상위 수준 모듈은 전통적으로 의존성이 있고 의존도가 낮은 수준입니다.

다시 말해, 모듈 상위 레벨은 조치가 호출되는 위치이고 하위 레벨은 조치가 수행되는 위치입니다.

이 원칙을 근거로 합리적인 결론은 결단 사이에 의존성이 없어야하지만 추상화에 의존해야한다는 것입니다. 그러나 우리가 취하는 접근법에 따르면 투자 의존도를 잘못 적용 할 수 있지만 추상화입니다.

다음과 같이 코드를 수정했다고 상상해보십시오.

추상화를 정의하는 데이터 액세스 계층을위한 라이브러리 또는 패키지가 있습니다.

// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{

}

그리고 데이터 액세스 계층에 의존하는 다른 라이브러리 또는 패키지 계층 비즈니스 로직.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
    private IProductDAO productDAO;
}

우리는 비즈니스와 데이터 액세스 간의 추상화 종속성에 의존하지만 여전히 동일합니다.

종속성 반전을 얻으려면 지속성 인터페이스가이 높은 수준의 논리 또는 도메인이 있고 낮은 수준의 모듈이 아닌 모듈 또는 패키지에서 정의되어야합니다.

먼저 도메인 계층이 무엇인지 정의하고 통신의 추상화는 지속성으로 정의됩니다.

// Domain.dll
public interface IProductRepository;

using DataAccessLayer;
public class ProductBO {
    private IProductRepository productRepository;
}

지속성 계층이 도메인에 의존 한 후 종속성이 정의되면 지금 반전시킵니다.

// Persistence.dll
public class ProductDAO : IProductRepository{

}


(출처 : xurxodev.com )

원리 심화

개념을 잘 동화시키고 목적과 이점을 심화시키는 것이 중요합니다. 기계적으로 머물면서 전형적인 사례 저장소를 배우면 의존성 원칙을 적용 할 수있는 곳을 식별 할 수 없습니다.

그러나 왜 의존성을 뒤집는가? 구체적인 예를 넘어서는 주요 목표는 무엇입니까?

일반적으로 덜 안정적인 것들에 의존하지 않는 가장 안정적인 것들이 더 자주 변경 될 수 있습니다.

데이터베이스 또는 기술이 지속성과 통신하기 위해 설계된 도메인 논리 나 작업과 동일한 데이터베이스에 액세스하는 지속성 유형을 변경하는 것이 더 쉽습니다. 이 때문에이 변경이 발생하면 지속성을 변경하기가 쉽기 때문에 종속성이 반대로됩니다. 이런 식으로 도메인을 변경할 필요가 없습니다. 도메인 계층은 무엇보다 가장 안정적이므로 어떤 것에 의존해서는 안됩니다.

그러나이 저장소 예제 만있는 것은 아닙니다. 이 원칙이 적용되는 많은 시나리오가 있으며이 원칙을 기반으로하는 아키텍처가 있습니다.

아키텍처

종속성 반전이 정의의 핵심 인 아키텍처가 있습니다. 모든 도메인에서 가장 중요하며 도메인과 나머지 패키지 또는 라이브러리 간의 통신 프로토콜이 정의되었음을 나타내는 추상화입니다.

깨끗한 건축

에서 청소 아키텍처 도메인은 중앙에 위치하고 의존성을 나타내는 화살표 방향으로 보면, 가장 중요하고 안정적인 층이 무엇인지 분명합니다. 외부 레이어는 불안정한 도구로 간주되므로 의존하지 마십시오.


(출처 : 8thlight.com )

6 각형 아키텍처

도메인이 중앙 부분에 위치하고 포트가 도미노 외부에서 통신의 추상화 인 6 각형 아키텍처와 동일한 방식으로 발생합니다. 여기서도 도메인이 가장 안정적이며 전통적인 의존성이 반전된다는 것이 분명합니다.


답변

나에게, 공식 기사에 설명 된 종속성 반전 원리 은 본질적으로 재사용 성이 떨어지는 모듈의 재사용 가능성을 높이고 C ++ 언어의 문제를 해결하는 방법으로 잘못 안내 된 시도입니다.

C ++의 문제는 헤더 파일에 일반적으로 개인 필드 및 메서드 선언이 포함되어 있다는 것입니다. 따라서 상위 레벨 C ++ 모듈에 하위 레벨 모듈의 헤더 파일이 포함 된 경우 해당 모듈의 실제 구현 세부 사항에 따라 다릅니다 . 그리고 그것은 분명히 좋은 것이 아닙니다. 그러나 이것은 오늘날 일반적으로 사용되는보다 현대적인 언어에서는 문제가되지 않습니다.

높은 수준의 모듈은 기본적으로 낮은 수준의 모듈보다 재사용 성이 떨어집니다. 전자는 일반적으로 후자보다 더 많은 응용 프로그램 / 컨텍스트입니다. 예를 들어, UI 화면을 구현하는 구성 요소는 응용 프로그램에 따라 최상위 수준이며 매우 완전합니다. 다른 응용 프로그램에서 이러한 구성 요소를 재사용하려고하면 생산성이 떨어지고 과도한 엔지니어링 만 가능합니다.

따라서 컴포넌트 B에 의존하는 (A에 의존하지 않는) 컴포넌트 A의 동일한 레벨에서 별도의 추상화를 생성하는 것은 컴포넌트 A가 실제로 다른 애플리케이션이나 컨텍스트에서 재사용하는 데 유용 할 경우에만 수행 할 수 있습니다. 그렇지 않다면 DIP를 적용하는 것이 좋지 않습니다.


답변

기본적으로 그것은 말합니다 :

클래스는 특정 세부 사항 (구현)이 아닌 추상화 (예 : 인터페이스, 추상 클래스)에 의존해야합니다.


답변

좋은 답변과 좋은 예는 이미 다른 사람들이 제공 한 것입니다.

DIP 이유 가 중요한 는 OO 원칙 “느슨하게 결합 된 디자인”을 보장하기 때문입니다.

소프트웨어의 개체는 하위 개체에 따라 일부 개체가 최상위 개체 인 계층으로 들어가서는 안됩니다. 낮은 수준의 개체를 변경하면 최상위 개체로 리플 스루되어 소프트웨어가 변경되기 매우 취약합니다.

‘최상위’개체가 매우 안정적이며 변경에 취약하지 않기를 원하므로 종속성을 반전시켜야합니다.