-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[로또] 박주희 미션 제출합니다. #1366
base: main
Are you sure you want to change the base?
[로또] 박주희 미션 제출합니다. #1366
Changes from all commits
0ab2ae2
0c1c09b
7da7a15
de1b1b3
0ea8b8b
e790776
dd5b337
699b792
eef86de
6c36c1b
4caf4bb
0807de8
588f6a9
c0e3e9d
1c915ba
bf420a5
79b6398
ae3cba1
e7a5571
8a94c7e
a64206f
58d017d
547a271
94c3291
855ed20
49aa69d
5378929
c6a097e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,8 @@ build/ | |
!**/src/main/** | ||
!**/src/test/** | ||
|
||
*.class | ||
|
||
### STS ### | ||
.apt_generated | ||
.classpath | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,96 @@ | ||
# java-lotto-precourse | ||
# 🎟️ java-lotto-precourse | ||
|
||
## 📋 기능 요구 사항 | ||
|
||
> 간단한 로또 발매기를 구현한다. | ||
|
||
- 로또 번호의 숫자 범위는 1~45까지이다. | ||
- 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원 | ||
- 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. | ||
- 로또 1장의 가격은 1,000원이다. | ||
- 당첨 번호와 보너스 번호를 입력받는다. | ||
- 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다. | ||
- 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. | ||
- Exception이 아닌 IllegalArgumentException, IllegalStateException 등과 같은 명확한 유형을 처리한다. | ||
|
||
## 🛠️ 프로그래밍 요구 사항 1 | ||
- **JDK 21** 버전에서 실행 가능해야 한다. | ||
- 프로그램 실행의 시작점은 Application의 main()이다. | ||
- **build.gradle** 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다. | ||
- 프로그램 종료 시 System.exit()를 호출하지 않는다. | ||
- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. | ||
- 자바 코드 컨벤션을 지키면서 프로그래밍한다. | ||
- 기본적으로 [Java Style Guide](https://github.com/woowacourse/woowacourse-docs/tree/main/styleguide/java)를 원칙으로 한다. | ||
|
||
## 📝 프로그래밍 요구 사항 2 | ||
- **indent**(인덴트, 들여쓰기) **depth**를 3이 넘지 않도록 구현한다. **2**까지만 허용한다. | ||
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. | ||
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. | ||
- **3항 연산자**를 쓰지 않는다. | ||
- 함수(또는 메서드)가 **한 가지** 일만 하도록 최대한 작게 만들어라. | ||
- `JUnit 5`와 `AssertJ`를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. | ||
|
||
## 🔍 프로그래밍 요구 사항 3 | ||
- 함수(또는 메서드)의 길이가 **15 라인**을 넘어가지 않도록 구현한다. | ||
- 함수(또는 메서드)가 **한 가지** 일만 잘 하도록 구현한다. | ||
- **else 예약어**를 쓰지 않는다. | ||
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 **switch/case**도 허용하지 않는다. | ||
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. | ||
- **Java** `Enum`을 적용하여 프로그램을 구현한다. | ||
- 구현한 기능에 대한 단위 테스트를 작성한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다. | ||
- 단위 테스트 작성이 익숙하지 않다면 `LottoTest`를 참고하여 학습한 후 테스트를 작성한다. | ||
|
||
## 🚀 구현할 기능 목록 | ||
|
||
### 1. 입력 처리 | ||
- [x] 사용자가 구입할 로또의 총 금액을 **1,000 단위**로 입력 | ||
- [x] 당첨 번호인 6개의 숫자 입력 (번호는 쉼표(,)로 구분) | ||
- [x] 보너스 번호인 1개의 숫자 입력 | ||
|
||
### 2. 로또 번호 생성 | ||
- [ ] 사용자가 구매한 금액에 따라 해당 수량만큼 로또 번호 생성 | ||
- [ ] 1부터 45까지의 숫자 중 중복되지 않는 6개의 로또 번호를 랜덤하게 생성 | ||
|
||
### 3. 당첨 번호 저장 | ||
- [ ] 입력받은 당첨 번호 및 보너스 번호 리스트 생성 | ||
|
||
### 4. 당첨 결과 및 수익률 계산 | ||
- [ ] 구매한 로또 번호와 당첨 번호를 비교하여 일치하는 개수 계산 | ||
- [ ] 각 등수별 당첨 금액으로 총 수익을 계산 | ||
- [ ] 계산된 수익과 구입한 로또의 총 금액을 기반으로 소수점 둘째 자리에서 반올림하여 수익률 계산 | ||
|
||
### 5. 출력 처리 | ||
- [ ] 사용자가 구매한 로또의 개수와 랜덤 번호 오름차순 출력 | ||
- [ ] 등수별 당첨 개수와 해당 상금 출력 | ||
- [ ] 당첨 금액과 총 수익률 출력 | ||
|
||
### 6. 예외 처리 | ||
- [ ] 구입 금액이 1,000의 배수가 아닐 시 | ||
- [ ] 구입 금액이 100,000원을 초과할 시 (구입한 로또 개수가 100개를 초과할 시) | ||
- [ ] 로또 구매 개수가 1개 이하일 시 | ||
- [ ] 당첨 번호의 구분자로 쉼표(,)를 사용하지 않을 시 | ||
- [ ] 당첨 번호가 중복되거나 보너스 번호가 당첨 번호와 중복될 시 | ||
- [ ] 당첨 번호와 보너스 번호가 1부터 45 사이의 범위를 벗어날 시 | ||
- [ ] 당첨 번호 6개, 보너스 번호 1개의 개수에서 벗어날 시 | ||
- [ ] 입력된 값의 형식이 숫자가 아닐 시 (단, 당첨 번호 입력에서 구분자는 허용함) | ||
- [ ] 입력된 값이 공백 또는 빈 문자열일 시 | ||
|
||
### 7. 테스트 | ||
- [ ] **기능 단위 테스트**로 프로그램의 비즈니스 로직이 올바르게 작동하는지 검증 | ||
- [ ] **예외 단위 테스트**로 프로그램이 예외 상황에 적절한 에러 메시지를 출력하는지 검증 | ||
|
||
## 🎯 3주차 과제의 목표 | ||
2주차의 코드 리뷰를 상기하며 피드백에 따라 코드를 작성하기 | ||
|
||
1. if문 내에서 중괄호를 생략하면 안 됨 (`Google Java Style Guide` 어긋남) | ||
2. 메서드 네이밍 중 `is`가 접두어로 쓰이면 `boolean` 타입을 반환할 것 같으니 메서드명을 명확하게 표현하자 | ||
3. 하드 코딩 대신 범위나 조건 값을 상수로 처리해 일관성 가지기 (2주차 공통 피드백과 일맥상통) | ||
4. 예외에 대한 테스트뿐만 아니라 도메인 로직에 대한 테스트 코드 추가하자 | ||
5. `String` 클래스 메서드와 같은 함수를 활용해 코드의 가독성을 높이자 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,14 @@ | ||
package lotto; | ||
|
||
import lotto.controller.LottoController; | ||
import lotto.view.InputView; | ||
import lotto.view.OutputView; | ||
|
||
public class Application { | ||
public static void main(String[] args) { | ||
// TODO: 프로그램 구현 | ||
InputView inputView = new InputView(); | ||
OutputView outputView = new OutputView(); | ||
LottoController lottoController = new LottoController(inputView, outputView); | ||
lottoController.run(); | ||
} | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package lotto.controller; | ||
|
||
import lotto.service.LottoService; | ||
import lotto.view.InputView; | ||
import lotto.view.OutputView; | ||
|
||
public class LottoController { | ||
private final LottoService lottoService; | ||
|
||
public LottoController(InputView inputView, OutputView outputView) { | ||
this.lottoService = new LottoService(inputView, outputView); | ||
Comment on lines
+9
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mvc 패턴에서 모델과 뷰의 관계에 대해 생각해 보시면 좋을 것 같아요 |
||
} | ||
|
||
public void run() { | ||
lottoService.run(); | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 패키지명은 최대한 단수형을 지정하는 것도 컨벤션의 일부인 것을 아셨나요?.? 저도 방금 알았습니다... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package lotto.enums; | ||
|
||
public enum LottoErrorMessage { | ||
EMPTY_OR_BLANK_INPUT("[ERROR] 입력 값은 공백이나 빈 문자열일 수 없습니다."), | ||
NON_NUMERIC_INPUT("[ERROR] 입력 값은 숫자 형식이어야 합니다."), | ||
INVALID_PURCHASE_AMOUNT("[ERROR] 구입 금액은 1,000의 배수여야 합니다."), | ||
INVALID_DELIMITER("[ERROR] 당첨 번호는 쉼표(,)로 구분해야 합니다."), | ||
INVALID_NUMBER_COUNT("[ERROR] 당첨 번호는 6개, 보너스 번호는 1개여야 합니다."), | ||
EXCEEDS_MAX_PURCHASE_AMOUNT("[ERROR] 구입 금액은 100,000원을 초과할 수 없습니다."), | ||
BELOW_MINIMUM_PURCHASE_AMOUNT("[ERROR] 로또 구매 개수는 1개 이상이어야 합니다."), | ||
DUPLICATE_WINNING_NUMBERS("[ERROR] 당첨 번호와 보너스 번호는 중복될 수 없습니다."), | ||
NUMBER_OUT_OF_RANGE("[ERROR] 당첨 번호와 보너스 번호는 1부터 45 사이의 숫자여야 합니다."); | ||
|
||
private final String message; | ||
|
||
LottoErrorMessage(String message) { | ||
this.message = message; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이넘 클래스 내에서도 메소드를 구현할 수 있다는 걸 얼마 전에 알게 되었는데 주희 님께서는 이부분을 잘 활용하신 것 같아서 인상 깊습니다 😋 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package lotto.enums; | ||
|
||
public enum LottoRank { | ||
NO_MATCH(0, 0, false), | ||
THREE_MATCH(3, 5000, false), | ||
FOUR_MATCH(4, 50000, false), | ||
FIVE_MATCH(5, 1500000, false), | ||
FIVE_MATCH_WITH_BONUS(5, 30000000, true), | ||
SIX_MATCH(6, 2000000000, false); | ||
Comment on lines
+4
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 숫자 구분을 위해 3자리마다 |
||
|
||
private final int matchCount; | ||
private final int reward; | ||
private final boolean matchesBonus; | ||
|
||
LottoRank(int matchCount, int reward, boolean matchesBonus) { | ||
this.matchCount = matchCount; | ||
this.reward = reward; | ||
this.matchesBonus = matchesBonus; | ||
} | ||
|
||
public int getMatchCount() { | ||
return matchCount; | ||
} | ||
|
||
public int getReward() { | ||
return reward; | ||
} | ||
|
||
public boolean matchesBonus() { | ||
return matchesBonus; | ||
} | ||
|
||
public static LottoRank matchCountAndRank(int matchCount, boolean bonusMatch) { | ||
for (LottoRank rank : values()) { | ||
if (rank.getMatchCount() == matchCount && rank.matchesBonus() == bonusMatch) { | ||
return rank; | ||
} | ||
} | ||
return NO_MATCH; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package lotto.exception; | ||
|
||
import lotto.enums.LottoErrorMessage; | ||
|
||
public class LottoException extends IllegalArgumentException { | ||
|
||
public LottoException(LottoErrorMessage lottoErrorMessage) { | ||
super(lottoErrorMessage.getMessage()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package lotto.model; | ||
|
||
import lotto.validation.WinningNumberValidation; | ||
|
||
import java.util.List; | ||
|
||
public class Lotto { | ||
private final List<Integer> numbers; | ||
|
||
public Lotto(List<Integer> numbers) { | ||
WinningNumberValidation.validate(numbers); | ||
this.numbers = numbers; | ||
} | ||
|
||
public List<Integer> getNumbers() { | ||
return numbers; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package lotto.model; | ||
|
||
import lotto.enums.LottoErrorMessage; | ||
import lotto.exception.LottoException; | ||
|
||
import java.util.List; | ||
|
||
public class LottoBonusNumber { | ||
private static final int MIN_NUMBER = 1; | ||
private static final int MAX_NUMBER = 45; | ||
|
||
private final int bonusNumber; | ||
|
||
public LottoBonusNumber(int bonusNumber, List<Integer> winningNumbers) { | ||
validateBonusNumber(bonusNumber, winningNumbers); | ||
this.bonusNumber = bonusNumber; | ||
} | ||
|
||
private void validateBonusNumber(int bonusNumber, List<Integer> winningNumbers) { | ||
if (bonusNumber < MIN_NUMBER || bonusNumber > MAX_NUMBER) { | ||
throw new LottoException(LottoErrorMessage.NUMBER_OUT_OF_RANGE); | ||
} | ||
if (winningNumbers.contains(bonusNumber)) { | ||
throw new LottoException(LottoErrorMessage.DUPLICATE_WINNING_NUMBERS); | ||
} | ||
} | ||
|
||
public int getBonusNumber() { | ||
return bonusNumber; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package lotto.model; | ||
|
||
import lotto.enums.LottoRank; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class LottoMachine { | ||
private final Lotto winningLottoNumbers; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기에 보너스 번호까지 들어가 있는 건가요?.? |
||
|
||
public LottoMachine(Lotto winningLottoNumbers) { | ||
this.winningLottoNumbers = winningLottoNumbers; | ||
} | ||
|
||
public Map<LottoRank, Integer> match(List<LottoTicket> tickets) { | ||
Map<LottoRank, Integer> result = new HashMap<>(); | ||
|
||
for (LottoTicket ticket : tickets) { | ||
LottoRank rank = getRank(ticket.getLottoNumbers()); | ||
result.put(rank, result.getOrDefault(rank, 0) + 1); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private LottoRank getRank(List<Integer> lottoNumbers) { | ||
int matchCount = (int) lottoNumbers.stream() | ||
.filter(winningLottoNumbers.getNumbers()::contains) | ||
.count(); | ||
|
||
return LottoRank.matchCountAndRank(matchCount, false); | ||
} | ||
Comment on lines
+27
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 보너스 번호를 가지고 있는지에 대한 여부를 기본값으로 다 false로 지정해 주셨는데,LottoRank를 통해 등수까지 반환해 주는 것이라면 이쪽에서 보너스 번호의 매칭 여부까지 확인해 주면 좋을 것 같아요! |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package lotto.model; | ||
|
||
public class LottoPurchase { | ||
private static final int LOTTO_PRICE = 1000; | ||
|
||
private final int purchaseAmount; | ||
|
||
public LottoPurchase(int purchaseAmount) { | ||
this.purchaseAmount = purchaseAmount; | ||
} | ||
|
||
public int lottoCount() { | ||
return purchaseAmount / LOTTO_PRICE; | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package lotto.model; | ||
|
||
import camp.nextstep.edu.missionutils.Randoms; | ||
|
||
import java.util.List; | ||
|
||
public class LottoTicket { | ||
private static final int MIN_LOTTO_NUM = 1; | ||
private static final int MAX_LOTTO_NUM = 45; | ||
private static final int MAIN_NUMBER_COUNT = 6; | ||
|
||
private final List<Integer> lottoNumbers; | ||
|
||
public LottoTicket() { | ||
this.lottoNumbers = createLottoNumbers(); | ||
} | ||
|
||
private List<Integer> createLottoNumbers() { | ||
return Randoms.pickUniqueNumbersInRange(MIN_LOTTO_NUM, MAX_LOTTO_NUM, MAIN_NUMBER_COUNT); | ||
} | ||
|
||
public List<Integer> getLottoNumbers() { | ||
return lottoNumbers; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
애플리케이션 내에서 보면 주희 님이 생성하신 인스턴스가 다시 할당되지 않기 때문에 inputView, outputVie, lottoController에 final을 명시하는 것은 어떨지 궁금해요!