Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[로또] 염규영 미션 제출합니다. #1344

Open
wants to merge 79 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
b73ca1c
docs(readme): update README.md
gyuoo Nov 4, 2024
0a4fcb2
docs(readme): add package structure
gyuoo Nov 4, 2024
771c0e9
refactor(domain): move Lotto class to domain package
gyuoo Nov 4, 2024
b1c3f61
feat(exception): add ErrorMessage enum for error handling
gyuoo Nov 4, 2024
33c2af8
feat(exception): add LottoException base class for custom exceptions
gyuoo Nov 4, 2024
edfa3fb
feat(view): add ConsoleMessage enum for standardized console messages
gyuoo Nov 4, 2024
49d5ec8
refactor(exception): update method name for consistency
gyuoo Nov 4, 2024
c28342d
feat(domain): add Prize enum for lottery prize management
gyuoo Nov 4, 2024
69a2fc0
feat(view): add InputView for handling user input
gyuoo Nov 4, 2024
28459e9
feat(constants): add LottoConstantNumbers enum for managing lotto rel…
gyuoo Nov 4, 2024
996b213
feat(exception): add error messages for lotto purchase validation
gyuoo Nov 4, 2024
d3a07a3
feat(view): add ConsoleMessage enum for user prompts and outputs
gyuoo Nov 4, 2024
06615de
feat(view): add purchase amount message output
gyuoo Nov 4, 2024
9fa4490
feat(view): add method to get purchase amount from user
gyuoo Nov 4, 2024
6849849
refactor(exception): remove unnecessary codes
gyuoo Nov 4, 2024
90a90ee
feat(constants): add ErrorConstants enum for error message management
gyuoo Nov 4, 2024
2544898
feat(exception): add custom exceptions for purchase amount
gyuoo Nov 4, 2024
9682d41
refactor(exception): update exception
gyuoo Nov 4, 2024
7bba61a
refactor(exception): remove unnecessary parameter
gyuoo Nov 4, 2024
5b829f4
feat(view): add input validation for purchase amount
gyuoo Nov 4, 2024
5a59eac
feat(util): add purchase amount validation logic
gyuoo Nov 4, 2024
baf6ff2
feat(exception): add exception for lotto number counts
gyuoo Nov 4, 2024
a61364f
feat(exception): add error messages
gyuoo Nov 4, 2024
e846966
refactor(exception): move package
gyuoo Nov 4, 2024
1df5094
refactor(exception): move package
gyuoo Nov 4, 2024
6d4c0b7
feat(exception): add exception for winning number range
gyuoo Nov 4, 2024
223d57d
refactor(exception): update import paths
gyuoo Nov 4, 2024
eb2c9ec
refactor(exception): move package
gyuoo Nov 4, 2024
18db5b5
refactor(exception): move package
gyuoo Nov 4, 2024
81ef077
feat(util): add WinningNumberSeparator to split winning numbers by comma
gyuoo Nov 4, 2024
1b1f295
docs(readme): mark checklists
gyuoo Nov 4, 2024
769bac9
docs(readme): mark checklists
gyuoo Nov 4, 2024
d28ef83
fix(exception): rename OutOfRangeWinningNumberException to OutOfRange…
gyuoo Nov 4, 2024
504ff54
feat(exception): add duplicate exception
gyuoo Nov 4, 2024
d8e0568
feat(domain): add validate logic for lotto entity
gyuoo Nov 4, 2024
1480957
feat(view): add method to print total lottos
gyuoo Nov 4, 2024
a1d7b38
feat(util): add method to sort lotto numbers in ascending order
gyuoo Nov 4, 2024
5d49132
feat(util): add method to pick random numbers
gyuoo Nov 4, 2024
98f0674
refactor(view): simplify imports in OutputView
gyuoo Nov 4, 2024
f3d23fb
fix(exception): change exceptions to print correct values
gyuoo Nov 4, 2024
30952f9
fix(exception): correct exception message
gyuoo Nov 4, 2024
8be2114
feat(domain): add WinningNumber class
gyuoo Nov 4, 2024
9744dd6
refactor(util): seperate class for versatility
gyuoo Nov 4, 2024
1cb0d33
feat(view): update OutputView
gyuoo Nov 4, 2024
6660598
feat(view): update InputView
gyuoo Nov 4, 2024
231eba2
feat(exception): add InvalidNumberException
gyuoo Nov 4, 2024
c5a9c6f
fix(exception): change exceptions to print correct values
gyuoo Nov 4, 2024
e52f01f
fix(exception): change exceptions to print correct values
gyuoo Nov 4, 2024
09c960f
fix(exception): change exceptions to print correct values
gyuoo Nov 4, 2024
045166a
docs(readme): mark checklists
gyuoo Nov 4, 2024
a202d24
feat(factory): add LottoTicketStore class
gyuoo Nov 4, 2024
e48472a
refactor(test): formatting codes
gyuoo Nov 4, 2024
a9b46c4
feat(service): add LottoPurchaseService class
gyuoo Nov 4, 2024
84f1f2a
feat(controller): update LottoController class
gyuoo Nov 4, 2024
bdf826f
fix(factory): add sorting logic
gyuoo Nov 4, 2024
add95e4
docs(readme): mark checklists
gyuoo Nov 4, 2024
ad41f5e
feat(domain): add LottoResult class
gyuoo Nov 4, 2024
0dcda77
fix(service): change return type
gyuoo Nov 4, 2024
b8230ea
feat(controller): update lotto playing method
gyuoo Nov 4, 2024
5addac6
feat(domain): add getter for lotto entity
gyuoo Nov 4, 2024
12ba4ba
feat(service): add LottoGameService class
gyuoo Nov 4, 2024
202ed91
feat(domain): update Prize enum
gyuoo Nov 4, 2024
5e543c6
refactor(exception): add white space for error message
gyuoo Nov 4, 2024
d6fdf49
feat(util): add DuplicateValidator class
gyuoo Nov 4, 2024
85f6555
feat(util): add validation to ensure bonus number does not overlap wi…
gyuoo Nov 4, 2024
1121ef1
refactor(view): update InputView class
gyuoo Nov 4, 2024
6524cd2
refactor(service): update LottoGameService class
gyuoo Nov 4, 2024
3bf9b52
feat(domain): add getter for WinningNumber class
gyuoo Nov 4, 2024
7a45bd5
feat(controller): add logic for all lotto games
gyuoo Nov 4, 2024
623eb63
feat(util): add LottoResultCalculator class
gyuoo Nov 4, 2024
f0fe0c4
feat(service): add LottoStatisticsService class
gyuoo Nov 4, 2024
ab41dba
refactor(service): seperate method
gyuoo Nov 4, 2024
2268065
refactor(service): seperate method
gyuoo Nov 4, 2024
6af6985
feat(view): update OutputView class
gyuoo Nov 4, 2024
29ba68a
feat(application): update Application to run the lotto game
gyuoo Nov 4, 2024
a83d0e4
docs(readme): mark checklists
gyuoo Nov 4, 2024
b09fb49
docs(readme): add comments
gyuoo Nov 4, 2024
641d1aa
docs(readme): update package structure
gyuoo Nov 4, 2024
f481890
fix(view): remove unnecessary newline
gyuoo Nov 4, 2024
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
231 changes: 230 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,230 @@
# java-lotto-precourse
# 💸 로또

- - -

## 🚀 기능 요구 사항

- - -

### <간단한 로또 발매기 구현>

- 로또 번호의 숫자 범위는 1~45
- 로또 구입 금액 입력 시 구입 금액에 해당하는 만큼 로또 발행
- 로또 1장의 가격은 1,000원
- 1,000원으로 나누어 떨어지지 않는 경우 예외 처리
- 1장의 로또 발행 시 중복되지 않는 6개의 숫자 선정
- 로또 번호는 오름차순으로 정렬하여 출력
- 당첨 번호 추첨
- 중복되지 않는 6개의 숫자와 보너스 번호 1개 선정
- 당첨에는 1~5등 존재
- 당첨 기준과 금액
- 1등: 6개 번호 일치 / 2,000,000,000원
- 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원
- 3등: 5개 번호 일치 / 1,500,000원
- 4등: 4개 번호 일치 / 50,000원
- 5등: 3개 번호 일치 / 5,000원
- 당첨 번호와 보너스 번호를 입력받음
- 사용자가 구매한 로또 번호와 당첨 번호 비교
- 당첨 내역 및 수익률을 출력한 뒤 로또 게임 종료
- 수익률은 소수점 둘째 자리에서 반올림
- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException` 발생
- `[ERROR]`로 시작하는 에러 메시지 출력
- 그 부분부터 입력을 다시 받음
- `Exception`이 아닌 `IllegalArgumentException`, `IllegalStateException` 등과 같은 명확한 처리
- `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms`, `Console API` 사용
- Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickUniqueNumbersInRange()` 활용
- 사용자 입력 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용

## 📃 추가된 프로그래밍 요구 사항

- - -

- 메서드의 길이가 15라인을 넘어가지 않도록 구현
- 메서드가 한 가지 일만 잘 하도록 구현
- `else`, `switch`/`case` 금지
- `if` 조건절에서 값을 `return`하는 방식으로 구현하면 됨
- Java Enum 적용
- 구현한 기능에 대한 단위 테스트 작성
- 단, UI(`System.out`, `System.in`, `Scanner`) 로직 제외
- 단위 테스트 작성이 익숙하지 않다면 `LottoTest` 참고

## 📂 패키지 구조

- - -

```
🌐 src.main.java.lotto
├── 📦 constants
│ └── LottoConstantNumbers
├── 📦 controller
│ └── LottoController
├── 📦 domain
│ ├── Lotto
│ ├── LottoResult
│ ├── Prize
│ └── WinningNumber
├── 📦 exception
│ │
│ ├──📦 lottoticketexception
│ │ ├── DuplicateException
│ │ └── LottoNumberSizeException
│ │
│ ├──📦 numberexception
│ │ ├── InvalidNumberException
│ │ └── OutOfRangeNumberException
│ │
│ ├──📦 purchaseamountexception
│ │ ├── InvalidPurchaseAmountException
│ │ ├── MaxPurchaseExceedException
│ │ ├── NegativePurchaseAmountException
│ │ └── NotDivisibleByLottoPriceException
│ │
│ ├── ErrorConstants
│ └── ErrorMessage
├── 📦 factory
│ └── LottoTicketStore
├── 📦 service
│ ├── LottoGameService
│ ├── LottoPurchaseService
│ ├── LottoResultCalculator
│ └── LottoStatisticsService
├── 📦 util
│ ├── DuplicateValidator
│ ├── LottoNumberSorter
│ ├── NumberValidator
│ ├── PurchaseAmountValidator
│ ├── RandomNumberGenerator
│ └── WinningNumberSeparator
├── 📦 view
│ ├── ConsoleMessage
│ ├── InputView
│ └── OutputView
└── Application
```

## 📌 계획

- - -

- 로또의 흐름 파악
- 어울리는 디자인 패턴 선정
- 패키지 구조 설계
- 구현할 기능 목록 정리
- README.md 작성
- 프로젝트 초기 설정
- 구현
- 예외 처리에 대한 검토
- 테스트 코드 작성
- 리팩토링

## 💡 구현할 기능 목록

- - -

### 1. 입력

- [x] 로또 구입 금액 입력 받기
- [x] 1,000원 단위로 입력 받아야 함
- [x] 양의 정수가 아닌 경우 예외 발생
- [x] 1,000으로 나누어 떨어지지 않는 경우 예외 발생
- [x] 1회 최대 구매 가능 금액을 초과할 시 예외 발생
- [x] 로또 구매 개수 출력
- [x] 당첨 번호 입력
- [x] 쉼표로 구분
- [x] 올바르게 구분할 수 없을 시 예외 발생
- [x] 6개 입력 받음
- [x] 6개가 아닐 경우 예외 발생
- [x] 1~45 범위의 정수만 있는지 검증
- [x] 범위 외의 수 입력 시 예외 발생
- [x] 중복 없이 입력되었는지 검증
- [x] 중복되었을 시 예외 발생
- [x] 보너스 번호 입력
- [x] 1개 입력 받음
- [x] 1~45 범위의 정수인지 검증
- [x] 범위 외의 수 입력 시 예외 발생

### 2. 로또 발행

- [x] 발행할 로또 수량 계산
- [x] 로또 번호 선정
- [x] 1~45 범위의 정수 중 중복 없이 무작위 6개의 숫자
- [x] 각 로또마다 오름차순으로 번호 정렬
- [x] 로또 리스트 생성
- [x] 구입 금액에 따라 계산된 수만큼의 로또 생성
- [x] 리스트에 저장
- [x] 생성된 로또 리스트를 형식에 맞게 출력

### 3. 당첨 결과 계산

- [x] 각 로또의 번호와 당첨 번호를 비교
- [x] 일치하는 번호의 개수 계산
- [x] 당첨 등수 판단
- [x] 일치하는 번호의 개수에 따라 등수 결정
- [x] 각 등수의 당첨 횟수 카운팅
- [x] 당첨 횟수 저장

### 4. 수익률 계산

- [x] 총 당첨금 계산 (각 등수별 당첨 횟수 * 당첨금)
- [x] 수익률 계산 (총 당첨금 / 총 구입 금액) * 100
- [x] 소수점 둘째 자리에서 반올림

### 5. 결과 출력

- [x] 로또 구매 내역(생성된 로또 수와 각 로또의 번호를 오름차순 정렬) 출력
- [x] 당첨 통계(등수별 당첨 횟수와 당첨금) 출력
- [x] 계산된 수익률 출력

### 6. 단위 테스트 작성

- [x] 구현한 기능에 대한 단위 테스트 작성

### 7. 유틸리티

- [x] 숫자 검증 유틸리티
- [x] 숫자 랜덤 추출 유틸리티
- [x] 오름차순 정렬 유틸리티

### 8. 구조 개선 및 리팩토링

- [ ] 메서드 분리
- [ ] 메서드의 길이가 15라인을 넘어가지 않도록 구현
- [ ] 메서드가 한 가지 일만 잘 하도록 구현
- [ ] 책임 분리: 각 클래스가 단일 책임 원칙을 따르도록 구조 개선
- [x] else, switch/case문 금지
- [x] Enum의 적용

## 🤔 생각의 흔적

- - -

### 로또의 흐름

1. 로또 구입 금액 입력 받음
2. 당첨 번호 입력 받음
3. 보너스 번호 입력 받음
4. 발행한 로또의 수량 출력
5. 발행한 로또의 번호를 오름차순으로 정렬하여 출력
6. 당첨 내역 출력
7. 수익률 출력

- 예외 상황 시 에러 문구 출력 후 다시 입력 받음

### 고민한 부분

- 로또 구매 가능 금액을 int나 long으로 할 것인가, 현실처럼 10만 원까지 제한할 것인가
- 10만 원으로 제한
- Validator.validate가 맘에 안 드는데 어쩔까..

### 자기 반성

- 3시간 만에 하려니까 너무 촉박했다. 다음엔 시간을 조금 더 내서 하자..
41 changes: 38 additions & 3 deletions src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
package lotto;

import lotto.controller.LottoController;
import lotto.factory.LottoTicketStore;
import lotto.service.LottoGameService;
import lotto.service.LottoPurchaseService;
import lotto.service.LottoResultCalculator;
import lotto.service.LottoStatisticsService;
import lotto.util.DuplicateValidator;
import lotto.util.LottoNumberSorter;
import lotto.util.NumberValidator;
import lotto.util.PurchaseAmountValidator;
import lotto.util.RandomNumberGenerator;
import lotto.util.WinningNumberSeparator;
import lotto.view.InputView;
import lotto.view.OutputView;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
}
public static void main(String[] args) {
RandomNumberGenerator randomNumberGenerator = new RandomNumberGenerator();
LottoNumberSorter lottoNumberSorter = new LottoNumberSorter();
LottoTicketStore lottoTicketStore = new LottoTicketStore(randomNumberGenerator, lottoNumberSorter);

NumberValidator numberValidator = new NumberValidator();
DuplicateValidator duplicateValidator = new DuplicateValidator();
WinningNumberSeparator winningNumberSeparator = new WinningNumberSeparator();
PurchaseAmountValidator purchaseAmountValidator = new PurchaseAmountValidator(numberValidator);

OutputView outputView = new OutputView();
InputView inputView = new InputView(outputView, purchaseAmountValidator, winningNumberSeparator,
duplicateValidator);

LottoPurchaseService lottoPurchaseService = new LottoPurchaseService(inputView, outputView, lottoTicketStore);
LottoGameService lottoGameService = new LottoGameService(inputView);
LottoResultCalculator lottoResultCalculator = new LottoResultCalculator();
LottoStatisticsService lottoStatisticsService = new LottoStatisticsService(lottoResultCalculator, outputView);
LottoController lottoController = new LottoController(lottoGameService, lottoPurchaseService,
lottoStatisticsService);

lottoController.play();
}
Comment on lines +19 to +41
Copy link
Author

Choose a reason for hiding this comment

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

리팩토링이 필요해 보인다. 15줄을 넘기기도 했다.

Copy link

Choose a reason for hiding this comment

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

의존성을 관리하는 객체를 생성해보시길 추천드립니다.

}
20 changes: 0 additions & 20 deletions src/main/java/lotto/Lotto.java

This file was deleted.

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

public enum LottoConstantNumbers {
LOTTO_PRICE(1_000),
MAX_PURCHASE_AMOUNT(100_000),
LOTTO_NUMBERS_COUNT(6),
MIN_LOTTO_NUMBER(1),
MAX_LOTTO_NUMBER(45);
Comment on lines +4 to +8
Copy link

Choose a reason for hiding this comment

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

숫자 가독성을 위해 '_'를 붙인 점 좋네요!


private final int value;

LottoConstantNumbers(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}
31 changes: 31 additions & 0 deletions src/main/java/lotto/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package lotto.controller;

import java.util.List;

import lotto.domain.Lotto;
import lotto.domain.LottoResult;
import lotto.domain.WinningNumber;
import lotto.service.LottoGameService;
import lotto.service.LottoPurchaseService;
import lotto.service.LottoStatisticsService;

public class LottoController {
private final LottoGameService lottoGameService;
private final LottoPurchaseService lottoPurchaseService;
private final LottoStatisticsService lottoStatisticsService;

public LottoController(LottoGameService lottoGameService, LottoPurchaseService lottoPurchaseService,
LottoStatisticsService lottoStatisticsService) {
this.lottoGameService = lottoGameService;
this.lottoPurchaseService = lottoPurchaseService;
this.lottoStatisticsService = lottoStatisticsService;
}

public void play() {
int purchaseAmount = lottoPurchaseService.purchaseForLottos();
Copy link

Choose a reason for hiding this comment

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

purchaseForLottos 라는 메서드명이 아래의 buyLottos와 혼동될 수 있을 것 같아요! 반환되는 amount가 포함된 메서드명으로 네이밍해보는
것은 어떠실까요?

List<Lotto> lottos = lottoPurchaseService.buyLottos(purchaseAmount);
WinningNumber winningNumber = lottoGameService.setWinningNumber();
LottoResult lottoResult = lottoGameService.playLottoGame(lottos, winningNumber);
lottoStatisticsService.summarizeStatistics(purchaseAmount, lottos, winningNumber);
}
Comment on lines +24 to +30
Copy link
Author

Choose a reason for hiding this comment

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

개인적으로 맘에 드는 부분이다... service단을 잘 나누었다고 생각한다 😅

Copy link

Choose a reason for hiding this comment

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

service단을 객체가 아닌 기능별로 나누신 이유가 있을까요?

}
Loading