DIP에 대한 개념은 다음과 같다.
추상화에 의존해야 하며, 구체화에 의존하면 안 된다.
즉, 하위의 모듈이 상위 모듈에 정의한 추상 타입(인터페이스)에 의존해야 한다.
의존성 역전 원칙에서 말하는 "유연성이 극대화된 시스템"은 소스코드의 의존성이 추상(abstraction)에 의존하며 구체(concretion) 에는 의존하지 않는 시스템이다.
인터페이스나 추상 클래스와 같은 추상적인 선언을 참조하고, 구체적인 대상에는 절대로 의존하면 안된다는 이야기이다.
우리가 DIP를 통해 의존하지 않도록 피하고자 하는 것은 자주 변경될 수 밖에 없는 변동성이 큰(volatile) 구체적인 모듈이다.
추상 인터페이스에 변경이 생기면, 이를 구체화한 구현체들도 따라서 일괄 수정이 필요하다. 반대로 구체적인 구현체에 변경이 생기더라도 그 구현체가 구현하는 인터페이스는 대다수의 경우 변경될 필요가 없다. 따라서 인터페이스는 구현체보다 변경성이 낮다.
즉, 안정된 소프트웨어 아키텍처란 변동성이 큰 구현체에 의존하는 일은 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처라는 뜻이다.
DIP는 다음의 매우 구체적인 코딩실천법으로 요약 가능하다.
변동성이 큰 구체 클래스를 참조하지 말라
변동성이 큰 구체 클래스로부터 파생하지 말라
구체 함수를 오버라이드 하지 말라
구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말라
자바에서는 의존성을 처리할 때 추상 팩토리를 사용하곤 한다.
이러한 코드 의존성을 만들지 않기 위해, ServiceFactory 인터페이스를 만들어 makeSvc 메서드를 호출하게 되고, 구체화 클래스인 ServiceFactoryImpl 클래스의 makeSvc를 통해 ConcreteImpl 인스턴스를 생성하여 전달하게 된다.
여기서 곡선은 아키텍처 경계를 뜻한다. 이는 구체적인 것으로부터 추상적인 것들을 분리한다. 소스 코드 의존성은 해당 곡선과 교차할 때 모두 추상적인 쪽으로 향한다.
또한 곡선은 시스템을 추상과 구체 컴포넌트로 분리한다.
추상 컴포넌트는 애플리케이션의 모든 고수준 업무 규칙을 포함하고, 구체 컴포넌트는 업무 규칙을 다루기 위해 필요한 모든 세부사항을 포함한다.
소스 코드 의존성은 제어흐름과는 반대 방향으로 역전된다. 이러한 이유로 이를 의존성 역전이라고 부른다.
DIP 위배를 모두 없앨 수는 없다. 하지만 DIP를 위배하는 클래스들은 적은 수의 구체 컴포넌트 내부로 모을 수 있고, 이를 통해 시스템의 나머지 부분과는 분리할 수 있다.
참조