Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
1. 로또 번호를 발급한다 ---> LotteNumberGenerator - o
- input: 구입금액

2. 지난주 로또 번호와 비교하여 3-6개 사이 일치하는 개수를 반환한다.

3. 총 수익률을 계산한다.



**view**
- 구입금액을 입력받는다 (1) - o
- 지난주 당첨번호를 입력받는다 (4) - o
- 보너스 번호를 입력받는다 (5) - o


- 구매 갯수를 출력한다. (2)
- 로또 랜덤을 출력한다(3)
- 3-6개까지 일치하는 개수를 출력한다 (6)
- 수익률을 출한다 (7)



질문)
1. LottoTicket은 AutoLottoNumberGenerator에 의해서만 발행된다.
하지만 생성잔느 public하게 열려있다. 그래서 사이즈 체크 검증을 추가했고 그 결과
private static final int COUNT_OF_LOTTO_NUMBER = 6;

가 중복된다.


2. this.lottoNumbers = Collections.unmodifiableList(lottoNumbers);

add시 발생하는 UnsupportedOperationException 는 그냥 던지게 남겨두면 되는건가

3. LottoTicket 일급컬렉션의 검증은 어디까지?


4. 객체의 상태와 역할을 생각하면 가격은 티켓이 가지고 있어야 할 것 같은데, 계속 Getter로 가져오자니 비횰 ㅠ,,ㅇ?
24 changes: 24 additions & 0 deletions src/main/java/com/javabom/lotto/LottoApplication.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
package com.javabom.lotto;

import com.javabom.lotto.domain.AutoLottoNumberGenerator;
import com.javabom.lotto.domain.LottoStore;
import com.javabom.lotto.domain.result.BonusNumber;
import com.javabom.lotto.domain.result.LottoResult;
import com.javabom.lotto.domain.result.WinningTicket;
import com.javabom.lotto.domain.ticket.LottoTicket;
import com.javabom.lotto.domain.ticket.LottoTickets;
import com.javabom.lotto.view.InputView;
import com.javabom.lotto.view.OutputView;
import com.javabom.lotto.vo.Money;

public class LottoApplication {

public static void main(String[] args) {
LottoStore lottoStore = new LottoStore(new AutoLottoNumberGenerator());

Money totalMoney = new Money(InputView.askTotalMoney());
LottoTickets lottoTickets = lottoStore.buy(totalMoney);

OutputView.printLottoTicketNumbers(lottoTickets);

LottoTicket winningLottoTicket = new LottoTicket(InputView.askLastWeekWinningNumbers());
BonusNumber bonusNumber = new BonusNumber(InputView.askBonusNumber());
WinningTicket winningTicket = new WinningTicket(winningLottoTicket, bonusNumber);
LottoResult lottoResult = lottoTickets.getLottoResult(winningTicket);

OutputView.printResult(lottoResult, totalMoney);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.javabom.lotto.domain;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;

import static com.javabom.lotto.domain.LottoNumberSetting.*;
import static java.util.stream.Collectors.toList;

public class AutoLottoNumberGenerator implements LottoNumberGenerator {
private final List<Integer> lottoBalls;

public AutoLottoNumberGenerator() {
this.lottoBalls = IntStream.rangeClosed(NUMBER_BEGIN_BOUND, NUMBER_END_BOUND)
.boxed()
.collect(toList());
}

@Override
public Set<Integer> generate() {
Collections.shuffle(lottoBalls);
return Collections.unmodifiableSet(new HashSet<>(pickLottoBalls()));
}

private List<Integer> pickLottoBalls() {
return lottoBalls.subList(0, COUNT_OF_NUMBER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.javabom.lotto.domain;

import java.util.Set;

public interface LottoNumberGenerator {
Set<Integer> generate();
}
10 changes: 10 additions & 0 deletions src/main/java/com/javabom/lotto/domain/LottoNumberSetting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.javabom.lotto.domain;

public class LottoNumberSetting {
public static final int NUMBER_BEGIN_BOUND = 1;
public static final int NUMBER_END_BOUND = 45;
public static final int COUNT_OF_NUMBER = 6;

private LottoNumberSetting() {
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/javabom/lotto/domain/LottoStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.javabom.lotto.domain;

import com.javabom.lotto.domain.ticket.LottoTicket;
import com.javabom.lotto.domain.ticket.LottoTickets;
import com.javabom.lotto.vo.Money;

import java.util.ArrayList;
import java.util.List;

public class LottoStore {
private static final Money LOTTO_TICKET_PRICE = new Money(1000);
private final LottoNumberGenerator lottoNumberGenerator;

public LottoStore(LottoNumberGenerator lottoNumberGenerator) {
this.lottoNumberGenerator = lottoNumberGenerator;
}

public LottoTickets buy(final Money totalMoney) {
Money currentChange = totalMoney;
List<LottoTicket> lottoTickets = new ArrayList<>();

while (isEnoughChange(currentChange)) {
lottoTickets.add(new LottoTicket(lottoNumberGenerator.generate()));
currentChange = currentChange.spend(LOTTO_TICKET_PRICE);
}

return new LottoTickets(lottoTickets);
}

private boolean isEnoughChange(Money currentChange) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Money에서 isEnougToBuy랑 같은 행위를 하는데, 한번 더 메소드로 빼신 이유가 있으신가요?! 궁금합니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 지금 없어짐 메서드일거예여!

return currentChange.isEnoughToBuy(LOTTO_TICKET_PRICE);
}
}
27 changes: 27 additions & 0 deletions src/main/java/com/javabom/lotto/domain/result/BonusNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.javabom.lotto.domain.result;

import static com.javabom.lotto.domain.LottoNumberSetting.NUMBER_BEGIN_BOUND;
import static com.javabom.lotto.domain.LottoNumberSetting.NUMBER_END_BOUND;

public class BonusNumber {
private final int number;

public BonusNumber(int number) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 찬인님 말씀대로 BonusNumber와 WinningTicket 모두 number(integer)값에 대한 유효성 검사가 들어가서 한번 더 객체로 감싸서 하면 좋다고 생각합니다! ㅎㅎ

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에꺼는답글이 안달아지내용!

matchBonus 변수 자체가 보너스 번호 유무를 넘겨주는 건데, matchCount == THIRD.hitCount && matchBonus말고 한번 더 findByBonus로 빼신 이유가 있을까요??
개인적으로 메소드 없는게 더 파악이 잘되는 느낌이라서요!

if(matchCount == THIRD.hitCount && matchBonus){
       return SECOND;
}
if(matchCount == THIRD.hitCount && !matchBonus){
       return THRID;
}

보다는 위의 방식이 더 가독성있어보인다고 생각했어요!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BonusNumber는 지금은 없는 클래스예요! LottoNumber로 대체하고있어용

if (isValidNumber(number)) {
throw new IllegalArgumentException("보너스번호는 1이상 45이하여야합니다");
}
this.number = number;
}

private boolean isValidNumber(int number) {
return number < NUMBER_BEGIN_BOUND || number > NUMBER_END_BOUND;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 개인적으로 if 문에다가 조건을 전부 쓰고,
if부터 throw까지 메소드로 빼는게 더 깔끔한거같아요!
조건이 읽히기 쉽다고 생각해서 괜찮다고 봅니다

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가끔은 if문이 조금 더 판단하기 쉽다고 생각했는데 이 경우는 딱히 그래보이지 않네용 ㅎㅅㅎ 반영했습니다!


public int getNumber() {
return this.number;
}

public boolean isSameNumber(final int number) {
return this.number == number;
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/javabom/lotto/domain/result/LottoPrize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.javabom.lotto.domain.result;

import com.javabom.lotto.vo.Money;

import java.util.Arrays;
import java.util.List;

public enum LottoPrize {
FIRST(6, 2_000_000_000),
SECOND(5, 30_000_000),
THIRD(5, 1_500_000),
FOURTH(4, 50_000),
FIFTH(3, 5_000),
MISS(0, 0);

private final int hitCount;
private final long reward;

LottoPrize(int hitCount, long reward) {
this.hitCount = hitCount;
this.reward = reward;
}

public static Money calculateTotalReword(List<LottoPrize> statistics) {
Long totalMoney = statistics.stream()
.map(LottoPrize::getReward)
.reduce(0L, Long::sum);
return new Money(totalMoney);
}

public static LottoPrize findByMathCount(int matchCount, boolean matchBonus) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Match의 오타..인가요?
아니라면 의미가 궁금합니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오타입니다 !!

if (matchCount == THIRD.hitCount) {
return findByBonus(matchBonus);
}
return Arrays.stream(LottoPrize.values())
.filter(lottoPrize -> lottoPrize.hitCount == matchCount)
.findAny()
.orElse(MISS);
}

private static LottoPrize findByBonus(boolean matchBonus) {
if (matchBonus) {
return SECOND;
}
return THIRD;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 요런방법이...
저는 2등이면 2등던져주고 2등 제외한 List를 새로 만들어서 했는데 이게더 좋아보이네요 ㅠㅠ

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matchBonus 변수 자체가 보너스 번호 유무를 넘겨주는 건데, matchCount == THIRD.hitCount && matchBonus말고 한번 더 findByBonus로 빼신 이유가 있을까요??
개인적으로 메소드 없는게 더 파악이 잘되는 느낌이라서요!


public long getReward() {
return reward;
}

public int getHitCount() {
return hitCount;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 안쓰는메소드라 없애도 될거같아요

30 changes: 30 additions & 0 deletions src/main/java/com/javabom/lotto/domain/result/LottoResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.javabom.lotto.domain.result;

import com.javabom.lotto.vo.Money;

import java.util.Collections;
import java.util.List;

public class LottoResult {
private static final int PERCENTAGE = 100;
private final List<LottoPrize> lottoStatistics;

public LottoResult(List<LottoPrize> lottoPrizes) {
this.lottoStatistics = lottoPrizes;
}

public int getNumberOfHitTickets(final LottoPrize lottoPrize) {
return Math.toIntExact(lottoStatistics.stream()
.filter(lottoStatistic -> lottoStatistic.equals(lottoPrize))
.count());
}

public double getRateOfProfit(final Money spentMoney) {
Money totalProfit = LottoPrize.calculateTotalReword(lottoStatistics);
return (double) totalProfit.getValue() / spentMoney.getValue() * PERCENTAGE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 로직 Money 쪽에서 해줄수 있지않을까?

}

public List<LottoPrize> getLottoStatistics() {
return Collections.unmodifiableList(lottoStatistics);
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/javabom/lotto/domain/result/WinningTicket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.javabom.lotto.domain.result;

import com.javabom.lotto.domain.ticket.LottoTicket;

public class WinningTicket {
private final LottoTicket lottoTicket;
private final BonusNumber bonusNumber;

public WinningTicket(LottoTicket lottoTicket, BonusNumber bonusNumber) {
if (lottoTicket.contains(bonusNumber.getNumber())) {
throw new IllegalArgumentException("보너스번호와 당첨번호는 중복될 수 없습니다");
}
this.lottoTicket = lottoTicket;
this.bonusNumber = bonusNumber;
}

public boolean contains(final int lottoNumber) {
return lottoTicket.contains(lottoNumber);
}

public boolean matchBonus(final int lottoNumber) {
return bonusNumber.isSameNumber(lottoNumber);
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/javabom/lotto/domain/ticket/LottoTicket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.javabom.lotto.domain.ticket;

import com.javabom.lotto.domain.result.LottoPrize;
import com.javabom.lotto.domain.result.WinningTicket;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.javabom.lotto.domain.LottoNumberSetting.*;

public class LottoTicket {
private final Set<Integer> lottoNumbers;

public LottoTicket(Set<Integer> lottoNumbers) {
validate(lottoNumbers);
this.lottoNumbers = lottoNumbers;
}

private static boolean isValidNumber(Integer number) {
return number >= NUMBER_BEGIN_BOUND && number <= NUMBER_END_BOUND;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LottoTicket이 들고 있는 Integer에 대해 개별적으로 검증해야 할 로직이 있다는건
Integer가 하나의 객체로 분리할 수 있는 신호인거 같아
VO로 분리해보는거 어때?


private void validate(Set<Integer> lottoNumbers) {
Set<Integer> validLottoNumbers = lottoNumbers.stream()
.filter(LottoTicket::isValidNumber)
.collect(Collectors.toSet());
if (validLottoNumbers.size() != COUNT_OF_NUMBER) {
throw new IllegalArgumentException("로또번호는 중복이 없는 6개의 1과 45사이의 숫자여야 합니다");
}
}

public LottoPrize calculateLottoPrize(WinningTicket winningTicket) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

양방향 의존성이 발생해 버렸지 뭐야..!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋㅋㅋㅋ 나 안그래도 이거 질문남겼어 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 해결하겠읍니다 ,,!

int matchCount = Math.toIntExact(lottoNumbers.stream()
.filter(winningTicket::contains)
.count());
boolean matchBonus = lottoNumbers.stream()
.anyMatch(winningTicket::matchBonus);

return LottoPrize.findByMathCount(matchCount, matchBonus);
}

public boolean contains(int number) {
return lottoNumbers.contains(number);
}

@Override
public String toString() {
List<Integer> sortedLottoNumbers = new ArrayList<>(lottoNumbers);
Collections.sort(sortedLottoNumbers);
return String.valueOf(sortedLottoNumbers);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 나는 toString의 본래 용도대로 사용되는게 아닌거같아 보여

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:( 마쟈요 동의합니다 ,,

}
}
38 changes: 38 additions & 0 deletions src/main/java/com/javabom/lotto/domain/ticket/LottoTickets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.javabom.lotto.domain.ticket;

import com.javabom.lotto.domain.result.LottoPrize;
import com.javabom.lotto.domain.result.LottoResult;
import com.javabom.lotto.domain.result.WinningTicket;

import java.util.Collections;
import java.util.List;

import static java.util.stream.Collectors.toList;

public class LottoTickets {
private final List<LottoTicket> lottoTickets;

public LottoTickets(List<LottoTicket> lottoTickets) {
this.lottoTickets = lottoTickets;
}

public LottoResult getLottoResult(WinningTicket winningTicket) {
List<LottoPrize> lottoPrizes = lottoTickets.stream()
.map(lottoTicket -> lottoTicket.calculateLottoPrize(winningTicket))
.collect(toList());
return new LottoResult(lottoPrizes);
}

public List<LottoTicket> getLottoTickets() {
return Collections.unmodifiableList(lottoTickets);
}

public int count() {
return lottoTickets.size();
}

@Override
public String toString() {
return String.valueOf(lottoTickets);
}
}
Loading