오브젝트 - 08. 의존성 관리하기

강준혁·2022년 9월 15일
0

오브젝트

목록 보기
9/14
post-thumbnail

협력은 필수적이지만 과도한 협력은 설계를 곤경에 빠뜨릴 수 있다. 다른 객체와의 협력을 위해서는 자연히 객체간 의존성이 생겨난다.
그리고 과도한 의존성은 애플리케이션을 수정하기 어렵게 만든다.

객체지향 설계의 핵심은 협력을 위해 필요한 의존성은 유지하면서도 변경을 방해하는 의존성은 제거하는데에 있다.

의존성 이해하기

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

객체지향 어플리케이션에서 런타임의 주인공은 객체다. 따라서 런타임 의존성이 다루는 주체는 객체 사이의 의존성이다.

반면 코드 관점에서 주인공은 클래스다. 따라서 컴파일타임 의존성이 다루는 주제는 클래스 사이의 의존성이다.

class Movie {
  private DiscountPolicy discountPolicy;
  
  public methodA() {
    return this.discountPolicy.call();
  }
}

abstract class DiscountPolicy {
  call();
}

class DiscountPolicyA extends DiscountPolicy {
  ...
}

class DiscountPolicyB extends DiscountPolicy {
  ...
}

위 예제에서 Movie 클래스는 DiscountPolicy 클래스에만 의존하고,
DiscountPolicyA, DiscountPolicyB는 전혀 알지 못한다.
즉 컴파일 타임 의존성은 Movie 는 DiscountPolicy에만 의존한다.

하지만 런타임 시점에서는 구현클래스인 A 또는 B와 협력해야 한다.

유연하고 재사용 가능한 설계를 창조하기 위해서는 동일한 소스코드 구조를 가지고 다양한 실행구조를 만들 수 있어야 한다.

어떤 클래스의 인스턴스가 다양한 클래스의 인스턴스와 협력하기 위해서는 협력할 인스턴스의 구체적인 클래스를 알아서는 안된다. 실제로 협력할 객체가 어떤 것인지는 런타임에 해결해야 한다.

클래스가 협력할 객체의 클래스를 명시적으로 드러내고 있다면 다른 클래스의 인스턴스와 협력할 가능성 자체가 없어진다.

따라서 컴파일타임 구조와 런타임 구조 사이의 거리가 멀면 멀수록 설계가 유연해지고 재사용 가능해진다.

지식이 결합을 낳는다

서로에 대해 알고있는 지식의 양이 결합도를 결정한다.
만약 Movie 가 추상클래스가 아닌 구현 클래스 A 에 의존한다고 했을 때에는 구체적인 구현에 대해 의존하게 된다.
반면 추상클래스에만 의존하게 될 경우 그저 인터페이스에 대해서만 알 뿐, 구체적인 구현에 대해서는 알지 못한다.

더 많이 알수록 더 많이 결합된다. 더 많이 알고 있다는 것은 더 적은 컨텍스트에서 재사용 가능하다는 것을 의미한다.

결합도를 느슨하게 유지하려면 협력하는 대상에 대해 더 적게 알아야 하며 필요한 정보 외에 최대한 감추는 것이 중요하다.

명시적인 의존성

의존성을 해결하는 방법에는 생성자, setter 메서드, 메서드 인자를 사용하는 세가지 방식이 존재한다.

위 방식으로 의존성을 처리한다면 모든 경우에 명시적으로 퍼블릭 인터페이스에 노출된다. 이를 명시적인 의존성이라 부른다.

반면 내부에서 new 를 통해 인스턴스를 생성한다면 퍼블릭 인터페이스에는 표현되지 않는다. 이를 숨겨진 의존성이라 부른다.

new는 해롭다

  • new 연산자를 사용하기 위해서는 구체 클래스의 이름을 직접 기술해야 한다.
    따라서 추상화가 아닌 구체클래스에 의존할 수밖에 없기 때문에 결합도가 높아진다.
  • new 연산자는 구체클래스 뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다. 따라서 관련 구체클래스에 대한 필요 지식의 양이 늘어나기 때문에 결합도가 높아진다.

가끔은 생성해도 무방하다

거의 모든 경우에 특정 구현 클래스와 협력해야 하는 경우에는, 디폴트 설정을 해당 구현클래스와 협력하게 하고 그 외의 경우를 별도로 처리할 수도 있다.

조합 가능한 행동

어떤 객체와 협력하느냐에 따라 객체의 행동이 달라지는 것은 유연하고 재사용 가능한 설계가 가진 특징이다. 유연하고 재사용 가능한 설계는 응집도 높은 책임들을 가진 작은 객체들을 다양한 방식으로 연결함으로써 애플리케이션의 기능을 쉽게 확장할 수 있다.

유연하고 재사용 가능한 설계는 객체가 어떻게 하는지를 장황하게 나열하지 않고도 객체들의 조합을 통해 무엇을 하는지를 표현하는 클래스로 구성된다.
따라서 클래스의 인스턴스를 생성하는 코드를 보는 것 만으로 객체가 무엇을 하는지 쉽게 파악할 수 있다.

profile
백엔드 개발자

0개의 댓글