객체의 내부 구조에 강하게 결합되지 않게 하라
클래스가 특정한 조건을 만족하는 대상에게만 메시지를 전송하도록 프로그래밍 해야한다. 클래스 내부의 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하도록 프로그래밍 해야한다
public class ReservationAgency {
public Reservation reserve(Screening screening, Customer customer, int audienceCount) {
Money fee = screening.calculateFee(audienceCount);
return new Reservation(customer, screening, fee, audienceCount);
}
디미터의 법칙을 따르면 Shy Code를 작성
shy code 란? : 불필요한 어떤 것도 다른 객체에게 보여주지 않으며, 다른 객체의 구현에 의존하지 않는 코드
디미터 법칙은 결국 객체지향에서 말하는 캡슐화를 다른 관점에서 표현한 것. 클래스를 캡슐화하기 위해 따라야하는 구체적인 지침을 제공하기 때문에 가치있다.
캡슐화 원칙 : 객체 내부의 구현을 외부 객체가 모르게 해야한다라는 점을 강조
디미터 법칙 : 협력하는 클래스의 캡슐화를지키기 위해 접근해야 하는 요소를 제한.
screening.getMovie().getDiscountConditions();
메시지 전송자가 수신자의 내부 구조에 대해 물어보고 반환받은 요소에 대해 연쇄적으로 메시지를 전송하는 형태. 이와 같은 코드를 기차 충돌(train wreck)이라 한다.
메시지 전송자는 메시지 수신자의 내부 구조에 대해 알게 되고 캡슐화가 무너짐.
위의 코드를 아래와 같이 고칠 수 있을 것이다. 더이상 메시지 수신자의 내부 구조에 관해 묻지 않게 되며, 원하는 것이 무엇인지를 명시하고 단순히 수행하도록 요청한다.
screening.calculateFee(audienceCount);
객체 자신이 내부적으로 보유하고 있는 정보나 메시지 전송의 결과로 얻게 되는 정보만 사용해서 의사결정을 내리게 하라. -> 다른 객체의 내부를 탐색하지 않아도 된다.
메서드의 이름을 짓는 방법 : '어떻게' 가 아닌 '무엇을' 하는지 드러내는 것.
인터페이스를 활용하면 클라이언트 관점에서의 동일한 작업을 수행하는 메서드들을 하나의 타입 계층으로 묶을 수 있다.
의도를 드러내는 선택자 -> 의도를 드러내는 인터페이스.
메서드 레벨에서 인터페이스 레벨로의 확장
디미터 법칙과 묻지말고 시켜라 스타일은 퍼블릭 인터페이스를 깔끔하고 유연하게 만들 수 있는 훌륭한 설계 원칙이다. 하지만 절대적인 법칙은 아니다.
'오직 하나의 도트만 사용하라' 라는 말과 비슷하다는 이야기를 했을 것이다. 하지만 이는 하나의 도트를 강제하는 것이 아니다.
IntStream.of(1, 15, 20, 3, 9).filter(x -> x>10).distinct().count();
위의 코드가 디미터의 법칙을 위반한다고 생각 할 수 있다. 하지만 이는 디미터 법칙을 제대로 이해하지 못한 것이다.
위 코드의 of, filter, distinct 메서드는 모두 IntStream이라는 동일한 클래스의 인스턴스를 반환한다. IntStream의 인스턴스를 또다른 IntStream으로 변환.
따라서 이 경우에는 디미터 법칙을 위반하지 않는다.
문제가 되는 것은 객체의 내부 구조가 외부로 노출되는 경우에 한정된다.
하지만 위의 코드에서 IntStream의 내부 구조가 노출 된 것은 없다. 단지 IntStream에서 다른 IntStream으로 변환할 뿐, 객체를 둘러 싸고 있는 캡슐은 유지된다.
객체의 상태를 물어본 후 반환된 상태를 기반으로 결정. 그 결정에 따라 객체의 상태를 변경하는 코드는 묻지 말고 시켜라 스타일로 변경해야 한다.
다만 항상 디미터의 법칙과 묻지 말고 시켜라 스타일을 준수하는 것이 긍정적인 결과로 귀결되는 것은 아니다. 모든 상황에서 맹목적으로 위임 메서드를 추가한다면 같은 퍼블릭 인터페이스 안에 어울리지 않는 오퍼레이션들이 공존하게 된다.
묻지말고 시켜라를 하기위해 응집도를 낮추고 결합도를 높이는 코드가 나올 수도 있다. 이런 상황에서는 응집도를 높이고 결합도를 낮추는 것이 전체적인 관점에서 더 좋은 방법.
디미터의 법칙 위반 여부 -> 묻는 대상이 객체인지 자료 구조인지에 달려있다.
객체는 내부구조를 숨겨야 하므로 디미터의 법칙을 따르는 것이 좋다. 자료구조라면 내부를 노출해야 하므로 디미터의 법칙을 적용할 필요가 없다.
들어가기에 앞서 프로시저(procedure)와 함수(function)를 명확하게 구분하자
가끔식은 필요에 따라 물어야 한다는 사실을 이해했다면, 명령 쿼리 분리 원칙 (Command - Query Seperation)을 알아두자.
이 부분은 CQRS 정리 해둔 내 블로그 글이 있다. 여길 참조하자.
여기서 중요한 점은