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