협력 대상에 대해 지속적으로 의존 관계를 맺을 필요 없이 메서드가 실행되는 동만만 일시적으로 의존 관계가 존재해도 무방하거나,
메서드가 실행될 때마다 의존 대상이 매번 달라져야 하는 경우에 유용
유연한 설계
의존성과 결합도
의존성은 두 요소 사이의 관계 유무
(e.g. "의존성이 존재한다.")
결합도는 두 요소 사이에 존재하는 의존성의 정도를 상대적으로 표현
(e.g. "결합도가 느슨하다")
모든 의존성이 나쁜 것은 아니다. 의존성은 객체들의 협력을 가능하게 만드는 매개체라는 관점에서 바람직하지만, 의존성이 과하면 문제가 될 수 있다
지식이 결합을 낳는다
결합도를 느슨하게 만들기 위해서는 협력하는 대상에 대해 필요한 정보 외에는 최대한 감추는 것이 중요 -> 추상화
추상화에 의존하라
추상화 : 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법
대상에 대해 알아야 하는 지식의 양을 줄일 수 있기 때문에 결합도를 느슨하게 유지할 수 있다.
의존하는 대상이 더 추상적일수록 결합도는 더 낮아진다.
의존 대상 구분 (아래쪽으로 갈수록 알아야 하는 지식의 양이 적어지기 때문에 결합도가 느슨)
구체 클래스 의존성
추상(abstract) 클래스 의존성
인터페이스(interface) 의존성
명시적인 의존성 (explicit dependency)
퍼블릭 인터페이스에 의존성을 명시적으로 노출하는 것
<-> 숨겨진 의존성(hidden dependency)
new는 해롭다
new 연산자 사용을 위해 구체 클래스의 이름을 직접 기술해야 한다.
new를 사용하는 클라이언트는 추상화가 아닌 구체 클래스에 의존할 수밖에 없기 때문에 결합도가 높아진다.
new 연산자는 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다.
new를 사용하면 클라이언트가 알아야 하는 지식의 양이 늘어나기 때문에 결합도가 높아진다.
new로 인한 결합도 증가 해결 방법
인스턴스를 생성하는 로직과 생성된 인스턴스를 사용하는 로직을 분리
직접 인스턴스를 생성해서는 안 된다. 단지 해당하는 인스턴스를 전달받아(생성자, setter, 인자를 통해) 사용하기만 해야 한다.
생성하는 책임을 클라이언트로 옮기고, 생성된 인스턴스를 사용하는 책임만 남겨야 한다
사용과 생성의 책임을 분리하고 생성하는 책임을 클라이언트로 옮김
의존성을 생성자에 명시적으로 드러냄
구체 클래스가 아닌 추상 클래스에 의존
가끔은 생성해도 무방하다
표준 클래스에 대한 의존은 해롭지 않다.
변경될 확률이 거의 없는 클래스라면 의존성이 문제 되지 않는다
컨텍스트 확장하기
public class Movie {
private DiscountPolicy discountPolicy;
public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
생성자에서 받는 타입이 추상화되어 있기 때문에 원하는 대로 바꾸어 적용할 수 있다
Movie avatar = new Movie("아바타", Duration.ofMinutes(120), Money.wons(10000),
new PercentDiscountPolicy())
Movie avatar = new Movie("아바타", Duration.ofMinutes(120), Money.wons(10000),
new NoneDiscountPolicy())