어떤 객체가 예정된 작업을 정상적으로 수행하기 위해 다른 객체를 필요로 하는 경우 두 객체 사이에 의존성이 존재한다고 말한다.
public class PeriodCondition implements DiscountCondition {
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
public boolean isSatisfiedBy(Screening screening){
return dayOfWeek.equals(screening.getWhenScreened().getDayOfWeek()) &&
startTime.isBefore(screening.getWhenScreened().toLocalTime()) &&
endTime.isAfter(screening.getWhenScreened().toLocalTime());
}
}
위 클래스는 DayOfWeek, LocalTime 그리고 Screening에 대해 의존성을 가진다. 이 클래스들이 변경될 때, PeriodCondition도 수정될 수 있다. 즉 의존성이란 변경에 의한 영향의 전파 가능성을 암시하기도 한다.
의존성은 전이될 수 있다. (transitive dependency) PeriodCondition이 Screening에 의존할 경우 PeriodCondition은 Screening이 의존하는 대상에 대해서도 의존하게 된다는 것이다.
의존성은 변경될 수 있는 가능성을 의미하기 때문에 모든 경우에 의존성이 전이되는 것은 아니다. 실제로 전이될지 여부는 변경의 방향과 캡슐화의 정도에 따라 달라진다. 의존성 전이는 변경에 대한 영향이 널리 퍼질 수 있다는 경고일 뿐이다. 그리고 한 요소가 다른 요소에 직접 의존하는 직접 의존성, 직접적인 관계는 없지만 의존성 전이에 의해 영향이 전파될 수 있는 간접 의존성이 있다.
의존성에 대한 다른 주제로는 의존성 시점의 차이다.
런타임은 말 그대로 애플리케이션이 실행되는 시점을 가리킨다. 단 컴파일 타임은 조금 애매하다. 일반적으로는 작성된 코드를 컴파일하는 시점을 가리키지만 문맥에 따라서는 코드 자체를 가리키기도 한다. 컴파일 타임 의존성이 이런 경우다. 바로 코드의 구조 자체를 의미한다.
중요한 것은 이 두 의존성이 서로 다를 수 있다는ㄴ 것이다. 유연하고 재사용 가능한 코드를 위해서는 두 종류의 의존성을 다르게 만들어야 한다.
public class Movie {
...
private DiscountPolicy discountPolicy;
public Movie(..., DiscountPolicy dp){
...
this.discountPolicy = dp;
}
}
위 코드를 보면 Movie 클래스는 AmountDiscountPolicy, PercentDiscountPolicy 그 무엇과도 의존하지 않는다. 하지만 런타임 의존성을 살펴보면 완전히 달라진다. 할인을 위해 AmountDiscountPolicy, PercentDiscountPolicy 둘 중 하나와 협력해야 한다. 코드를 작성하는 시점의 Movie는 아무것도 모르지만 실행 시점의 Movie 인스턴스는 둘 중 하나 정도는 알고 있어야 한다.
이렇게 컴파일 시점에 DiscountPolicy에 의존하고 런타임 시점에 의존성을 갈아치움으로 여러 종류의 인스턴스와 협력할 수 있게된다.
방금 알아봤듯이 클래스는 자신과 협력할 객체의 구체적인 클래스에 대해 알아서는 안된다. 구체적인 클래스를 알면 알수록 그 클래스가 사용되는 특정 문맥에 강하게 결합되기 때문이다.
PercentDiscount에 의존하면 비율 할인이라는 문맥에서만 사용할 수 있고 반대로 AmountDiscountPolicy에 의존하면 금액 할인이라는 문맥에서만 사용할 수 있다. 런타임 의존성으로 대체할 수도 없다.