해결된 질문
작성
·
24
1
public Reservation reserve(Customer customer, int audienceCount) {
Money fee = movie.calculateFee(this).times(audienceCount);
return new Reservation(customer, this , audienceCount, fee);
}
reserve 하기위해 movie에게 calculateFee를 하기 위해 screening을 전달해주는데 이 전달해주는 명확한 근거가 있는지 궁금합니다.
만약 screening을 전달하지않고 빈 것으로 전달했다하더라도
public Money calculateFee(Screening screening) {
return fee.minus(discountPolicy.calculateDiscount(screening));
}
Movie 클래스에서 screening 대신 movie를 this로 전달했어도 동일한 기능은 가능한 거 같습니다.
질문을 하다가 깨달았는데 DiscountCondition에 screening이 필요하기 때문에 전달해주는 것을 알게되었습니다.
이렇게 전달해주는 것은 자연스럽게 일단 관계와 책임을 결정한 뒤 필요한 요소들이 뭘까 전체적으로 고민하면서 정해지는 것일까요?
상영이 예약에 대한 책임을 지고, 영화에게 가격을 계산하는 책임을 줄 때 screening을 넘겨주는 것과 condition에서 screening이 필요하다는 것까지 같이 설계하셨었는지 궁금합니다.
답변 2
2
거북이님 좋은 질문 남겨 주셔서 감사합니다.
강의에서 말씀 드렸던 것처럼 객체지향에서 모든 것은 ‘행동(또는 책임)’과 관련이 있습니다.
따라서 객체지향에서 제일 먼저 고민할 부분은 어떤 책임을 어떤 객체에게 할당할 것인지를 결정하는 거죠.
그 과정에서 할인 여부를 판단할 책임을 도메인 모델 상에서 할인 조건에 해당하는 DiscountCondition에 할당하면 어떨까 고민하고 있다고 해보겠습니다.
여기까지는 DiscountCondition이 외부에 제공할 행위, 즉 책임을 결정한 거죠.
이제 외부에 제공할 행동은 결정했으니 그 다음으로는 DiscountCondition이 할인 여부를 판단하기 위해 코드를 어떤 식으로 구현할 지를 구체적으로 고민하게 됩니다.
기간 조건에서는 ‘상영 시간’이 필요하고, 순번 조건에서는 ‘상영 회차’가 필요합니다.
여기서 ‘상영 시간’과 ‘상영 회차’는 ‘데이터’가 아니라 ‘정보(information)’라는 사실이 중요합니다.
강의에서 말씀드린 것처럼 정보는 행동과 관련이 있기 때문에, DiscountCondition을 구현하기 위해서는 상영 시간과 상영 회차를 물어보기에 가장 적절한 객체를 찾아야 합니다.
당연히 Screening이 되겠죠.
여기까지 Screening에게 상영 시간과 상영 회차를 물어본 후에 이렇게 얻은 정보를 DiscountCondition에서 사용한다는 부분까지는 결정했습니다.
이제 DiscountCondition에서 이 정보에 접근할 수 있는 방법을 결정해야겠죠.
이 정보를 제공할 책임은 Screening에게 있기 때문에 가장 간단한 방법은 Screening 인스턴스를 DiscountCondition의 isSatisfiedBy 오퍼레이션의 파라미터로 전달하는 겁니다.
결과적으로 Movie의 calculateFee 메서드에 Screening 인스턴스를 전달해서 DiscountCondition까지 전달되도록 만들어야겠죠.
이렇게 설계를 할 때는 항상 응집도와 결합도 관점에서 설계를 검토해봐야 합니다.
만약 Screening에서 시작해서 DiscountCondition까지의 전체 클래스가 함께 수정되는 경향이 있다면 강의에서 사용한 예제처럼 Screening을 DiscountCondition까지 전달해도 무방합니다.
강의에서는 강의를 최대한 단순화시키기 위해 함께 변경한다고 가정하고 동일한 패키지 안에 클래스들을 포함시키기 때문에 Screening에 대한 결합도가 높아도 무방하다고 가정하고 있습니다.
만약 이 결합도가 문제가 된다면 정보를 DiscountCondition에 전달하면서도 Screening에 직접 의존하지 않도록 만드는 방법을 찾는게 좋겠죠.
예를 들면 Screening을 직접 전달하는 대신 필요한 정보를 별도의 객체에 담아 전달해서 Screening에 대한 의존성을 낮출 수 있습니다.
아래 코드를 보시면 Screening 내부의 내용을 ScreeningCondition으로 변환한 후에 이 정보를 Movie에 전달하고 있습니다.
public class Screening {
private Long id;
private Long movieId;
private Integer sequence;
private LocalDateTime screeningTime;
public class Screening {
public Reservation reserve(Long customerId, Movie movie, int audienceCount) {
if (!movieId.equals(movie.getId()) {
throw new IllegalArgumentException();
}
Money fee = movie.calculateFee(toScreeningCondition()).times(audienceCount);
return new Reservation(customerId, this.id, audienceCount, fee);
}
public ScreeningCondition toScreeningCondition() {
return new ScreeningCondition(sequence, screeningTime);
}
...
}
위 코드는 다른 분의 질문에 삽입된 코드인데 자세한 내용이 궁금하시면 아래 글을 참고 부탁드릴게요.
결과적으로 책임을 할당한다는 것은 그 객체가 수행해야 하는 책임을 할당하면서 동시에 그 책임을 수행하기 위해 다른 객체에게 어떤 정보를 요청해야 하는 지를 결정하는 과정입니다.
이 과정에 클래스의 응집도가 높아지고 결합도가 낮아지도록 협력 방식을 조율해야 합니다.
이를 위해 필요한 코드 레벨에서의 가이드는 “오브젝트 - 설계 원칙편”에서 좀 더 상세히 다루고 있습니다.
감사합니다. 🙂
0
안녕하세요, 인프런 AI 인턴입니다.
거북이님께서 제기하신 질문은 객체지향 설계에서 중요한 부분인 객체 간의 역할 분리와 책임 할당에 관련되어 있습니다. 특히, Screening
객체를 Movie
의 calculateFee
메서드에 전달하는 이유에 대해 궁금해 하시는 것 같습니다.
우선, Screening
을 calculateFee
메서드에 전달하는 이유는 기본적으로 책임과 역할에 따라 데이터와 행동을 처리해야 하는 객체를 명확히 하기 위해서입니다.
Screening 역할: Screening
객체는 특정 영화의 특정 상영에 대한 정보(시간, 시퀀스 등)를 관리합니다. 이는 예매나 요금 계산과 같은 연산을 할 때 필요한 컨텍스트를 제공합니다.
DiscountPolicy와의 연계: 할인 정책(DiscountPolicy
)은 상영의 정보에 의존하기 때문에, calculateDiscount
메서드가 Screening
객체를 필요로 합니다. 따라서, Movie
객체가 Screening
을 알고 있어야 전체적인 데이터 흐름이 자연스럽게 이어집니다.
설계 과정에서는 객체들이 각각의 역할을 정확히 수행할 수 있도록 계획합니다. 다음은 이러한 설계 과정에서 고려할 요소들입니다:
데이터 흐름과 의존성: 각 객체가 필요로 하는 데이터와 해당 데이터의 출처를 파악합니다. DiscountPolicy
가 Screening
정보를 필요로 하기 때문에, Screening
객체를 calculateFee
로 전달하게 됩니다.
역할과 책임의 분리: 각 객체가 자신에 적합한 역할을 수행하도록 합니다. 예를 들어, Screening
은 상영과 관련된 정보를, Movie
는 요금 계산을, DiscountPolicy
는 할인을 관리합니다.
변경의 용이성: 시스템이 변화할 때 한 객체의 변화가 다른 객체에 미치는 영향을 최소화하기 위해 의존성을 조절합니다.
질문의 마지막 부분에 있는 설계 결정을 어떻게 하셨는지 궁금하다는 부분은, 제가 직접 설계한 것은 아니지만, 일반적으로 설계 시 역할과 책임을 명확히 하고 나면 필요한 데이터 이동과 메서드 호출이 자연스럽게 결정되곤 합니다.
참고로, 이러한 객체 간의 설계와 관련된 유사한 질문을 확인하고 싶으시다면, 다음 게시글을 추천드립니다: DiscountPolicy 구현 및 설계에 대해 궁금한 점이 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
상세한 답변 감사합니다!