의존성 역전 원칙에서 말하는 ‘유연성이 극대화된 시스템’이란 소스 코드 의존성이 추상(Abstraction)에 의존하며 구체(Concretion)에는 의존하지 않는 시스템이다.
자바와 같은 정적 타입 언어에서 이 말은 use
, import
, include
구분은 오직 인터페이스나 추상 클래스 같은 추상적인 선언만을 참조해야 한다는 뜻이다.
마찬가지로 동적 타입 언어에서도 소스 코드 의존 관계에서 구체 모듈은 참조해서는 안된다.
하지만 구체 모듈이 무엇인지 정의하기 다소 어렵고, 호출할 함수가 구현된 모듈이라면 참조하지 않기가 특히 어렵다.
소프트웨어 시스템이라면 구체적인 많은 장치에 반드시 의존하기 때문에 규칙으로 보기에 비현실적이다.
- ex) 자바
String
은 구체 클래스- 애써 추상 클래스로 만들려는 시도는 현실성이 없다.
java.lang.String
구체 클래스에 대한 소스 코드 의존성은 벗어날 수 없고, 벗어나서도 안된다.
String
클래스는 변경되는 일이 거의 없고, 있더라도 엄격하게 통제되므로 매우 안정적이다.
- 개발자와 아키텍트는
String
클래스에서 변경이 자주 발생할 것이라고 염려할 필요가 없다.
이러한 이유로 DIP를 논할 때 운영체제나 플랫폼 같이 안정성이 보장된 환경에 대해서는 무시하는 편이다.
- 환경에 대한 의존성을 용납하는 이유는 변경되지 않기 때문이다.
즉, 변동성이 큰(Volatile) 구체적인 요소가 의존하지 않도록 만들어야 하는 목표이며, 구체적인 요소는 지속적인 개발을 통해 계속 변경될 수 밖에 없는 모듈들이다.
안정된 추상화
안정된 소프트웨어 아키텍처란 변동성이 큰 구현제에 의존하는 일은 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처이다.
인터페이스는 구현체보다 변동성이 낮다.
따라서 인터페이스를 변경하지 않고도 구현체에 기능을 추가할 수 있는 방법을 통해 인터페이스의 변동성을 낮춰야한다.
- 변동성이 큰 구체 클래스를 참조하지 말라.
- 추상 인터페이스를 참조하라
- 객체 생성 방식을 강하게 제약하며, 일반적으로 추상 팩토리(Abstract Factory)를 사용하도록 강제한다.
- 변동성이 큰 구체 클래스로부터 파생하지 말라.
- 상속은 소스 코드에 존재하는 모든 관계 중에서 가장 강력한 동시에 뻣뻣해서 변경하기 어렵다.
- 상속을 아주 신중하게 사용해야한다.
- 구체 함수를 오버라이드 하지 말라.
- 구체 함수는 대체로 소스 코드 의존성을 필요로한다.
- 구체 함수를 오버라이드 하면 이러한 의존성을 제거할 수 없게되며, 그 의존성을 상속하게 된다.
- 추상 함수로 선언하고 구현체들에서 각자의 용도에 맞게 구현한다.
- 구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말라.
팩토리
사실상 모든 언어에서 객체를 생성하려면 해당 객체를 구체적으로 정의한 코드에 대해 소스 코드 의존성이 발생하므로, 변동성이 큰 구체적인 객체는 특별히 주의해서 생성해야한다.
대다수의 객체 지향 언어에서 바람직하지 못한 의존성을 처리할 때 추상 팩토리를 사용할 수 있다.
- 소스 코드 의존성
- 곡선은 구체적인 것들로부터 추상적인 것들은 분리하고 있다.
- 소스 코드 의존성은 해당 곡선과 교차할 때 모두 추상적인 쪽으로 향한다.
- 컴포넌트
- 시스템이 곡선을 기준으로 추상 컴포넌트와 구체 컴포넌트로 분리된다.
제어흐름은 소스 코드 의존성과는 정반대 방향으로 곡선을 가로지르고 있는데, 소스 코드 의존성은 제어흐름과는 반대 방향으로 역전된다.
이러한 이유로 이 원칙을 의존성 역전이라고 부른다.
구체 컴포넌트
위 예제의 경우 ServiceFactoryImpl
구체 클래스가 ConcreteImpl
구체 클래스에 의존하고 있으므로 DIP를 위배한다고 볼 수 있다.
DIP 위배를 모두 없앨수는 없지만, DIP를 위배하는 클래스들은 적은 수의 구체 컴포넌트 내부로 모을 수 있고, 이를 통해 시스템의 나머지 부분과는 분리할 수 있다.
결론
DIP는 고수준의 아키텍처 원칙에서 지속적으로 언급되는 규칙이다.
DIP는 아키텍처 다이어그램에서 가장 눈에 드라나는 원칙이 될 것이다.
곡선은 아키텍처의 경계가 되고, 의존성은 곡선을 경계로 더 추상적인 엔티티가 있는 쪽으로만 향한다.