DIP 란?
의존성 역전 원칙은 유연성을 극대화시키는데 그 목적이 있다.
이는 소스 코드 의존성이 추상(abstraction)에 의존하며,
구체(concretion)에는 의존하지 않는 시스템을 이야기한다.
즉, 정적 타입의 언어에서는 use, import, include를 통해 인터페이스 혹은 추상 클래스와 같은 추상적인 선언만 참조해야한다는 뜻이다.
하지만 정적타입 언어인 JAVA를 예로들면, String이나 Integer와 같은 구체클래스도 사용하면 안된다는 걸까?
그렇지 않다. String이나 Integer와 같은 클래스는 매우 안정적인 클래스 이기 때문에 변경되는 일이 적다. 따라서 DIP를 논할 때, 운영체제나 플랫폼 같이 안정성이 보장된 환경에 대해서는 무시하는 편이다.
즉, 우리가 주의해야할 것은 변동성이 큰 구체적인 요소라고 생각하면 된다.
안정된 추상화
안정된 소프트웨어 아키텍처란 변동성이 큰 구현체에 의존하는 일은 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처라는 뜻이다.
이를 위한 실천법을 정리해보자
- 변동성이 큰 구체 클래스를 참조하지 말라. 대신 추상 인터페이스를 참조하라. (추상 팩토리 사용)
- 변동성이 큰 구체 클래스로부터 파생하지 말라. (상속 X)
- 구체 함수를 오버라이드(함수 덮어쓰기) 하지 말라. (차라리 추상 함수로 선언하고 구현체들에서 구현)
- 구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말라.
팩토리
추상 팩토리는 변동성이 큰 구체 클래스의 참조를 피하기 위해서 사용한다고 보면 된다.
아래 예시를 통해서 추상 팩토리 사용에 대해서 생각해보도록 하자.
- Application은 Service 인터페이스를 통해서 ConcreteImpl를 사용한다. 이를 위해서 어떻게든 Application에서 ConcreteImpl 생성이 필요하다.
- ConcreteImpl에 대해 코드 의존성을 만들지 않으면서 그 목적을 이루기 위해 ServiceFactory인터페이스의 makeSvc 함수를 사용해 객체를 생성한다.
- ServiceFactory는 ServiceFactoryImpl을 통해 구현되고 makeSvc에서 ConcreteImpl의 객체를 생성하여 Service타입으로 반환해준다.
- 곡선은 추상컴포넌트와 구체컴포넌트로 나누고있다.
- 소스 코드의 의존성의 화살은 추상적인 쪽으로 향한다.(의존성과 반대 방향으로 흐름 => 의존성 역전)
구체 컴포넌트
위 예시를 보게 되면 구체 컴포넌트에는 구체적인 의존성이 한개 존재한다 (ServiceFactoryImpl 구체 클래스가 ConcreteImpl 구체 클래스에 의존).
이와 같이 대다수 시스템은 이러한 구체 컴포넌트를 최소한 하나는 포함할 것이다. (예. main 함수) 위 예시에서는 main함수에서 ServicFactoryImpl 객체를 생성하여 ServiceFactory 타입의 전역 변수로 지정하였을 것이고, 이를 Application 객체가 접근하여 Service객체를 받아올 것이다.
결론
DIP는 앞으로의 고수준 아키텍처 원칙을 다루게 되면서 자주 마주칠 것이다. 추후에는 이 규칙을 의존성 규칙(Dependency Rule)이라고 부를 것이다.
'Design > Architecture' 카테고리의 다른 글
[Clean Architecture] 컴포넌트 응집도 (0) | 2021.11.01 |
---|---|
[Clean Architecture] 컴포넌트 (0) | 2021.10.13 |
[Clean Architecture]SOLID 원칙 : 4.ISP 인터페이스 분리 원칙 (0) | 2021.09.26 |
[Clean Architecture]SOLID 원칙 : 3.LSP 리스코프 치환 원칙 (0) | 2021.09.16 |
[Clean Architecture]SOLID 원칙 : 2.OCP 개방-폐쇄 원칙 (0) | 2021.09.14 |