본문 바로가기

공부기록/객체지향

오브젝트 8장 - 의존성 관리하기

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

의존성 이해하기

변경과 의존성

의존성은 실행 시점과 구현 시점에 서로 다른 의미를 가진다.

구현 시점 → 인터페이스에 의존해야함

실행 시점 → 각각의 구현체에 의존해야함

(서로 다른게 유연하게 작성된 애플리케이션)

두 요소 사이의 의존성은 의존되는 요소가 변경될 때 의존하는 요소도 함께 변경될 수 있다는 것을 의미한다.

의존성 전이

의존성 - 의존하고 있는 대상의 변경에 영향을 받을 수 있는 가능성

직접 의존 - 직접 의존하는 것

간접 의존 - 한 단계 거치는 것, 해당 코드를 수정했을 때, 그것을 사용하는 코드들이 영향 받을 가능성이 있음

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

컴파일에는 인터페이스에 의존, 런타임에는 실제로 협력할 객체.

컴파일 타임에 구체적인 클래스를 드러내고 있으면, 다른 인스턴스와 협력할 가능성이 사라짐.

그래서 컴파일 타임과 런타임 구조 사이의 거리가 멀면 설계가 유연해지고 재사용 가능해진다.

컨텍스트 독립성

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

컨텍스트 독립적이라는 말은 각 객체가 해당 객체를 실행하는 시스템에 관해 아무것도 알지 못한다. → 행위의 단위를 가지고 새로운 상황에 적용할 수 있다.

의존성 해결하기

의존성을 해결하는 데 여러가지 방법이 있다.

  • 객체를 생성하는 시점에 생성자를 통해 의존성 해결
  • 객체 생성 후 setter 메서드를 통해 의존성 해결
  • 메서드 실행 시 인자를 이용해 의존성 해결

좋은 방법은 생성자 방식과 setter 방식을 혼합하는 것. 처음부터 완전한 객체를 생성하고, 필요에 따라 setter 메서드를 사용하여, 의존 대상을 변경한다.

유연한 설계

의존성과 결합도

어떤 의존성이 다양한 환경에서 재사용할 수 있다면 그 의존성은 바람직한 것이다.

의존성을 가리키는 좀 더 세련된 용어 = 결합도. 의존성이 바람직할 때 느슨한 결합도, 약한 결합도. 의존성이 바람직하지 못할 때 단단한 결합도, 강한 결합도를 가진다고 한다.

지식이 결합을 낳는다

결합도는 한 요소가 자신이 의존하고 있는 다른 요소에 대해 알고 있는 정보의 양으로 결정된다. 한 요소가 다른 요소에 대해 더 많은 정보를 알고 있을수록 두 요소는 강하게 결합된다. → 인터페이스를 결합하면 행위만 알게되고 내부 구현까지는 알필요가 없게 되므로(??) 협력하는 대상에 대해 필요한 정보 외에는 최대한 감추는 것이 중요하다.

추상화에 의존하라

특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법.

문제를 해결하는 데 불필요한 정보를 감출 수 있다.

결합도를 느슨하게 만들기 위해서는 구체적인 클래스보다 추상 클래스에, 추상 클래스보다 인터페이스에 의존하도록 만드는 것이 더 효과적이다.

명시적인 의존성

클래스 안에서 구체 클래스에 대한 모든 의존성을 제거해야만 한다. 의존성이 명시적이지 않으면 의존성을 파악하기 위해 내부 구현을 직접 살펴볼 수 밖에 없다. 의존성을 구현 내부에 숨겨두지 말자.

→ 인자를 new로 생성하더라도, 생성자를 여러개 둠으로서 해결가능??

new는 해롭다

  • new 연산자 → 구체 클래스의 이름을 직접 기술해야 한다. new를 사용하면, 구체 클래스에 의존할 수밖에 없기 때문에 결합도가 높아진다.
  • new 연산자는 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다. 따라서 new를 사용하면 클라이언트가 알아야 하는 지식의 양이 늘어나기 때문에 결합도가 높아진다.

new → 구체 클래스도 알아야하고, 생성자의 인자에 대한 정보도 알아야하므로, 결합도가 높아진다..

해결책 → 인스턴스를 생성하는 로직과 인스턴스를 사용하는 로직을 분리하는 것

가끔은 생성해도 무방하다

생성자의 내부에서 다른 객체의 생성자를 호출하는 경우 → 다른 생성자를 만들어서 다른 객체의 생성자와 해당 생성자를 호출하도록 한다.

모든 결합도가 모이는 새로운 클래스를 추가함으로써 사용성과 유연성이라는 두 마리 토끼 → FACTORY

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

변경될 확률이 거의 없는 클래스라면 의존성이 문제가 되지 않는다. ArrayList같은 경우 변경될 가능성이 없기 때문에, 해당 클래스를 의존하는 것이 문제가 없다.

컨텍스트 확장하기

코드상에 예외를 추가하지 말고, OCP를 활용해 새로운 구현체를 만들자.

조합 가능한 행동

훌륭한 객체지향 설계란 객체들의 조합을 선언적으로 표현함으로써 객체들이 무엇을 하는지를 표현하는 설계. 핵심은 의존성을 관리하는 것. 방법이 아니라, 목적에 집중할 수 있을 때 시스템의 행위를 변경하기가 더 쉽다.