Skip to content

Instantly share code, notes, and snippets.

@whosoonhwang
Last active July 15, 2019 10:02
Show Gist options
  • Save whosoonhwang/0bb4b239309df06c9bcae5792bc42068 to your computer and use it in GitHub Desktop.
Save whosoonhwang/0bb4b239309df06c9bcae5792bc42068 to your computer and use it in GitHub Desktop.
코드스피치 s83 2차 과제
package reservation;
abstract public class AmountDiscount implements DiscountPolicy.AMOUNT, DiscountCondition {
private final Money amount;
AmountDiscount(Money amount) {
this.amount = amount;
}
@Override
public final Money calculateFee(Money fee) {
return fee.minus(amount);
}
}
package reservation;
public class CountAmountDiscount extends AmountDiscount {
private final int count;
public CountAmountDiscount(final Money amount, int count) {
super(amount);
this.count = count;
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return audienceCount >= count;
}
}
package reservation;
public class CountPercentDiscount extends PercentDiscount {
private final int count;
public CountPercentDiscount(double pecent, int count) {
super(pecent);
this.count = count;
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return audienceCount >= count;
}
}
package reservation;
public class Customer {
Reservation reservation = Reservation.NONE;
private Money amount;
public Customer(Money amount) {
this.amount = amount;
}
public void reserve(final TicketSeller seller, final Theater theater, final Movie movie, final Screening screening, int count) {
reservation = seller.reserve(this, theater, movie, screening, Screen.NONE, count);
}
public void reserve(final TicketSeller seller, final Theater theater, final Movie movie, final Screening screening, final Screen screen, int count) {
reservation = seller.reserve(this, theater, movie, screening, screen, count);
}
boolean hasAmount(Money amount) {
return this.amount.greaterThen(amount);
}
void minusAmount(Money amount) {
this.amount = this.amount.minus(amount);
}
public double getAmount() {
return amount.getAmount();
}
public boolean hasReservation() {
return reservation != Reservation.NONE;
}
@Override
public String toString() {
return reservation.toString();
}
}
package reservation;
interface DiscountCondition {
boolean isSatisfiedBy(final Screening screening, int audienceCount);
}
package reservation;
interface DiscountPolicy {
Money calculateFee(Money fee);
interface AMOUNT extends DiscountPolicy {
}
interface PERCENT extends DiscountPolicy {
}
interface NONE extends DiscountPolicy {
}
}
package reservation;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Set;
/**
* 1. 본 예제에서는 Sequence를 통한 할인조건만 구현되어 있다.
* Period 및 한번에 예약하는 사람의 수가 일정수를 넘어가면 할인해주는 Count 조건에 따른 할인조건을 구현하라.
*
* - 각각 구현완료, None 정책에 대해서는 NoneDiscount를 구현 클래스화 해서 구현
* - 여러 조건을 만족할 시에는 가장 할인율이 큰 조건을 택하도록 처리
*
* 2. 현재의 예제는 영화와 상영이라는 컨텍스트로 예매를 진행한다. 상영은 본디 시간표일 뿐이므로 좌석수 등을 가질 수 없다.
* 극장이 상영관을 소유하게 하고 상영이 상영관과 협력하여 예매시의 좌석수를 관리하도록 개선하라.
*
* - 극장이 상영관 소유, 상영 추가시에 상영관 등록하게끔 변경
* 고객이 상영과, 상영관을 선택하면 해당 상영 시간에 상영관 좌석이 있는지 여부 처리
* 고객이 상영관을 선택하지 않고 상영만 선택하면 해당 상영 시간에 좌석이 있는 상영관 찾아서 예약
*/
public class Main {
private static void test(final Movie movie) {
final Theater theater = new Theater(Money.of(100.0));
final Screen imax = new Screen("imax", 20);
final Screen _4d = new Screen("4D", 100);
theater.addMovie(movie);
theater.addScreen(imax, _4d);
for (int day =7 ; day<32 ; day++) {
for (int hour=10, seq = 1 ; hour<24 ; hour+=3, seq++) {
final Screening screening = new Screening(
seq,
LocalDateTime.of(2019, 7, day, hour, 0, 0)
);
theater.addScreening(movie, screening, imax);
theater.addScreening(movie, screening, _4d);
}
}
final TicketOffice ticketOffice = new TicketOffice(Money.of(0.0));
theater.contractTicketOffice(ticketOffice, 10.0);
final TicketSeller seller = new TicketSeller();
seller.setTicketOffice(ticketOffice);
final Customer customer = new Customer(Money.of(200000.0));
for (final Map.Entry<Screening, Set<Screen>> entry : theater.getScreening(movie).entrySet()) {
final Screening screening = entry.getKey();
boolean isOk = true;
for (final Screen screen : entry.getValue()) {
double curAmount = customer.getAmount();
customer.reserve(seller, theater, movie, screening, screen,8);
if (!customer.hasReservation()) {
// 해당 상영관에 자리가 없을 수도 있으므로 상영만 던져서 비어있는 상영관 찾기
System.out.println("상영관 찾아주세요...");
customer.reserve(seller, theater, movie, screening, 8);
}
isOk = theater.enter(customer, 8);
System.out.println("입장여부 : " + isOk);
if (isOk) {
System.out.println(customer);
System.out.println("보유돈 : " + curAmount + " 남은돈 : " + customer.getAmount());
System.out.println();
} else {
System.out.println();
break;
}
}
if (!isOk) break;
}
}
public static void main(String[] args) {
System.out.println("---------------------------------------------------------------------------------");
System.out.println("AmountDiscount Test");
final Movie movieAmountPolicy = new Movie<>(
"spiderman",
Duration.ofMinutes(120L),
Money.of(5000.0),
new SequenceAmountDiscount(Money.of(1000.0), 1),
new CountAmountDiscount(Money.of(2000.0), 5),
new PeriodAmountDiscount(Money.of(3000.0)
, LocalDateTime.of(2019, 7, 10, 6, 0, 0)
, LocalDateTime.of(2019, 7, 15, 12, 0, 0))
);
test(movieAmountPolicy);
System.out.println("---------------------------------------------------------------------------------");
System.out.println();
System.out.println("---------------------------------------------------------------------------------");
System.out.println("PercentDiscount Test");
final Movie moviePercentPolicy = new Movie<>(
"AI",
Duration.ofMinutes(120L),
Money.of(5000.0),
new SequencePercentDiscount(0.25, 1),
new CountPercentDiscount(0.5, 8),
new PeriodPercentDiscount(0.1
, LocalDateTime.of(2019, 7, 14, 6, 0, 0)
, LocalDateTime.of(2019, 7, 14, 12, 0, 0))
);
test(moviePercentPolicy);
System.out.println("---------------------------------------------------------------------------------");
System.out.println();
System.out.println("---------------------------------------------------------------------------------");
System.out.println("NoneDiscount Test");
final Movie movieNonePolicy = new Movie<>(
"Titanic",
Duration.ofMinutes(120L),
Money.of(10000.0),
new NoneDiscount()
);
test(movieNonePolicy);
System.out.println("---------------------------------------------------------------------------------");
}
}
package reservation;
public class Money {
public static Money of(Double amount) {
return new Money(amount);
}
private final Double amount;
Money(Double amount) {
this.amount = amount;
}
Money minus(Money amount) {
return new Money(this.amount > amount.amount ? this.amount - amount.amount : 0.0);
}
Money multi(Double times) {
return new Money(this.amount * times);
}
Money plus(Money amount) {
return new Money(this.amount + amount.amount);
}
boolean greaterThen(Money amount) {
return this.amount >= amount.amount;
}
Double getAmount() {
return amount;
}
}
package reservation;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class Movie<T extends DiscountPolicy & DiscountCondition> {
final String title;
private final Duration runningTime;
final Money fee;
private final Set<T> discountConditions = new HashSet<>();
public Movie(String title, Duration runningTime, Money fee, T... conditions) {
this.title = title;
this.runningTime = runningTime;
this.fee = fee;
this.discountConditions.addAll(Arrays.asList(conditions));
}
Money calculateFee(final Screening screening, int audienceCount) {
Money applyFee = fee;
for (T condition : discountConditions) {
// 만족하는 조건 중에 가장 할인율이 큰 조건으로 선택
if (condition.isSatisfiedBy(screening, audienceCount)) {
Money price = condition.calculateFee(fee);
if (applyFee.greaterThen(price))
applyFee = price;
}
}
return applyFee.multi((double) audienceCount);
}
}
package reservation;
public class NoneDiscount implements DiscountPolicy.NONE, DiscountCondition {
public NoneDiscount() {
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return false;
}
@Override
public final Money calculateFee(Money fee) {
return new Money(fee.getAmount());
}
}
package reservation;
abstract public class PercentDiscount implements DiscountPolicy.PERCENT, DiscountCondition {
private final double percent;
PercentDiscount(double percent) {
this.percent = percent;
}
@Override
public final Money calculateFee(Money fee) {
return fee.minus(fee.multi(percent));
}
}
package reservation;
import java.time.LocalDateTime;
public class PeriodAmountDiscount extends AmountDiscount {
private final LocalDateTime start;
private final LocalDateTime end;
public PeriodAmountDiscount(Money amount, LocalDateTime start, LocalDateTime end) {
super(amount);
this.start = start;
this.end = end;
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return (screening.whenScreened.isAfter(start)
&& screening.whenScreened.isBefore(end));
}
}
package reservation;
import java.time.LocalDateTime;
public class PeriodPercentDiscount extends PercentDiscount {
private final LocalDateTime start;
private final LocalDateTime end;
public PeriodPercentDiscount(double pecent, LocalDateTime start, LocalDateTime end) {
super(pecent);
this.start = start;
this.end = end;
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return (screening.whenScreened.isAfter(start)
&& screening.whenScreened.isBefore(end));
}
}
package reservation;
import java.time.format.DateTimeFormatter;
public class Reservation {
static final Reservation NONE = new Reservation(null, null, null, null, 0, null, false);
final Theater theater;
final Movie movie;
final Screening screening;
final Screen screen;
final int count;
private final Money price;
private final boolean chooseScreen;
Reservation(final Theater theater, final Movie movie, final Screening screening, final Screen screen, int audienceCount, Money price, boolean chooseScreen) {
this.theater = theater;
this.movie = movie;
this.screening = screening;
this.screen = screen;
this.count = audienceCount;
this.price = price;
this.chooseScreen = chooseScreen;
}
@Override
public String toString() {
if (this == Reservation.NONE) {
return "";
}
final StringBuffer sb = new StringBuffer();
sb.append("영화타이틀: ");
sb.append(movie.title);
sb.append(", ");
sb.append("상영관: ");
sb.append(screen.title);
sb.append(", ");
sb.append("상영관 지정: ");
if (chooseScreen) sb.append("(X)");
else sb.append("(O)");
sb.append(", ");
sb.append("인원: ");
sb.append(count);
sb.append(", ");
sb.append("남은좌석: ");
sb.append(screen.getAvailableSeat());
sb.append(", ");
sb.append("상영시간: ");
sb.append(screening.whenScreened.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분")));
sb.append(", ");
sb.append("상영순서: ");
sb.append(screening.sequence);
sb.append(", ");
sb.append("금액: ");
sb.append(movie.fee.multi((double) count).getAmount());
sb.append(", ");
sb.append("지불금액: ");
sb.append(price.getAmount());
sb.append(", ");
sb.append("할인금액: ");
sb.append(movie.fee.multi((double) count).minus(price).getAmount());
return sb.toString();
}
}
package reservation;
public class Screen {
static final Screen NONE = new Screen("NONE", 0);
String title;
private int seat;
public Screen(String title, int seat) {
this.title = title;
this.seat = seat;
}
boolean hasSeat(int count) {
return seat >= count;
}
void reserveSeat(int count) {
if (hasSeat(count)) seat -= count;
else throw new RuntimeException("no seat");
}
int getAvailableSeat() {
return seat;
}
}
package reservation;
import java.time.LocalDateTime;
public class Screening {
final int sequence;
final LocalDateTime whenScreened;
public Screening(int sequence, LocalDateTime when) {
this.sequence = sequence;
this.whenScreened = when;
}
}
package reservation;
public class SequenceAmountDiscount extends AmountDiscount {
private final int sequence;
public SequenceAmountDiscount(final Money amount, int sequence) {
super(amount);
this.sequence = sequence;
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return screening.sequence == sequence;
}
}
package reservation;
public class SequencePercentDiscount extends PercentDiscount {
private final int sequence;
public SequencePercentDiscount(double pecent, int sequence) {
super(pecent);
this.sequence = sequence;
}
@Override
public boolean isSatisfiedBy(final Screening screening, int audienceCount) {
return screening.sequence == sequence;
}
}
package reservation;
import java.util.*;
public class Theater {
private static final Set<Screen> EMPTY_SCREEN_SET = new HashSet<>();
private static final Map<Screening, Set<Screen>> EMPTY_SCREENING_MAP = new HashMap<>();
private final Set<TicketOffice> ticketOffices = new HashSet<>();
private final Map<Movie, Map<Screening, Set<Screen>>> movies = new HashMap<>();
private final Set<Screen> screens = new HashSet<>();
private Money amount;
public Theater(Money amount) {
this.amount = amount;
}
public boolean addMovie(final Movie movie) {
if (movies.containsKey(movie)) return false;
movies.put(movie, new HashMap<>());
return true;
}
public boolean addScreen(final Screen... screens) {
return this.screens.addAll(Arrays.asList(screens));
}
public boolean addScreening(final Movie movie, final Screening screening, final Screen screen) {
if (!movies.containsKey(movie)) return false;
if (!screens.contains(screen)) return false;
Map<Screening, Set<Screen>> screenMap = movies.get(movie);
if (!screenMap.containsKey(screening)) {
screenMap.put(screening, new HashSet<>());
}
return screenMap.get(screening).add(screen);
}
public boolean contractTicketOffice(final TicketOffice ticketOffice, Double rate) {
if (!ticketOffice.contract(this, rate)) return false;
return ticketOffices.add(ticketOffice);
}
public boolean cancelTicketOffice(final TicketOffice ticketOffice) {
if (!ticketOffices.contains(ticketOffice) || !ticketOffice.cancel(this)) return false;
return ticketOffices.remove(ticketOffice);
}
void plusAmount(Money amount) {
this.amount = this.amount.plus(amount);
}
public Map<Screening, Set<Screen>> getScreening(final Movie movie) {
if (!movies.containsKey(movie)) return EMPTY_SCREENING_MAP;
return movies.get(movie);
}
public Set<Screen> getScreen(final Movie movie, final Screening screening) {
if (!movies.containsKey(movie)
|| !movies.get(movie).containsKey(screening))
return EMPTY_SCREEN_SET;
return movies.get(movie).get(screening);
}
boolean isValidScreening(final Movie movie, final Screening screening) {
return movies.containsKey(movie)
&& movies.get(movie).containsKey(screening);
}
boolean isValid(final Movie movie, final Screening screening, final Screen screen) {
return movies.containsKey(movie)
&& screens.contains(screen)
&& movies.get(movie).containsKey(screening)
&& movies.get(movie).get(screening).contains(screen);
}
public boolean enter(final Customer customer, int count) {
final Reservation reservation = customer.reservation;
return reservation != Reservation.NONE &&
reservation.theater == this &&
isValid(reservation.movie, reservation.screening, reservation.screen) &&
reservation.count <= count;
}
private Screen getAvailableScreen(final Movie movie, final Screening screening, int count) {
if (!isValidScreening(movie, screening)) return Screen.NONE;
final Map<Screening, Set<Screen>> screeningSetMap = movies.get(movie);
for (final Set<Screen> screens : screeningSetMap.values()) {
for (final Screen screen : screens) {
if (screen.hasSeat(count)) {
return screen;
}
}
}
return Screen.NONE;
}
Reservation reserve(final Movie movie, final Screening screening, int count, Money price) {
if (!isValidScreening(movie, screening)) return Reservation.NONE;
final Screen availableScreen = getAvailableScreen(movie, screening, count);
if (availableScreen == Screen.NONE) {
return Reservation.NONE;
}
return new Reservation(this, movie, screening, availableScreen, count, price, true);
}
Reservation reserve(final Movie movie, final Screening screening, final Screen screen, int count, Money price) {
if (!isValid(movie, screening, screen) || !screen.hasSeat(count)) return Reservation.NONE;
screen.reserveSeat(count);
return new Reservation(this, movie, screening, screen, count, price, false);
}
}
package reservation;
import java.util.HashMap;
import java.util.Map;
public class TicketOffice {
private Money amount;
private Map<Theater, Double> commissionRate = new HashMap<>();
public TicketOffice(Money amount) {
this.amount = amount;
}
boolean contract(final Theater theater, Double rate) {
if (commissionRate.containsKey(theater)) return false;
commissionRate.put(theater, rate);
return true;
}
boolean cancel(final Theater theater) {
if (!commissionRate.containsKey(theater)) return false;
commissionRate.remove(theater);
return true;
}
Reservation reserve(final Theater theater, final Movie movie, final Screening screening, final Screen screen, int count, final Money price) {
if (!commissionRate.containsKey(theater))
return Reservation.NONE;
Reservation reservation;
if (screen == Screen.NONE) {
reservation = theater.reserve(movie, screening, count, price);
} else {
reservation = theater.reserve(movie, screening, screen, count, price);
}
if (reservation != Reservation.NONE) {
Money commission = price.multi(commissionRate.get(theater));
amount = amount.plus(commission);
theater.plusAmount(price.minus(commission));
}
return reservation;
}
}
package reservation;
public class TicketSeller {
private TicketOffice ticketOffice;
public void setTicketOffice(final TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
Reservation reserve(final Customer customer, final Theater theater, final Movie movie, final Screening screening, final Screen screen, int count) {
Reservation reservation = Reservation.NONE;
Money price = movie.calculateFee(screening, count);
if (customer.hasAmount(price)) {
reservation = ticketOffice.reserve(theater, movie, screening, screen, count, price);
if (reservation != Reservation.NONE) customer.minusAmount(price);
}
return reservation;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment