Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0b57891
docs: 요구사항 및 기능 정리
DoohyunHwang97 Nov 7, 2023
7ae17e4
feat: 로또 한개 생성 저장
DoohyunHwang97 Nov 7, 2023
d8be1c7
refactor(NumberPickingStrategy): 추상 메소드 이름 변경
DoohyunHwang97 Nov 7, 2023
ab64b9f
feat: 1-45 사이 서로 다른 랜덤한 숫자들을 생성
DoohyunHwang97 Nov 7, 2023
1115a5f
feat: 지불금액을 1000원으로 나눈 수만큼 로또 생성
DoohyunHwang97 Nov 8, 2023
3a7dafe
feat: 금액이 1000원 단위가 아니면 `IllegalArgumentException` 예외를 던진다
DoohyunHwang97 Nov 8, 2023
0352595
refactor: 돈을 천원으로 나누는 기능을 StringUtil로 분리
DoohyunHwang97 Nov 8, 2023
941508b
feat: 로또 번호가 1-45 사이의 중복 되지 않은 6개의 숫자 아니면 예외를 던진다.
DoohyunHwang97 Nov 8, 2023
7ed43e4
style(ValidationUtil): 조건문 스타일 변경
DoohyunHwang97 Nov 8, 2023
ef8480a
feat: 로또 숫자 입력을 반점 기준으로 분리
DoohyunHwang97 Nov 8, 2023
2b6397a
feat: 우승 로또 번호와 보너스 번호 저장
DoohyunHwang97 Nov 8, 2023
c9cb258
refactor: 당첨 번호와 보너스 번호 등록 기능 별도의 클래스로 분리
DoohyunHwang97 Nov 8, 2023
5fa985c
refactor: 도메인 클래스과 나머지 클래스 패키지로 분리
DoohyunHwang97 Nov 8, 2023
d6dbb8a
feat: 로또 번호가 1-45 사이 중복되지 않은 숫자 아닐시 예외 처리
DoohyunHwang97 Nov 8, 2023
9ee3c1c
feat: 로또 하나를 우승 로또 숫자들과 비교해서 맞은 숫자 개수와 보너스 번호 맞춤 여부를 알려준다
DoohyunHwang97 Nov 8, 2023
a085424
feat: 맞은 숫자 개수와 보너스 번호 맞춤 여부를 통해 등수를 계산
DoohyunHwang97 Nov 8, 2023
87cb4a4
feat: 개별 로또의 등수를 모아 마지막 등수를 제외한 우선순위 정렬 후 반환
DoohyunHwang97 Nov 8, 2023
d8796b6
feat: 당첨 내역에 따라 총 당첨 금액을 계산한다
DoohyunHwang97 Nov 8, 2023
28e4d94
test(RanksTest): 테스트 내 오류 수정
DoohyunHwang97 Nov 8, 2023
5ce3f52
feat: 지불한 금액과 당첨 금액을 나누어 수익률을 계산한다.
DoohyunHwang97 Nov 8, 2023
a845755
style: 패키지 구조 개선(service 패키지 분리)
DoohyunHwang97 Nov 8, 2023
0b24628
feat: 예외가 발생하지 않을 때까지 입력을 받는다.
DoohyunHwang97 Nov 8, 2023
1727d58
style: 컨트롤러 패키지 추가
DoohyunHwang97 Nov 8, 2023
214c558
feat: 에러 메시지 기능 추가
DoohyunHwang97 Nov 8, 2023
a8af344
feat: 로또 생성 입출력 UI 기능 추가
DoohyunHwang97 Nov 8, 2023
883b6f8
test(StringUtilTest): 로또 출력 형식 기능 관련 유틸리티 기능 테스트 추가
DoohyunHwang97 Nov 8, 2023
4335d79
refactor: 입력 관련 기능 InputView로 통일
DoohyunHwang97 Nov 8, 2023
a2d0fc9
feat: 로또를 생성할 때 숫자들을 오름차순 정렬해서 저장
DoohyunHwang97 Nov 8, 2023
a2456ef
feat: Rank에서 출력 메시지를 얻는다(ex. 3개 일치 (5,000원))
DoohyunHwang97 Nov 8, 2023
c4c8ec4
feat: 당첨된 등수를 개수와 함께 반환
DoohyunHwang97 Nov 8, 2023
066e285
test: 원 단위로 변환 테스트 추가
DoohyunHwang97 Nov 8, 2023
aff2afb
test: 당첨 내역 입출력 기능 추가
DoohyunHwang97 Nov 8, 2023
237b10d
fix(LottoController): winningLottoRegisterService 초기화 추가
DoohyunHwang97 Nov 9, 2023
1c716db
feat: 수익률 UI 추가
DoohyunHwang97 Nov 9, 2023
0389c88
fix: 잘못된 금액 입력시 예외 메시지 무한 출력 해결
DoohyunHwang97 Nov 9, 2023
414817d
style(LottoController): 가독성 리팩토링
DoohyunHwang97 Nov 9, 2023
b18cd4e
Merge pull request #1 from DoohyunHwang97/DoohyunHwang97
DoohyunHwang97 Nov 9, 2023
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
59 changes: 59 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
### 기능 요구사항
- [X] 구입 금액에 맞는 로또를 생성한다.
- [X] 로또 한장의 가격은 1000원
- [X] 금액이 1000원 단위가 아니면 `IllegalArgumentException` 예외를 던진다
- [X] 로또 번호는 1-45 사이의 중복 되지 않은 6개의 숫자
- [X] 세부 기능
- [X] 1-45 사이 서로 다른 랜덤한 숫자들을 생성한다. (테스팅 어려움! / 상위계층에서 전략을 주입한다.)
- [X] 숫자들로 한 개의 로또를 생성한다. Input: 숫자 리스트 Output: 로또 객체
- [X] 생성할 숫자만큼 로또를 생성한다. Input: 로또 생성 숫자, 숫자 생성 전략 Output: 로또 리스트
- [X] 구입 금액이 1000원으로 나누어 떨어지면 로또 생성 숫자 아니면 `IllegalArgumentException` 예외를 던진다. Input: 금액 Output: 로또 생성 숫자 or `IllegalArgumentException`
- [X] 숫자들이 1-45 사이 숫자가 아니면 `IllegalArgumentException` 예외를 던진다
- [X] 숫자들 중 중복된 숫자가 있으면 `IllegalArgumentException` 예외를 던진다
- [X] 당첨 번호와 보너스 번호를 저장한다.
- [X] 1-45 사이의 중복 되지 않는 숫자 6개와 보너스 번호 1개
- [X] 세부 기능
- [X] 당첨 번호와 보너스 번호를 저장한다. Input: 당첨 숫자 리스트, 보너스 번호 Output: 우승 로또 객체
- [X] 숫자들이 1-45 사이 숫자가 아니면 `IllegalArgumentException` 예외를 던진다
- [X] 숫자들 중 중복된 숫자가 있으면 `IllegalArgumentException` 예외를 던진다
- [X] 발행한 로또 번호와 우승 로또를 비교하여 당첨 내역을 계산한다.
- [X] 세부 기능
- [X] 로또 하나를 우승 로또 숫자들과 비교해서 맞은 숫자 개수와 보너스 번호 맞춤 여부를 알려준다. Input: 우승 로또 숫자들 Output: 맞은 숫자 개수
- [X] 로또 하나를 우승 로또의 보너스 번호와 비교해서 보너스 번호 맞춤 여부를 알려준다. Input: 보너스 번호 Output: 보너스 번호 맞춤 여부
- [X] 맞은 숫자 개수와 보너스 번호 맞춤 여부를 통해 등수를 계산한다. Input: 맞은 숫자 개수, 보너스 번호 맞춤 여부 Output: 등수
- [X] 개별 로또의 등수를 모아서 당첨 내역을 반환한다. Input: 로또들 Output: 당첨 내역
- [X] 당첨 내역에 따라 총 당첨 금액을 계산한다. Input: 당첨 내역 Output: 당첨 금액
- [X] 지불한 금액과 당첨 금액을 통해 수익률을 계산한다.
- [X] 세부 기능
- [X] 당첨 금액을 지불 금액으로 나누어 수익률을 계산한다. Input: 당첨 금액, 지불 금액 Output: 수익률
- [X] 수익률을 소수점 둘째자리로 반올림한다. (유틸리티 기능 사용)
- [ ] 사용자가 잘못된 입력을 할 시에 예외 처리한다.
- [ ] 잘못된 입력 발생시 `IllegalArgumentException` 를 발생시키고, 에러 문구를 출력 후 그 부분부터 입력을 다시 받는다.
- [ ] Exception이 아닌 `IllegalArgumentException` 등과 같은 명확한 유형을 처리
- [ ] 빈칸 입력시 예외 처리
- [ ] 숫자 아닌 입력 예외 처리
- [ ] 로또 번호와 보너스 번호는 1-45 사이의 중복 되지 않은 6개의 숫자, 아닐시 예외 처리

### 입출력 요구사항
- [X] 로또 금액을 입력 받는다.
- [X] 당첨 번호를 입력 받는다.
- [X] 보너스 번호를 입력 받는다.
- [X] 번호는 쉼표로 구분
- [X] 발행한 로또 수량 및 번호를 출력한다.
- [X] 출력한 번호는 오름차순
- [X] 당첨 내역을 출력한다.
- [ ] 수익률을 출력한다.
- [X] 예외 문구를 출력한다.
- [X] "[ERROR]"로 시작하는 에러 메시지

### 유틸리티 기능
#### 문자열 관련
- [X] 숫자 오름차순 정렬
- [X] 소숫점 둘째 자리 수에서 반올림
- [X] 로또 번호 출력 형식 생성 [ , , , , ]
- [X] 입력을 반점 기준으로 분리
- [X] 금액을 원 단위 형식으로 변환
#### 입력값 검증 관련
- [X] 금액 입력값 검증
- [X] 로또 입력값 검증
- [X] 보너스 숫자 입력값 검증
5 changes: 4 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package lotto;

import lotto.controller.LottoController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
LottoController lottoController = new LottoController();
lottoController.run();
}
}
20 changes: 0 additions & 20 deletions src/main/java/lotto/Lotto.java

This file was deleted.

9 changes: 9 additions & 0 deletions src/main/java/lotto/controller/ExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package lotto.controller;

public class ExceptionHandler {
private static final String ERROR_MSG_HEADER = "[ERROR] ";

public static void handleException(IllegalArgumentException e) {
System.out.println(ERROR_MSG_HEADER + e.toString());
}
}
61 changes: 61 additions & 0 deletions src/main/java/lotto/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package lotto.controller;

import java.util.function.Supplier;
import lotto.domain.Ranks;
import lotto.domain.WinningLotto;
import lotto.dto.LottoInfos;
import lotto.service.LottoCreationService;
import lotto.service.WinningLottoRegisterService;
import lotto.view.CreatedLottosView;
import lotto.view.InputView;
import lotto.view.WinningStatusView;

public class LottoController {
private LottoCreationService lottoCreationService;
private WinningLottoRegisterService winningLottoRegisterService;

public LottoController() {
this.lottoCreationService = new LottoCreationService(new NumberPickingStrategyImpl());
this.winningLottoRegisterService = new WinningLottoRegisterService();
}

public void run() {
CreatedLottosView.viewCreatedLottos(createLottos());

registerWinningLottoNumbers();
registerBonusNumber();

WinningLotto winningLotto = winningLottoRegisterService.getWinningLotto();
Ranks ranks = winningLotto.calcRanksOfGivenLottos(lottoCreationService.getLottos());
WinningStatusView.viewWinningStatus(
ranks.getRankCountPairs(),
ranks.toTotalReward().calcProfitRate(lottoCreationService.getTotalMoney().getAmount()));

}

private WinningLotto registerBonusNumber() {
return (WinningLotto) repeatUntilNoInternalException(
() -> winningLottoRegisterService.registerBonusNumber(InputView.readBonusNumberInput()));
}

private WinningLotto registerWinningLottoNumbers() {
return (WinningLotto) repeatUntilNoInternalException(
() -> winningLottoRegisterService.registerNumbers(InputView.readNumbersInput()));
}

private LottoInfos createLottos() {
return (LottoInfos) repeatUntilNoInternalException(
() -> lottoCreationService.createLottos(InputView.readMoneyInput())
);
}

private Object repeatUntilNoInternalException(Supplier<Object> supplier) {
while (true) {
try {
return supplier.get();
} catch (IllegalArgumentException e) {
ExceptionHandler.handleException(e);
}
}
}
}
7 changes: 7 additions & 0 deletions src/main/java/lotto/controller/NumberPickingStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package lotto.controller;

import java.util.List;

public interface NumberPickingStrategy {
List<Integer> pickLottoNumbers();
}
12 changes: 12 additions & 0 deletions src/main/java/lotto/controller/NumberPickingStrategyImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lotto.controller;

import camp.nextstep.edu.missionutils.Randoms;
import java.util.List;
import lotto.controller.NumberPickingStrategy;

public class NumberPickingStrategyImpl implements NumberPickingStrategy {
@Override
public List<Integer> pickLottoNumbers() {
return Randoms.pickUniqueNumbersInRange(1, 45, 6);
}
}
51 changes: 51 additions & 0 deletions src/main/java/lotto/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package lotto.domain;

import java.util.List;
import java.util.Objects;
import lotto.utils.ValidationUtil;

public class Lotto {
private final List<Integer> numbers;

public Lotto(List<Integer> numbers) {
validate(numbers);
this.numbers = numbers;
}

public int calcCorrectNumbers(List<Integer> winningNumbers) {
return (int) numbers.stream()
.filter(winningNumbers::contains)
.count();
}

private void validate(List<Integer> numbers) {
ValidationUtil.validateHasSixNumbers(numbers);
ValidationUtil.validateNoDuplicatedNumbers(numbers);
}

public List<Integer> getNumbers() {
return numbers;
}


public boolean containsBonusNumber(int bonusNumber) {
return numbers.contains(bonusNumber);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Lotto lotto = (Lotto) o;
return Objects.equals(numbers, lotto.numbers);
}

@Override
public int hashCode() {
return Objects.hash(numbers);
}
}
45 changes: 45 additions & 0 deletions src/main/java/lotto/domain/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package lotto.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import lotto.controller.NumberPickingStrategy;

public class Lottos {
private List<Lotto> lottos;

public Lottos() {
this.lottos = new ArrayList<>();
}

public Lottos(List<Lotto> lottos) {
this.lottos = lottos;
}

public Lotto createLotto(NumberPickingStrategy numberPickingStrategy) {
Lotto lotto = new Lotto(makeLottoNumbers(numberPickingStrategy));
lottos.add(lotto);
return lotto;
}

private List<Integer> makeLottoNumbers(NumberPickingStrategy numberPickingStrategy) {
List<Integer> numbers = numberPickingStrategy.pickLottoNumbers();
Collections.sort(numbers);
return numbers;
}

public Ranks calRanksWithWinningNumbers(List<Integer> numbers, int bonusNumber) {
List<Rank> ranks = new ArrayList<>();
for (Lotto lotto : lottos) {
ranks.add(Rank.calcRank(
lotto.calcCorrectNumbers(numbers),
lotto.containsBonusNumber(bonusNumber))
);
}
return new Ranks(ranks);
}

public List<Lotto> getLottos() {
return lottos;
}
}
57 changes: 57 additions & 0 deletions src/main/java/lotto/domain/Rank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lotto.domain;

import java.util.Arrays;
import java.util.List;
import lotto.utils.StringUtil;

public enum Rank {
FIRST(6, 6, false, 2000000000L),
SECOND(5, 5, true, 30000000L),
THIRD(4, 5, false, 1500000L),
FOURTH(3, 4, false, 50000L),
FIFTH(2, 3, false, 5000L),
LAST(1, 2, false, 0L);

private int correctCount;
private boolean needBonus;
private Long reward;
private int priority;

Rank(int priority, int correctCount, boolean needBonus, Long reward) {
this.priority = priority;
this.correctCount = correctCount;
this.needBonus = needBonus;
this.reward = reward;
}

public static Rank calcRank(int correctCount, boolean doesLottoContainBonus) {
return Arrays.stream(Rank.values())
.filter(rank -> rank.isCorrectCountEqualTo(correctCount))
.filter(rank -> rank.doesSatisfyBonusConditions(doesLottoContainBonus))
.findAny()
.orElse(LAST);
}

public static Long calcReward(List<Rank> ranks) {
return ranks.stream()
.map(rank -> rank.reward)
.mapToLong(i -> i)
.sum();
}

public String toMessage() {
return String.format("%d개 일치 (%s원)", correctCount, StringUtil.toKoreanWon(reward));
}

private boolean doesSatisfyBonusConditions(boolean doesLottoContainBonus) {
return !needBonus || doesLottoContainBonus;
}

private boolean isCorrectCountEqualTo(int correctCount) {
return this.correctCount == correctCount;
}

public int getPriority() {
return priority;
}
}
38 changes: 38 additions & 0 deletions src/main/java/lotto/domain/RankCountPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package lotto.domain;

import java.util.Objects;

public class RankCountPair {
private final Rank RANK;
private final int COUNT;

public RankCountPair(Rank RANK, int COUNT) {
this.RANK = RANK;
this.COUNT = COUNT;
}

public Rank getRANK() {
return RANK;
}

public int getCOUNT() {
return COUNT;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RankCountPair that = (RankCountPair) o;
return COUNT == that.COUNT && RANK == that.RANK;
}

@Override
public int hashCode() {
return Objects.hash(RANK, COUNT);
}
}
Loading