Hi yoahn 개발블로그

11장 DIP: Dependency Inversion Principle 의존성 역전 원칙 본문

스터디/클린아키텍처

11장 DIP: Dependency Inversion Principle 의존성 역전 원칙

hi._.0seon 2023. 8. 20. 23:37
반응형
고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안된다. 세부사항이 정책에 의존해야 한다.

유연성이 극대화된 시스템이란?

소스 코드 의존성이 추상에 의존하며 구체에는 의존하지 않는 시스템

 

자바와 같은 정적 타입 언어에서, use, import, include 구문은 오직 인터페이스나 추상 클래스 같은 추상적인 선언만을 참조해야 한다는 뜻이다.

구체적인 대상에는 절대로 의존해서는 안된다.

 

루비나 파이썬과 같은 동적 타입 언어에도 동일한 규칙이 적용된다. 소스코드 의존 관계에서 구체 모듈은 참조해서는 안된다. 하지만 구체 모듈이 무엇인지를 정의하기가 다소 어렵다.

 

소프트웨어 시스템이라면 구체적인 많은 장치에 의존한다.

java 의 String 클래스는 구체 클래스이다. 이를 추상 클래스로 만들려는 시도는 현실성이 없다. java.lang.String 구체 클래스에 대한 소스 코드 의존성은 벗어날 수 없고, 벗어나서도 안된다.

반면 String 클래스는 매우 안정적이다. 변경될 일이 거의 없고, 엄격하게 통제된다.

이런 이유로 DIP 얘기에서 운영체제나 플랫폼 같이 안정성이 보장된 환경에 대해서는 무시하는 편이다.

환경에 대한 의존성은 용납함. 변경되지 않는다면 의존할 수 있다.

 

우리가 의존하지 않도록 피하고자 하는 것은 바로 변동성이 큰 구체적인 요소다. 이는 우리가 열심히 개발하는 중이라 자주 변경될 수밖에 없는 모듈들이다.

안정된 추상화

인터페이스에 변경이 생기면 구현체들도 따라서 수정해야 한다.

반대로 구체적인 구현체에 변경이 생기더라도 인터페이스는 변경될 필요가 없다.

→ 인터페이스는 구현체보다 변동성이 낮다

소프트웨어 설계의 기본

인터페이스를 변경하지 않고도 구현체에 기능을 추가할 수 있는 방법을 찾는 것

안정된 소프트웨어 아키텍처

→ 안정된 추상 인터페이스에 의존하는 것

  1. 변동성이 큰 구체 클래스를 참조하지 말라
    1. 추상 인터페이스를 참조하기
    2. 객체 생성 방식을 강하게 제약하며, 추상 팩토리를 사용하도록 강제한다.
  2. 변동성이 큰 구체 클래스로부터 파생하지 말라
    1. 상속은 소스 코드에 존재하는 모든 관계 중에서 가장 강력 & 뻣뻣해서 변경하기 어렵다
    2. 의존성을 거는 일이므로 상속은 신중하게 사용해야 한다.
  3. 구체 함수를 오버라이드 하지 말라
    1. 구체 함수는 소스 코드 의존성을 필요로 한다. → 의존성을 제거할 수 없게 된다. (의존성 상속)
    2. 추상 함수로 선언하고 구현체에서 각자의 용도에 맞게 구현해야 한다.
  4. 구체적이며 변동성이 크다면 그 이름을 언급하지 마라 (DIP)

팩토리

변동성이 큰 구체적인 객체는 주의해서 생성해야 한다.

객체를 생성하려면 구체적으로 정의한 코드에 의존성이 발생 → 추상 팩토리를 사용해서 의존성 해결

  • Application → Service 인터페이스를 통해 ConcreteImpl 을 사용
    • ConcreteImpl의 인스턴스를 생성해야 한다.
  • Application → ServiceFactory 인터페이스의 makeSvc 메서드 호출
    • ConcreteImpl에 대해 소스 코드 의존성을 만들지 않으면서 ConcreteImpl 을 생성하기 위해
  • ServiceFactoryImpl 구현체가 ConcreteImpl의 인스턴스를 생성한 후 Service 타입으로 반환한다.

(어쨌든 소스 코드 의존성이 생기는건 맞지 않나?)

 

빨간 곡선은 아키텍처 경계를 뜻한다

  • 구체적인 것들로부터 추상적인 것들을 분리한다.
    • 추상 컴포넌트
      • 애플리케이션의 모든 고수준 업무 규칙을 포함한다.
    • 구체 컴포넌트
      • 구체 컴포넌트는 업무 규칙을 다루기 위해 필요한 모든 세부사항을 포함한다.
  • 소스 코드 의존성은 해당 곡선과 교차할 때 모두 추상적인 쪽으로 향한다.

의존성 역전

: 소스 코드 의존성은 제어 흐름과는 반대 방향으로 역전된다

구체 컴포넌트

11.1의 구체 컴포넌트에는 구체적인 의존성이 하나 → DIP 에 위배된다.

DIP 위배를 모두 없앨 수는 없다. 하지만 DIP 를 위배하는 클래스들은 적은 수의 구체 컴포넌트 내부로 모을 수 있고, 이를 통해 시스템의 나머지 부분과는 분리할 수 있다.

 

DIP 를 위배하는 클래스들은 모은 구체 컴포넌트 → Main 컴포넌트라고 함

main() 을 포함하기 때문.

 

ServiceFactoryImpl 인스턴스를 생성한 후 이 인스턴를 ServiceFactory 타입으로 전역변수에 저장

Application은 이 전역 변수를 이용해서 ServiceFactoryImpl의 인스턴스에 접근

결론

그림 11.1의 곡선은 아키텍처의 경계가 된다.

의존성은 이 곡선을 경계로 더 추상적인 엔티티가 있는 쪽으로만 향한다. (의존성 규칙이라고 함)

 

의존성 해결하는 부분에서 엄청난 통찰을 얻어가는 느낌이었던 파트였다.

반응형
Comments