Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ceb411e
refactor : 티켓 장 수 계산 메소드 수정, static 키워드 수정
pageprologue Jul 13, 2021
250b0a0
refactor : 로또 당첨 순위 클래스명 변경 (LottoPrize -> LottoRank), 코드 이동 및 테스트 코드 수정
pageprologue Jul 13, 2021
5079c3d
docs : 구현 목록 추가
pageprologue Jul 14, 2021
76114b5
style : 패키지 정리
pageprologue Jul 14, 2021
7889a2c
feat : 티켓 장 수 테스트 및 기능 구현, 관련 소스 수정
pageprologue Jul 14, 2021
3ce0976
feat : InputView 수동 티켓 입력, 티켓 장 수 예외 처리 추가
pageprologue Jul 14, 2021
0f9d2a0
feat : 로또 수동 생성 테스트 및 기능 추가
pageprologue Jul 14, 2021
7f7ae6c
feat : 전체 로또 티켓 생성 테스트 및 기능 추가, 관련 소스 수정
pageprologue Jul 14, 2021
4f4b103
refactor : OutputView 정리
pageprologue Jul 14, 2021
1fe76e0
feat : 로또 번호가 중복될 경우 예외 발생 테스트 및 기능 구현
pageprologue Jul 15, 2021
6983ea5
feat : 당첨 결과 테스트 및 기능 구현, 관련 소스 수정, 코드 정리
pageprologue Jul 15, 2021
2094b29
fix : 보너스 번호가 일치하는 경우 당첨 순위 버그 수정 및 테스트 코드 작성
pageprologue Jul 15, 2021
4089514
refactor : 요소가 없는 경우 Collections.emptyList() 로 변경
pageprologue Jul 15, 2021
ab32b7c
feat : 로또 티켓 유효성 검사 테스트 및 기능 구현
pageprologue Jul 15, 2021
b2792af
refactor : static 키워드 제거, 생성자 주입으로 변경
pageprologue Aug 1, 2021
2a6e9d8
refactor : 사용자 입력 에러를 Controller 가 담당하도록 수정, 클래스 변수를 생성자 주입으로 수정
pageprologue Aug 2, 2021
af61ec9
refactor : 당첨 번호 일치 개수 기능 WinningLottoRank -> WinningNumbers 로 이동
pageprologue Aug 3, 2021
75f5df1
refactor : LottoNumber 캐시 적용
pageprologue Aug 17, 2021
491f190
refactor : LottoService, LottoController, StringUtil 정리
pageprologue Aug 17, 2021
a01d40a
style : rank 패키지 생성 및 Rank 관련 클래스 이동
pageprologue Aug 17, 2021
db7c747
refactor : 테스트 클래스 패키지 이동, Rank 2등인 경우 수정
pageprologue Aug 20, 2021
7bb61e0
chore: root project name 수정
pageprologue Nov 3, 2021
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
45 changes: 33 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,63 @@

- [x] 입력
- [x] 구입 금액을 입력한다.
- [x] 수동으로 구매할 로또 수를 입력한다.
- [x] 수동으로 구매할 번호를 입력한다.
- [x] 지난 주 당첨 번호를 입력한다.
- [x] 보너스 볼을 입력한다.

- [] 출력
- [x] 구매 금액에 따른 로또 티켓 장수를 출력한다.
- [x] 출력
- [x] 구입 금액에 따라 수동 구매 장수와 자동 구매 장수를 출력한다.
- [x] 구매 금액에 따른 각 로또 티켓의 번호를 출력한다.
- [x] 당첨 번호와 일치하는 로또 티켓 개수를 출력한다.
- [x] 총 수익률을 출력한다.

***

- [x] 구입 금액
- [x] 구입 금액은 1000원 단위의 정수값이다.
- [x] ERROR : 음수일 경우 예외가 발생한다.
- [x] ERROR : 1000원 단위가 아닐 경우 예외가 발생한다.
- [x] 로또 티켓 금액으로 나눈 티켓 장수로 반환한다.

- [x] 티켓 장수
- [x] 구입 금액과 수동 로또 티켓 개수를 인자로 받아 로또 티켓 장수를 반환한다.
- [x] ERROR : 전체 티켓 장수 보다 수동 구매 장수가 많은 경우 예외가 발생한다.

- [x] 로또 티켓 머신
- [x] 구입 금액을 1000원 단위로 나누어 로또 티켓 장수를 반환한다.
- [x] 로또 티켓 장수만큼 로또 티켓을 생성한다.

- [x] 로또 티켓
- [x] 6개의 로또 번호를 가진다.
- [x] 로또 티켓 머신
- [x] 수동으로 로또 티켓을 생성한다.
- [x] 자동으로 로또 티켓을 생성한다.
- [x] 전체 로또 티켓을 생성한다.

***

- [x] 로또 번호 생성기
- [x] 1 ~ 45 사이의 난수로 로또 번호를 생성한다.
- [x] 6개의 로또 번호를 반환한다.

- [x] 로또 번호
- [x] 로또 번호는 1 ~ 45 사이의 값으로 로또 번호를 생성한다.
- [x] ERROR : 1 ~ 45가 아닐 경우 예외가 발생한다.

- [x] 로또 티켓
- [x] 6개의 로또 번호를 가진다.
- [x] ERROR : 로또 번호가 6개 보다 작거나 많은 경우 예외가 발생한다.
- [x] ERROR : 로또 번호가 중복될 경우 예외가 발생한다.

- [x] 로또 순위
- [x] 로또 티켓의 당첨 수에 따른 순위를 반환한다.

***

- [x] 당첨 번호
- [x] ERROR: 중복되는 로또 번호가 있을 경우 예외가 발생한다.
- [x] 당첨 번호와 중복되지 않는 1 ~ 45 중 하나는 보너스 볼이다.
- [x] 당첨 번호와 일치하는 로또 번호 개수를 반환한다.

- [x] 로또 상금
- [x] 로또 티켓의 당첨 순위에 따른 당첨 금액을 반환한다.

- [x] 로또 통계
- [x] 당첨 결과
- [x] 당첨 번호와 일치하는 로또 번호 개수를 반환한다.
- [x] 보너스 번호 일치하는 로또 번호 개수를 반환한다.
- [x] 당첨 번호 개수가 같은 로또 티켓 개수를 반환한다.

- [x] 당첨 통계
- [x] 당첨 순위의 수익률을 계산한다.
3 changes: 1 addition & 2 deletions src/main/java/lotto/LottoApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

public class LottoApplication {
public static void main(String[] args) {
LottoController lottoController = new LottoController();
lottoController.run();
LottoController.run();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LottoController에서 run하는 것도 instance 생성에서 static으로 변경한 이유가 있을까요?
이전 리뷰에서 제가 질문을 드린건 무조건 변경하라는 것이 아니라 어떤 차이가 있을까에 대해 고민해보시면 좋을 것 같다는 부분이었어요!
저는 둘 다 괜찮다고 생각하는데 '왜 그렇게 선택했는가'에 대한 기준은 중요하다고 생각합니다!

@pageprologue pageprologue Aug 3, 2021

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.

이 부분에 대해서 static 키워드와 정적 팩터리 메서드 부분을 다시 살펴보았습니다.



static 영역에 할당된 메모리는 모든 객체가 공유하고, 프로그램의 종료시까지 메모리가 할당된 채로 존재하게 됩니다. 모든 객체가 공유한다는 점에서 사이드 이펙트 영향이 커 질 수 있습니다. 또, 프로그램이 종료되기 전에 항상 메모리에 상주하고 있어서 자주 사용하지 않는 메서드가 누적된다면 GC에 수거되지 못하므로 메모리 낭비가 발생할 수 있습니다.







인스턴스 생성과 비교하여 정적 팩터리 메서드의 장점에 대해 정리해 보았습니다.



1. 생성자는 의미있는 명명을 하기 어렵지만, 정적 팩토리 메소드는 의미있는 명명이 가능하다.



2. 정적 팩토리 메소드는 메소드 리턴 타입 범위 내에서 다양한 리턴 타입의 정의가 가능하다.



3. 정적 팩토리 메소드는 인스턴스를 생성하기 위한 로직을 캡슐화 할 수 있다.









여기에서는 LottoController 에서 메소드를 작게 분리하고, 의미 있는 명명을 하기 위해 인스턴스를 생성하여 메서드를 호출하는 방식으로 변경하였습니다~!


}
}
43 changes: 24 additions & 19 deletions src/main/java/lotto/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
package lotto.controller;

import lotto.domain.*;
import lotto.domain.lotto.LottoTickets;
import lotto.domain.util.StringUtil;
import lotto.domain.vending.BuyingPrice;
import lotto.domain.vending.LottoTicketVendingMachine;
import lotto.domain.vending.TicketAmount;
import lotto.domain.winning.WinningLottoRank;
import lotto.domain.winning.WinningNumbers;
import lotto.domain.winning.WinningStatistics;
import lotto.view.InputView;
import lotto.view.OutputView;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static lotto.domain.LottoTicketVendingMachine.TICKET_PRICE;

public class LottoController {
public void run() {

private static final LottoTicketVendingMachine lottoTicketVendingMachine = new LottoTicketVendingMachine();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

클래스 내부에서 new로 선언하며 의존하는 것과 주입받아서 사용하는 것에 대해서 고민해보면 좋을 것 같아요.
벤딩머신은 변경점이 그렇게 많지는 않겠지만, 주입해주는 방향에 대해 고민해보면 좋을 것 같아요.

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.

넵~! LottoService 객체를 새로 만들었고, 여기에서 LottoTicketVendingMachine을 생성자로 주입받도록 변경하였습니다.


public static void run() {
String inputPrice = InputView.getBuyingPrice();
BuyingPrice buyingPrice = new BuyingPrice(inputPrice);
int ticketAmount = buyingPrice.divide(TICKET_PRICE);
OutputView.printTicketAmount(ticketAmount);

LottoTicketVendingMachine lottoTicketVendingMachine = new LottoTicketVendingMachine();
List<LottoTicket> lottoTickets = lottoTicketVendingMachine.issueTickets(buyingPrice);
String inputManualCount = InputView.getManualCount();
TicketAmount ticketAmount = new TicketAmount(buyingPrice, inputManualCount);

List<String> inputManualNumbers = InputView.getManualNumbers(ticketAmount);
LottoTickets lottoTickets = lottoTicketVendingMachine.issueTickets(ticketAmount, inputManualNumbers);

OutputView.printTicketAmount(ticketAmount);
OutputView.printLottoTickets(lottoTickets);

String inputWinningNumbers = InputView.getWinningNumber();
List<Integer> splitWinningNumbers = InputView.split(inputWinningNumbers)
.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
List<Integer> splitWinningNumbers = StringUtil.splitParseInt(inputWinningNumbers);

int bonusNumber = Integer.parseInt(InputView.getBonusNumber());
WinningNumbers winningNumbers = new WinningNumbers(splitWinningNumbers, bonusNumber);
WinningStatistics winningStatistics = new WinningStatistics(winningNumbers);

Map<LottoPrize, Integer> ranks = winningStatistics.groupByWinningNumber(lottoTickets);
OutputView.printWinningStatistics(ranks);
WinningLottoRank winningLottoRank = new WinningLottoRank(lottoTickets, winningNumbers);
OutputView.printWinningStatistics(winningLottoRank);

float profitRate = winningStatistics.profitRate(ticketAmount, ranks);
WinningStatistics winningStatistics = new WinningStatistics(winningLottoRank);
float profitRate = winningStatistics.profitRate(ticketAmount);
OutputView.printProfitRate(profitRate);
}
}
55 changes: 0 additions & 55 deletions src/main/java/lotto/domain/LottoPrize.java

This file was deleted.

29 changes: 0 additions & 29 deletions src/main/java/lotto/domain/LottoTicket.java

This file was deleted.

17 changes: 0 additions & 17 deletions src/main/java/lotto/domain/LottoTicketVendingMachine.java

This file was deleted.

62 changes: 0 additions & 62 deletions src/main/java/lotto/domain/WinningStatistics.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package lotto.domain;
package lotto.domain.lotto;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static lotto.domain.LottoNumber.MAX_LOTTO_BOUND;
import static lotto.domain.LottoNumber.MIN_LOTTO_BOUND;
import static lotto.domain.lotto.LottoNumber.MAX_LOTTO_BOUND;
import static lotto.domain.lotto.LottoNumber.MIN_LOTTO_BOUND;

public class LottoGenerator {

private static final List<LottoNumber> lottoNumberContainer = create();
private static final List<LottoNumber> LOTTO_NUMBER_CONTAINER = create();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LottoNumber에 대한 cache가 이곳에 위치해야 하는지 고민해보면 좋을 것 같아요.
지금 LottoGenerator를 통해서 Lotto를 생성하고 있는데, 코드 곳곳에 보면 new LottoNumber()로 생성하는 곳도 있고,
LottoGenerator를 통해서 caching된 객체를 받는 곳도 있어요.
이 부분에 대한 일관성(모두 new 를하여 사용할지, 어짜피 lottoNumber는 1~45만 필요하니 어디서든 caching된 것을 사용할지)을 유지하면 좋지 않을까 싶습니다!

@pageprologue pageprologue Aug 3, 2021

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.

LottoGenerator 의 역할을 LottoTicketVendingMachine 로 옮겼습니다~!
 그리고 로또 번호 인스턴스를 생성하는 부분은 로또 티켓이 자동 생성인지, 수동 생성인지에 따라 차이가 생기게 되었습니다.

  • 자동 생성에서는 LottoGenerator를 통해 전체 로또 번호를 미리 생성하여 중복되지 않은 로또 번호를 반환해 주고 있습니다.
  • 수동 생성인 경우에는 수동 번호를 입력 받기 때문에 유효성 검증과 일관된 자료형을 사용할 목적으로 LottoNumber로 생성하고 있습니다.


Copy link
Copy Markdown

Choose a reason for hiding this comment

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

수동 생성의 경우도 고민이 되는 부분이 있네요.
중복되지 않은 번호여야 한다는 것은 로또 티켓이 가져야 하는 role이지 않을까요?
수동으로 생성한다하더라도 티켓을 만들 때 유효성 검증에서 걸릴텐데
한 애플리케이션 안에서 어떤 부분은 new를 통해 새롭게 생성하고, 어떤 부분은 caching을 사용하는 구조보다는
일관성을 가져가는게 어떨까 싶네요 :)

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.

넵 이 부분 다시 수정하였습니다~!


private static List<LottoNumber> create() {
return IntStream.rangeClosed(MIN_LOTTO_BOUND, MAX_LOTTO_BOUND)
Expand All @@ -20,8 +20,8 @@ private static List<LottoNumber> create() {
}

public List<LottoNumber> issueAutoLottoNumbers() {
Collections.shuffle(lottoNumberContainer);
return lottoNumberContainer.subList(0, 6).stream()
Collections.shuffle(LOTTO_NUMBER_CONTAINER);
return LOTTO_NUMBER_CONTAINER.subList(0, 6).stream()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0과 6이 의미하는 바가 무엇일까요?

.sorted(Comparator.comparing(LottoNumber::value))
.collect(Collectors.toList());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package lotto.domain;
package lotto.domain.lotto;

import java.util.Objects;

Expand All @@ -13,6 +13,10 @@ public LottoNumber(final int lottoNumber) {
this.lottoNumber = lottoNumber;
}

public LottoNumber(String lottoNumber) {
this(Integer.parseInt(lottoNumber));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Integer.parseInt(lottoNumber)를 했는데 lottoNumber가 숫자가 아닌 문자열이면 어떤 일이 벌어질까요?
무엇이 잘못되었는지 사용자가 잘 알 수 있게 하려면 어떻게 하는 것이 좋을까요?

@pageprologue pageprologue Aug 3, 2021

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.

LOTTO_NUMBER_CONTAINER 에서 6개의 볼을 꺼내기 위해 ArrayList.subList 를 사용하였습니다. 파라미터로 받는 fromIndex와 toIndex를 따로 상수화 하지 않았었는데, 6이라는 숫자는 LOTTO_NUMBER_SIZE 상수로 대체 하였습니다. 0도 상수로 빼는 것이 좋을까요?

또한 ArrayList.subList 사용시 메모리 누수 위험성이 있다고 하여 수정하였습니다.
(
ArrayList.subList 주의할 점)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

엇 제가 남긴 코멘트는 IntegerParseInt에 대해서 valid하지 않은 String이 들어오면 어떻게 되는지에 대한 물음이었는데,
혹시 그에 대한 답변이 맞을까요??

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.

아 이 부분 바로 위에 남긴 코멘트였는데 잘못 달았네요..ㅎ
IntegerParseInt 하는 부분 처리 했습니다.


private void validateBound(int lottoNumber) {
if (lottoNumber < MIN_LOTTO_BOUND || lottoNumber > MAX_LOTTO_BOUND) {
throw new IllegalArgumentException("로또 번호는 1 ~ 45 까지 입니다.");
Expand Down
Loading