조영호님의 오브젝트 읽고!
자세한 내용은 도서를 구입해서 보세요!
1. 영화 예매 시스템 만들기
- 상영클래스
public class Screening {
private Movie movie;
private int sequence;
private LocalDateTime whenScreened;
... 생성자 ...
public LocalDateTime getStartTime() {
return whenScreened;
}
public boolean isSequence(int sequence) {
return this.sequence == sequence;
}
public Money getMoviceFee() {
return movie.getFee();
}
public Reservation reserve(Customer customer, int audienceCount) {
return new Reservation(customer, this, calculateFee(audienceCount), audienceCount);
}
private Money calcuateFee(int audienceCount) {
return movie.calcuateMovieFee(this).times(audienceCount);
}
}
- Money 클래스 (금액과 관련된 다양한 계산을 구현하는 간단한 클래스)
public class Money {
public static final Money ZERO = Money.wons(0);
private final BigDecimal amount;
public static Money wons(long amount) {
return new Money(BigDecimal.valueOf(amount));
}
public static Money wons(double amount) {
return new Money(BigDecimal.valueOf(amount));
}
public(BigDecimal amount) {
this.amount = amount;
}
public Money plus(Money amount) {
return new Money(this.amount.add(amount.amount));
}
public Money minus(Money amount) {
return new Money(this.amount.subtract(amount.amount));
}
public Money times(double percent) {
return new Money(this.amount.multiply(BigDecimal.valueOf(percent)));
}
public boolean isLessThan(Money other) {
return amount.compareTo(other.amount) < 0;
}
public boolean isGreaterThanOrEqual(Money other) {
return amount.compareTo(other.amount) >= 0;
}
}
- 예약 클래스
public class Reservation {
private Customer customer;
private Screening screening;
private Money fee;
private int audienceCount;
...생성자...
}
2. 할인 요금 구하기
- 영화 클래스
public class Movie {
private String title;
private Duration runningTime;
private Money fee;
private DiscountPolicy discountPolicy;
...생성자...
public Money getFee() {
return fee;
}
public Money calculateMovieFee(Screening screening) {
return fee.minus(discountPolicy..calcualteDiscountAmount(screening));
}
}
- 할인정책 클래스
public abstract class DiscountPolicy {
private List<DiscountCondition> conditions = new ArrayList<>();
public DiscountPolicy(DiscountCondition ... conditions) {
this.conditions = Arrays.asList(conditions);
}
public Money caculateDiscountAmount(Screening screening) {
for (DisCountCondition each : conditions) {
if (each.isSatisfiedBy(screening)) {
return getDiscountAmount(screening);
}
}
return Money.ZERO;
}
absract protected Money getDiscountAmount(Screening screening);
}
DiscountPolicy는 할인 여부와 요금 계산에 필요한 전체적인 흐름은 정의하지만 실제로 요금을 계산하는 부분은 추상 메서드은 getDiscountAmount 메서드에게 위임한다. 실제로는 DiscountPolicy를 상속받은 자식 클래스에서 오버라이딩한 메서드가 실행될 것이다. 이처럼 부모 클래스에 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴을 TEMPLATE METHOD 패턴이라 한다.
- 할인조건 인터페이스
public interface DiscountCondition {
boolean isSaatisfiedBy(Screening screening);
}
- 순번 클래스
public class SequenceCondition implements DiscountCondition {
private int sequence;
...생성자...
public boolean isSatisfiedBy(Screening screening) {
return screening.isSequence(sequence);
}
}
- 기간 클래스
public class PeriodCondition implements DiscountCondition {
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
...생성자...
public boolean isSatisfiedBy(Screening screening) {
return screening.getStartTime().getDayOfWeek().equals(dayOfWeek) &&
startTime.comapareTo(screening.getStartTime().toLocalTime()) <= 0 &&
endTime.compareTo(screening.getEndTime().toLocalTime()) >= 0;
}
}
- 할인정책 클래스
public class AmountDiscountPolicy extends DiscountPolicy {
private Money discuontAmount;
public AmountDiscountPolicy(Money discountAmount, DiscountCondition ... conditions) {
super(conditions);
this.discountAmount = discountAmount;
}
@Override
protected Money getDIscountAmount(Screening screening) {
return discountAmount;
}
}
- 퍼센트정책 클래스
public class PercentDiscountPolicy extends DiscountPolicy {
private double percent;
...생성자...
@Override
protected Money getDiscountAmount(Screening screening) {
return screening.getMovieFee().times(percent);
}
}
3. 상속과 다형성
상속은 기존 클래스를 기반으로 새로운 클래스를 쉽고 빠르게 추가할 수 있는 간편한 방법을 제공한다. 상속을 이용하면 부모 클래스의 구현은 공유하면서도 행동이 다른 자식 클래스를 쉽게 추가할 수 있다.
AmountDiscountPolicy와 PercentDiscountPolicy의 경우 DiscountPolicy에서 정의한 추상 메서드인 getDiscountAmoun 메서드를 오버라이딩해서 DiscountPolicy의 행동을 수정한다는 것을 알 수 있다.
이처럼 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 차이에 의한 프로그래밍 이라고 부른다.
4. 추상화와 유연성
- 할인요금이 0원일 경우를 위한 NoneDiscountPolicy 클래스 추가
public class NoneDiscountPolicy extends DiscountPolicy {
@Override
protected Money getDiscountAmount(Screening screening) {
return Money.ZERO;
}
}
- 스타워즈 영화를 추가해보기
Movie starWars = new Movie(
"스타워즈",
Duration.ofMinutes(210),
Money.wons(10000),
new NoneDiscountPolicy()
);
- DiscountPolicy에서는 getDiscountAmount()가 호출되지 않으면 무조건 ZERO를 리턴한다. 그래서 코드를 수정해보면
- DiscountPolicy 클래스
public interface DiscountPolicy {
Money calculateDiscountAmount(Screening screening);
}
// 기존의 DiscountPolicy
public abstract class DefaultDiscountPlocy implements DiscountPolicy {
...
}
public class NoneDiscountPolicy implements DiscountPolicy {
@Override
public Money calculateDiscountAmount(Screening screening) {
return Money.ZERO;
}
}
- 출처
https://link.coupang.com/a/baoA2S
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
'개발 > Java & Kotlin' 카테고리의 다른 글
[Spring] addAttribute, addFlashAttribute 차이 (95) | 2024.03.13 |
---|---|
[Java] DTO, DAO, VO 차이 알아보기 (79) | 2023.10.10 |
[Java] 오브젝트: 코드로 이해하는 객체지향 설계 (1) (124) | 2023.09.22 |
[Java] Record 알아보기 (57) | 2023.09.20 |
[Spring] Redirect, Forward 차이 (55) | 2023.09.18 |