Skip to main content

8. 의존성 관리하기

01. 의존성 이해하기#

변경과 의존성#

  • 의존성은 실행 시점과 구현 시점에 서로 다른 의미를 가집니다.
    • 실행 시점: 의존하는 객체가 정상적으로 동작하기 위해서는 실행 시에 의존 대상 객체가 반드시 존재해야 합니다.
    • 구현 시점: 의존 대상 객체가 변경될 경우 의존하는 객체도 함께 변경됩니다.

UML과 의존성

  • UML에서는 두 요소 사이의 관계로 실체화 관려, 연관 관계, 의존 관계 등이 있습니다.
  • 이 장에서의 의존성은 UML에서의 의존 관계로 보면 안되고, 공통의 특성으로 봐야합니다.

의존성 전이#

  • 의존성 전이(transitive dependency) 가 의미하는 것은 A가 B에 의존할 경우, A는 B가 의존하는 대상에 대해서도 자동적으로 의존하게 되는 것입니다.
  • 의존성은 전이될 수 있기 때문에 의존성의 종류를 직접 의존성(direct dependency)간접 의존성(indirect dependency) 로 나눠집니다.

런타임 의존성과 컴파일타임 의존성#

  • 런타임 의존성은 애플리케이션이 실행되는 시점을 가리킵니다.
  • 컴파일타임 의존성은 컴파일이나 문맥에 따라 코드 작성 시점을 가리키기도 합니다.
  • 런타임 의존성과 컴파일타임 의존성이 다를 수 있으며, 유연하고 재사용 가능한 코드를 설계하기 위해서는 두 종류의 의존성을 서로 다르게 만들어야 합니다.
  • 컴파일타임 구조와 런타임 구조 사이의 거리가 멀수록 설계가 유연해지고 재사용 가능해집니다.

컨텍스트 독립성#

  • 클래스가 사용될 특정 문맥에 대해 최소한의 가정만으로 이뤄져 있다면 다른 문맥에서 재사용하기가 더 수월해지며 이를 컨텍스트 독립성이라고 부릅니다.

의존성 해결하기#

  • 컴파일타임 의존성은 구체적인 런타임 의존성으로 대체돼야 합니다.
  • 의존성을 해결하기 위해 일반적으로 다름과 같은 세 가지 방법을 사용합니다.
    • 객체를 생성하는 시점에 생성자를 통해 의존성 해결
    • 객체 생성 후 setter 메서드를 통해 의존성 해결
    • 메서드 실행 시 인자를 이용해 의존성 해결

02. 유연한 설계#

의존성과 결합도#

  • 객체지향 패러다임의 근간은 협력입니다.
  • 의존성이 과한 것은 문제가 될 수 있습니다.
  • 이에 대한 해결책은 의존성을 바람직하게 만드는 것입니다.
  • 바람직한 의존성은 재사용성과 관련이 있습니다.
  • 특정한 컨텍스트에 강하게 의존하는 클래스를 다른 컨텍스트에서 재사용할 수 있는 유일한 방법은 구현을 변경하는 것입니다.
  • 두 요소 사이에 존재하는 의존성이 바람직할 때 두 요소가 느슨한 결합도(loose coupling) 또는 약한 결합도(weak coupling) 을 가진다고 말합니다.

지식이 결합을 낳는다#

  • 서로에 대해 알고 있는 지식의 양이 결합도를 결정합니다.
  • 더 많이 알수록 더 많이 결합됩니다.
  • 이에 대한 해결책은 추상화입니다.

추상화에 의존하라#

  • 추상화란 어떤 양상, 세부사항, 구조를 좀 더 명확하게 이해하기 위해 특정 절차나 물체를 의도적으로 새략하거나 감춤으로써 복잡도를 극복하는 방법입니다.
  • 추상화와 결합도의 관점에서 의존 대상을 다음과 같이 구분하는 것이 유용합니다.
    • 구체 클래스 의존성(concreate class dependency)
    • 추상 클래스 의존성(abstract class dependency)
    • 인터페이스 의존성(interface dependency)
  • 인터페이스에 의존하면 상속 계층을 모르더라도 협력이 가능해집니다.

명시적인 의존성#

  • 모든 경우에 의존성은 명시적으로 퍼블릭 인터페이스에 노출되며 이를 명시적인 의존성(explicit dependency) 라고 부릅니다.
  • 의존성이 퍼블릭 인터페이스에 표현되지 않는 것을 숨겨진 의존성(hidden dependency) 라고 부릅니다.
  • 의존성은 명시적으로 표현되어야 합니다.

new는 해롭다#

  • 결합도 측면에서 new가 해로운 이유는 두가지 입니다.
    • new연산자를 사용하기 위해서는 구체 클래스의 이름을 직접 기술해야합니다. 즉, new를 사용하는 클래스에 의존할 수 밖에 없기 때문에 결합도가 높아집니다.
    • new연산자를 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야합니다. 즉, new를 사용하면 클라이언트가 알아야 하는 지식의 양이 늘어나기 때문에 결합도가 높아집니다.
  • 결합도가 높으면 변경에 의해 영향을 받기 쉬워집니다.
  • 이를 해결하는 방법은 인스턴스를 생성하는 로직과 사용하는 로직을 분리하는 것입니다.
  • 사용과 생성의 책임을 분리하고, 의존성을 생성자에게 명시적으로 드러내고, 구체 클래스가 아닌 추상 클래스에 의존하게 함으로써 설계를 유연하게 만들 수 있습니다.

가끔은 생성해도 무방하다#

  • 클래스 안에서 객체의 인스턴스를 직접 생성하는 방식이 유용한 경우가 있습니다.
  • 주로 협력하는 기본 객체를 설정하고 싶은 경우가 여기에 속합니다
  • 설계는 결합도와 사용성의 트레이드 오프입니다.

표준 클래스에 대한 의존은 해롭지 않다#

  • 의존성이 불편한 이유는 그것이 항상 변경에 대한 영향을 암시하기 때문입니다.
  • 의존성에 의한 영향이 적은 경우에도 추상화에 의존하고 의존성을 명시적으로 드러내는 것은 좋은 설계 습관입니다.

컨텍스트 확장하기#

  • 예시에서서 영화 할인 정책이 없는 경우에는 null로 처리하는 것 보다는 할인정책이 없는 클래스를 만드는 것이 낫습니다.
  • 위의 방법은 추상화에 의존하고, 생성자를 토애 의존성을 명시적으로 드러내고, 직접적으로 다뤄야하는 책임을 Movie 외부로 옮겼기 때문입니다.

조합 가능한 행동#

  • 어떤 객체와 협력하느냐에 따라 객체의 행동이 달라지는 것은 유연하고 재사용 가능한 설계가 가진 특징입니다.
  • 유연하고 재사용 가능한 설계는 응집도가 높은 책임들을 가진 작은 객체들을 다양한 방식으로 열녁랗ㅁ으로써 애플리케이션의 기능을 쉽게 확장할 수 있습니다.
  • 유연하고 재사용 가능한 설계는 작은 객체들의 행동을 조합함으로써 새로운 행동을 이끌어낼 수 있는 설계입니다.
Last updated on