강연 영상을 통해 객체 지향과 절차 지향의 차이를 코드를 통해 매우 직관적으로 설명해주어 이해하기 좋았다. 그리고 왜 서비스 레이어가 나오게 되었는지를 알 수 있었다. 전반적으로 말씀을 너무 잘하셔서 도움이 많이 되었다.
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
1) 데이터 베이스로부터 Movie, Showing, Rule 조회
2) Showing에 적용할 수 있는 Rule이 존재하는지 확인
3) if(Rule이 존재하면) {
Discount를 읽어 할인된 요금계산
} else {
Movie의 정가를 이용해 요금 계산
}
4) Reservation을 생성해 데이터베이스에 저장
}
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
// 1) 데이터 베이스로부터 Movie, Showing, Rule 조회
// 필요한 데이터를 모두 준비한다.
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
// ...
}
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
// 2) Showing에 적용할 수 있는 Rule이 존재하는지 확인
Rule rule = findRule(showing, rules);
// ...
}
private Rule findRule(Showing showing, List<Rule> rules) {
for (Rule rule : rules) {
if (rule.isTimeOfDayRule()) { // 시간 할인 규칙
if (showing.isDayOfWeek(rule.getDayOfWeek()) &&
showing.isDurationBetween(rule.getStartTime(), rule.getEndTime())) {
return rule;
}
} else { // 횟수 할인 규
if (rule.getSequence() == showing.getSequence()) {
return rule;
}
}
}
}
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
/*
3) if(Rule이 존재하면) {
Discount를 읽어 할인된 요금계산
} else {
Movie의 정가를 이용해 요금 계산
}
*/
Money fee = movie.getFee();
if (rule != null) { // Rule이 null이 아니면 할인
fee = calculateFee(movie);
}
// ...
}
private Money calculateFee(Movie movie) {
Discount discount = DiscountDAO.selectDiscount(movie.getId());
Money discountFee = Money.ZERO;
if (discount != null) {
if (discount.isAmountType()) {
discountFee = Money.wons(discount.getFee());
} else if (discount.isPercentType()) {
discountFee = movie.getFee().times(discount.getPercent());
}
}
return movie.getFee().minus(discountFee);
}
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
Money fee = movie.getFee();
if (rule != null) { // Rule이 null이 아니면 할인
fee = calculateFee(movie);
}
// 4) Reservation을 생성해 데이터베이스에 저장
Reservation result = makeReservation(customerId, showingId, audienceCount, fee);
reservationDAO.insert(result);
return result;
}
private Reservation makeReservation(int customerId, int showingId, int audienceCount, Money payment) {
Reservation result = new Reservation();
result.setCustomerId(customerId);
result.setShowingId(showingId);
result.setAudienceCount(audienceCount);
result.setFee(payment);
return result;
}
if-else
로직이 존재한다.public class Showing {
public Reservation reserve(Customer customer, int audienceCount) {
return new Reservation(customer, this, audienceCount);
}
public Money calculateFee() {
return movie.calculateFee(this);
}
}
public class Reservation {
Reservation(Customer customer, Showing showing, int audienceCount) {
this.customer = customer;
this.showing = showing;
this.fee = showing.calculateFee().times(audienceCount);
this.audienceCount = audienceCount;
}
}
public class Movie {
public Money calculateFee(Showing showing) {
return fee.minus(discountStrategy.calculateDiscountFee(showing));
}
}
할인 전략
public abstract class DiscountStrategy {
public Money calculateDiscountFee (Showing showing) {
for(Rule each : rules) {
if(each.isStatisfiedBy(showing)) {
return getDiscountFee(showing);
}
}
return Money.ZERO;
}
abstract protected Money getDiscountFee(Showing showing);
}
public class AmountDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee (Showing showing) {
return discountAmount;
}
}
public class NonDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee (Showing showing) {
return Money.ZERO;
}
}
public class PercentDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee (Showing showing) {
return showing.getFixedFee().times(percent);
}
}
할인 규칙
public interface Rule {
boolean isStatisfiedBy(Showing showing);
}
public class SequenceRule implements Rule {
public boolean isStatisfiedBy(Showing showing) {
return showing.isSequence(sequence);
}
}
public class TimeOfDayRule implements Rule {
public boolean isStatisfiedBy(Showing showing) {
return showing.isPlayingOn(dayOfWeek) && Interval.closed(startTime, endTime).includes(showing.getPlayingInterval());
}
}
// 도메인 모델에서 서비스 레이어 로직
@Transactional
public Reservation reserveShowing(int reserverId, int showingId, int audienceCount) {
Customer reserver = customerRepository.find(reserverId);
Showing showing = showingRepository.find(showingId);
Reservation reservation = showing.reserve(reserver, audienceCount);
reservationRepository.save(reservation);
return reservation;
}
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = ShowingDAO.selectShowing(showingId);
Movie movie = MovieDAO.selectMovie(showing.getMovieId());
List<Rule> rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
Money fee = movie.getFee();
if (rule != null) {
fee = calculateFee(movie);
}
Reservation result = makeReservation(customerId, showingId, audienceCount, fee);
reservationDAO.insert(result);
return result;
}
private Money calculateFee(Movie movie) {
List<Discount> discounts = DiscountDAO.selectDiscounts(movie.getId());
Money discountFee = Money.ZERO;
for (Discount discount : discounts) {
if (discount != null) {
if (discount.isAmountType()) {
discountFee = Money.wons(discount.getFee());
} else if (discount.isPercentType()) {
discountFee = movie.getFee().times(discount.getPercent());
}
}
}
return movie.getFee().minus(discountFee);
}
for
문이 추가되었다.public class OverlappedDiscountStrategy extends DiscountStrategy {
// ...
}
OverlappedDiscountStrategy
라는 이름의 클래스를 추가했으므로, 이름만 보고 알 수 있다.