변경과 의존성
실행 시점 : 의존하는 객체가 정상적으로 동작하기 위해서는 실행 시에 의존 대상 객체가 반드시 존재해야 한다.
구현 시점 : 의존 대상 객체가 변경될 경우 의존하는 객체도 함께 변경된다.
의존성 전이(transitive dependency)
직접 의존성(direct dependency) : 한 요소가 다른 요소에 직접 의존하는 경우
간접 의존성(indirect dependency) : 직접적인 관계는 존재하지 않지만 의존성 전이에 의해 영향이 전파되는 경우
런타임 의존성과 컴파일 타임 의존성
런타임 의존성(run-time dependency) : 애플리케이션이 실행되는 시점의 의존성
- 객체 사이의 의존성
컴파일타임 의존성(compile-time dependency) : 작성된 코드를 컴파일하는 시점 혹은 코드 그 자체
- 클래스 사이의 의존성
컨텍스트 독립성
클래스가 사용될 특정한 문맥에 대해 최소한의 가정만으로 이뤄져 있는 것
의존성 해결
컴파일 타임 의존성을 실행 컨텍스트에 맞는 적절한 런타임 의존성으로 교체 하는 것
- 객체를 생성하는 시점에 생성자를 통해 의존성 해결
- 객체 생성 후 setter 메서드를 통해 의존성 해결
- 메서드 실행 시 인자를 이용해 의존성 해결
의존성과 결합도
바람직한 의존성은 재사용성과 관련이 있다.
어떤 의존성이 다양한 환경에서 클래스를 재사용할 수 없도록 제한한다면 그 의존성은 바람직하지 못한 것이고, 반대라면 바람직한 것이다.
컨텍스트에 독립적인 의존성은 바람직한 의존성이고, 특정 컨텍스트에 강하게 결합된 의존성은 바람직하지 않은 의존성이다.
어떤 두 요소 사이에 존재하는 의존성이 바람직할 때 느슨한 결합도(loose coupling) 또는 약한 결합도(weak coupling)를 가진다고 말한다.
반대로 두 요소 사이의 의존성이 바람직하지 못할 때 단단한 결합도(tight coupling) 또는 강한 결합도(strong coupling)를 가진다고 말한다.
결합도의 정도는 한 요소가 자신이 의존하고 있는 다른 요소에 대해 알고 있는 정보의 양으로 결정된다.
추상화에 의존하라
추상화
- 어떤 양상, 세부사항, 구조를 좀 더 명확하게 이해하기 위해 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법
- 현재 다루고 있는 문제를 해결하는 데 불필요한 정보를 감춤
- 대상에 대해 알아야 하는 지식의 양을 줄일 수 있기 때문에 결합도를 느슨하게 유지할 수 있음
구체 클래스 의존성(concrete class dependency)
추상 클래스 의존성(abstract class dependency)
- 메서드의 내부 구현과 자식 클래스의 종류에 대한 지식을 클라이언트에게 숨길 수 있음
- 협력하는 대상이 속한 클래스 상속 계층이 무엇인지 알아야 함
인터페이스 의존성(interface dependency)
- 상속 계층을 모르더라도 협력이 가능해짐
실행 컨텍스트에 대해 알아야 하는 정보를 줄일수록 결합도가 낮아짐
- 의존하는 대상이 더 추상적일수록 결합도는 낮아짐
명시적인 의존성(explicit dependency)
public class Movie {
...
private DiscountPolicy discountPolicy;
public Movie(String title, Duration runningTime, Money fee) {
...
this.discountPolicy = new AmountDiscountPolicy(...);
}
}
Movie의 인스턴스 변수인 discountPolicy는 DiscountPolicy 타입으로 선언되어 있지만 생성자에서 AmountDiscountPolicy의 인스턴스를 직접 생성하기 때문에 AmountDiscountPolicy에도 의존하게 된다
- 숨겨진 의존성(hidden dependency) : 의존성이 퍼블릭 인터페이스에 표현되지 않음
숨겨진 의존성을 해결하려면 생성자, setter 메서드, 메서드 인자를 사용해서 명시적으로 퍼블릭 인터페이스로 노출 시키자
- 다른 컨텍스트에서 재사용하기 위해 내부 구현을 직접 수정해야 하는 위험을 피할 수 있음
- 실행 컨텍스트에 적절한 의존성을 선택할 수 있음
new는 해롭다
결합도가 높아짐
- new 연산자를 사용하기 위해서는 구체 클래스의 이름을 직접 기술해야 한다
- new 연산자는 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다
인스턴스를 생성하는 로직과 사용하는 로직을 분리 시키자
조합 가능한 행동
어떤 객체와 협력하느냐에 따라 객체의 행동이 달라지는 것은 유연하고 재사용 가능한 설계가 가진 특징이다
- 응집도 높은 책임들을 가진 작은 객체들을 다양한 방식으로 연결함으로써 애플리케이션의 기능을 쉽게 확장할 수 있다
- 객체가 어떻게(how) 하는지를 장황하게 나열하지 않고도 객체들의 조합을 통해 무엇(what)을 하는지를 표현하는 클래스들로 구성된다
'책 > Object' 카테고리의 다른 글
상속과 코드 재사용 (0) | 2020.07.27 |
---|---|
유연한 설계 (0) | 2020.07.13 |
객체 분해 (0) | 2020.07.02 |
메시지와 인터페이스 (0) | 2020.06.29 |
책임 할당하기 (0) | 2020.06.23 |