이번 장의 목적은 다양한 의존성 관리 기법을 원칙의 관점에서 정리한다.
소프트웨어 개체는 확장에 열려있고 수정에 닫혀있어야 한다. (OCP)
→ 여기서 확장은 동작에 연관되었고, 수정은 코드에 연관되었다.
OCP는 런타임 의존성과 컴파일 타임 의존성에 관련된 이야기다.
OCP의 기본 원칙은 기존 코드를 건들지 않고 코드(클래스)를 추가하여 기능을 확장한다.
OCP의 핵심은 추상화에 의존하는 것이다.
추상화
ex) 에서 변하는 부분 → 할인 금액을 계산하는 방법 (생략)
ex) 에서 변하지 않는 부분 → 할인 여부를 판단하는 방법 (고정)
추상화는 의존의 방향성을 통해 설계의 확장을 가능하게 한다.
즉, 모든 요소는 추상화에 의존해야 한다.
ex) Movie는 구현체가 아닌 DiscountPolicy에 의존한다.
안정된 추상화에 의지함으로서 코드에 영향받지 않는다.
2줄 요약
🔥 주의할 점은 추상화 했다고 모든 수정에 대해 설계가 폐쇄되지 않는다는 점이다.
추상화의 목적은 변하는 것과 변하지 않는 것이 무엇인지 이해하고 변하지 않는 부분을 고정하고 변하는 부분을 생략하는 것이다.
올바른 추상화를 주의깊게 선택하라.
Movie 내부에서 구체 클래스 인스턴스를 직접 생성해서는 안 된다. (OCP 위반)
discountPolicy = new AmountDiscountPolicy(...); // 금지!
→ 객체는 어느 곳이든 생성될 수밖에 없다. <문제>는 부적절한 곳에서 객체를 생성하는 것이다.
동일 클래스에 객체 생성과 객체 사용이라는 두가지 이질적인 목적을 가진 코드가 공존하면 문제가 발생할 수 있다.
따라서 생성과 사용을 분리해서 사용해야 한다.
Client를 써도 되지만 만약에 Client도 컨텍스트에 묶이기 싫다면? 객체 생성에 대한 정보를 외부에 노출하기도 싫다면? 그 때는 어떻게 처리할까?
→ 생성 책임만 전담하는 별도의 객체를 추가하면 된다. 생성 책임에 특화된 객체가 바로 FACTORY이다.
결국 추상화하면서 생성-사용 분리의 원칙을 지키기 위해서 FACTORY를 사용한다.
시스템이 객체를 분해하는 두 가지 방법
도메인은 설계를 위한 출발점일 뿐, 순수 가공물을 추가해야 하는 상황이 올수밖에 없다.
→ 애플리케이션은 도메인 개념에 대한 표현, 순수 창조 객체(순수 가공물)이 모여 자신의 역할과 책임을 다해야 한다.
생성-사용 원칙을 적용하게 되면 Movie에 오로지 인스턴스를 사용하는 책임만 남게 된다.
외부 객체가 Movie에 인스턴스를 전달해야 함을 의미한다.
→ <문제> 숨겨진 의존성은 의존성 문제를 컴파일 타임이 아닌 런타임에 발견되게 한다.
이러한 숨겨진 의존성 문제는 캡슐화 위반에 해당한다. 캡슐화는 코드를 읽고 이해하는 행위에 관련있기 때문이다. 어떤 코드의 퍼블릭 인터페이스만으로 이해할 수 있어야 캡슐화 관점에서 훌륭한 코드라고 할 수 있다.
숨겨진 의존성보다 명시적인 의존성을 우선시 해야 한다. (반드시 의존성 주입을 쓰라는게 x, 명시적으로 사용하라는 말임)
물론 명시적인 의존성을 사용하지 못하는 경우도 존재한다. 이럴 때는 신중에 신중을 가해 service locator도 고려해보자.
public class Movie {
private AmountDiscountPolicy discountPolicy;
}
위의 코드에는 구체 클래스에 대한 의존성으로 인해 결합도가 높아지는 문제가 있다. 재사용성과 유연성이 저해된다.
→ 협력의 본질은 상위 수준의 클래스가 갖고있다. 즉, 어떤 협력에서 중요 정책이나 의사결정, 비즈니스 본질은 상위 수준이 담고있다.
의존성은 변경의 전파와 관련된다. 따라서 설계는 변경의 영향을 최소화하도록 의존성을 관리해야 한다.
→ 해결책은 추상화다.
상위 수준과 하위 수준 모두 추상화에 의존하면 하위 수준 변경으로 인해 상위 수준이 변경되는 것을 방지할 수 있다.
2줄 요약
⇒ 이를 의존성 역전 원칙. DIP라고 부른다.
역전은 의존성 방향뿐만 아니라 인터페이스 소유권에도 적용된다.
즉, 추상화가 제공하는 인터페이스의 소유권도 역전시켜야 한다.
2줄 요약
유연함은 단순성과 명확성의 희생 위에서 자라난다.
유연한 설계를 단순하고 명확하게 만드려면 커뮤니케이션이 필요하다.
아직 일어나지 않은 변경은 변경이 아니다.
중요하다.