선생님, 개발을 잘하고 싶어요.

[오브젝트] 챕터 8 - 의존성 관리하기 본문

일상/책 리뷰

[오브젝트] 챕터 8 - 의존성 관리하기

알고싶은 승민 2022. 3. 14. 20:00

OOP는 객체간에 메시지로 협력하며 기능을 구현하는 것은 좋습니다.

하지만 이걸론 부족합니다. 협력을 위해서 서로를 참조하며 코드가 커지다 보면 참조의 참조의 참조 문제가 발생하게 됩니다.

 

하나를 수정하면 그 파급효과가 저 멀리까지 퍼진다는 거죠.

 

그런 문제가 일어나는 이유는 잘못된 의존성 설계 때문에 그렇습니다.

이번 챕터에서는 의존성이 그 자체로 잘못된 것이 아님을 밝히고 어떻게하면 현명하게 사용할 수 있는지 알려줍니다.

 

의존성의 바람을 바람직 하게 만들라고 조언합니다. 그 방법에 대해서 정확하게 알려주진 않는 듯 합니다.

어떤 의존성이 바람직한가? 를 알려면 아키텍쳐에 대한 학습이 병형되어야 한다고 생각합니다. 클린 아키텍쳐에서 봤던 경계를 기준으로 단방향 의존성을 가진 시스템을 설계하는 게 괜찮을 것 같네요.

 

부주의한 생성자 활용이 코드 결합도를 높히므로 클라이언트 쪽으로 생성의 책임을 분리하라는 말이 나오는데 DI Framework가 생각났습니다. 제가 지금까지 활용한 DI는 뭔가 Configuration 관점에서 생각하진 않은 것 같네요. 좀더 생성과 기능의 분류를 추출 격리하기 위한 용도로 사용하는 용례를 고민해봐야겠습니다.

 


  • 객체지향 애플리케이션 → 작고 응집도 높은 객체들로 구성
    • 책임의 초점이 명확하고 한 가지 일만 잘하는 객체
  • 단독으로 수행할 수 있는 작업은 거의 없다.
  • 다른 객체의 도움이 필요하다. → 이 과정에서 메시지를 보내게 되고 협력을 낳는다.
  • 협력은 객체가 다른 객체에 대해 알 것을 강요한다.
    • 그런 객체가 존재한다는 사실을 알아야한다.
    • 수신할 수 있는 메시지에 대해 알아야한다.
    • 이러한 지식이 객체 사이의 의존성을 만든다.
  • 의존성은 수정을 어렵게 만든다.
  • 협력을 위해 필요한 의존성 유지하며 변경 방해하는 의존성은 제거해야한다.

<aside> 💡 충분히 협력적이고 유연한 객체를 만들기 위해 의존성을 관리하는 방법을 알아본다.

</aside>

  • 의존성 이해하기
    • 변경과 의존성
      • 실행 시점: 실행 시 의존 대상 객체가 반드시 존재해야 한다.
      • 구현 시점: 의존 대상 객체 변경시 의존하는 객체도 함께 변경된다.
      • 의존성은 변경에 의한 영향의 전파 가능성을 암시한다.
      • 클래스 의존 이외에 다양한 의존이 있을 수 있다. (인자로 받기, 필드로 가지기, 구현하기 등)
    • 의존성 전이
      • 의존하고 있는 객체가 의존하는 객체에 간접적으로 의존하게 되는 것
      • 항상 전이되는 건 아니고, 변경의 방향과 캡슐화의 정도에 따라서 달라진다.
    • 런타임 의존성과 컴파일타임 의존성
      • 런타임 → 실행 시점, 컴파일타임 → 작성한 코드 구조
      • 런타임의 주인공은 객체
        • 객체 사이의 의존성이 주요 아젠다
      • 컴파일타임의 주인공은 클래스
        • 클래스 사이의 의존성이 주요 아젠다
      • 실제로 협력할 객체가 어떤 것인지는 런타임에 해결해야 한다.
      • 컴파일타임 구조와 런타임 구조 사이의 거리가 멀면 멀수록 설계가 유연해지고 재사용 가능해진다.
        • 하지만... 이해하기 어려워지지
    • 컨텍스트 독립성
      • 구체적인 클래스를 알면 알수록 그 클래스를 활용하는 문맥에 강하게 결합된다.
        • 컴파일 에러 <<< 런타임 에러 <<< Context 에러 순으로 해결하기 어렵다.
        • 특정 문맥에 강하게 결합될 경우 부주의한 Context 에러가 발생할수 있다.
      • 컨텍스트 독립적이다?
        • 각 객체가 해당 객체를 실행하는 시스템 문맥을 알지 못해야한다.
    • 의존성 해결하기
      • 의존성 해결: 컴파일타임 의존성을 적절한 런타임 의존성으로 교체하는 것
        • 생성자
        • setter
        • 메서드 파라미터
        • 각 방법을 혼합하여 사용할 수 있다.
  • 유연한 설계</aside>
    • 의존성과 결합도
      • 객체간 상호작용은 필수 즉 의존성은 필수
      • 의존성의 정도가 문제
      • 의존성을 바람직하게 만들자.
        • 협력 대상이 정확한 인스턴스일 필요 없이 “이런 메시지를 수신할 수 있는” 타입이된다.
      • 바람직한 의존성은 재사용성과 긴밀한 관계
      • 결합도: 의존성의 정도
      • 어떤 의존성이 재사용을 방해한다면 결합도가 강하다고 표현할 수 있다.
    • 추상화에 의존하라
      • 추상화:
        • 문제를 해결하는 데 불필요한정보를 감춤
        • 결합도를 느슨하게 유지 가능
      • 결합도가 강한 순 추상화
        • 구체 클래스 의존
        • 추상 클래스 의존
        • 인터페이스 의존 → 어떤 메시지를 수신할 수 있는지에 대한 지식만 남음
    • 명시적 의존성
      • 의존성을 명시적으로 퍼블릭 인터페이스에 노출하는 것
      • 의존성을 명시적으로 드러내면 코드를 직접 수정하는 위험을 피할 수 있다.
      <aside> 💡 의존성을 명시적으로 표현하고 구현 내부에 숨기지 마라.
    • </aside>
    • new는 해롭다.
      • 생성자 호출은 결합도를 높힌다.
      • 생성 로직과 사용로직을 분리하라.
      • 썰계를 유연하게 하려면
        • 사용과 생성 책임 분리
        • 의존성을 명시적으로 드러내기
        • 추상클래스에 의존하기
      • 객체를 생성하는 책임은 클라이언트로 옮기자.
    • 가끔은 생성해도 무방하다.
      • 설계는 트레이드 오프
      • 구체 클래스에 의존하더라도 클래스 사용성을 높힐 수 있다면 해도 괜찮을지도
    • 표준 클래스에 대한 의존은 해롭지 않다.
      • String에 대한 의존이 해롭겠냐?
    • 컨텍스트 확장하기
      • 구체 클래스를 직접 다루는 책임을 Client로 옮기면 OCP 달성가능
    • 조합 가능한 행동</aside></aside>
    • <aside> 💡 객체의 조합을 선언적으로 표현하여 객체들이 무엇을 하는지 표현하는 설계가 좋은 설계
    • <aside> 💡 선언적으로 객체의 해동을 정의할 수 있다.
  • <aside> 💡 의존성 관리를 위한 유용한 원칙 & 기법 소개
Comments