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

[java-lotto] yohan.kim(김요한) 과제 제출합니다. #2

Open
wants to merge 25 commits into
base: yohan/java-lotto
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9f52f00
docs: 기능 목록 작성
Sep 30, 2024
7576de1
test: LottoTest 작성
Sep 30, 2024
008b6c1
docs: 기능 목록 수정
Sep 30, 2024
52c519f
feat: 로또 번호에 중복된 숫자가 있는지 검증 - validateDuplication()
Sep 30, 2024
4638e9f
feat: 정답과 비교 후 등수 반환 - calculateRank()
Sep 30, 2024
1425896
docs: 기능 목록 수정
Sep 30, 2024
1a0f5bd
docs: 기능 목록 수정
Sep 30, 2024
83c1bdd
feat: add Seller skeleton
Sep 30, 2024
8355f3b
docs: 기능 목록 수정
Sep 30, 2024
57d7b7d
docs: 기능 목록 수정
Sep 30, 2024
ac4142d
test: SellerTest 작성
Sep 30, 2024
d30908f
feat: 로또 발행 - getLotto()
Sep 30, 2024
a57eeae
feat: 개수만큼 로또 발행 - getLottos()
Sep 30, 2024
c21af95
feat: 금액에 받아 구매 가능한 로또 개수 반환 - calculateLottoCount()
Sep 30, 2024
7a8900b
feat: 로또 당첨 통계 반환 - getResult()
Sep 30, 2024
f620885
feat: 수익률 계산 - calculateRate()
Sep 30, 2024
c544e51
feat: UI 구현
Oct 1, 2024
c63bddb
style: style, 출력문 수정
Oct 1, 2024
1dfbf77
docs: 기능 목록 수정
Oct 1, 2024
3a4147f
test: Seller 추가된 조건 테스트 작성
Oct 1, 2024
1765eca
refactor: 오름차순으로 발행 - getLotto()
Oct 1, 2024
e405f96
refactor: 수익률은 소수점 둘째자리에서 반올림 - calculateRate()
Oct 1, 2024
1c8507a
refactor: 3항 연산자 제거 - calculateRank()
Oct 1, 2024
33e67dc
fix : 부동소수점 E 포함된 표기문제 BigDecimal 사용해 해결 - calculateRate
Oct 1, 2024
d08ea77
feat: 예외 처리
Oct 1, 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
22 changes: 22 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## 기능 목록

- [x] 로또 Lotto
- [x] 여섯 개인지 검증 - validateCounts()
- [x] 로또 번호에 중복된 숫자가 있는지 검증 - validateDuplication()
- [x] 정답과 비교 후 등수 반환 - calculateRank()
- [x] 3항 연산자 제거
- [x] 로또 판매기 Seller
- [x] 금액에 받아 구매 가능한 로또 개수 반환 - calculateLottoCount()
- [x] 로또 발행 - getLotto()
- [x] 오름차순으로 발행
- [x] 개수만큼 로또 발행 - getLottos()
- [x] 로또 당첨 통계 반환 - getResult()
- [x] 수익률 계산 - calculateRate()
- [x] 수익률은 소수점 둘째자리에서 반올림
- [x] UI
- [x] 구입금액 입력받기 - receivePurchaseMoney()
- [x] 구매한 로또 출력 - printBoughtLottos()
- [x] 당첨 번호 입력받기 - receiveAnswers()
- [x] 보너스 번호 입력받기 - receiveBonus()
- [x] 당첨 통계 출력 - printResult()
- [x] 수익률 출력 - printRate()
26 changes: 25 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
package lotto;

import java.util.List;

public class Application {

public static final int LOTTO_PRICE = 1_000;

public static void main(String[] args) {
// TODO: 프로그램 구현

String purchaseMoneyStr = UI.receivePurchaseMoney();
Seller seller = new Seller(LOTTO_PRICE);

try {
int purchaseCount = seller.calculateLottoCount(seller.parsePurchaseMoneyStr(purchaseMoneyStr));

List<Lotto> lottos = seller.getLottos(purchaseCount);
UI.printBoughtLottos(lottos);

List<Integer> answers = UI.receiveAnswers();
int bonus = UI.receiveBonus();

List<Integer> result = seller.getResult(lottos, answers, bonus);
UI.printResult(result);
UI.printRate(seller.calculateRate(result, purchaseCount));
} catch (IllegalArgumentException e) {
UI.handlingIllegalArgumentException(e);
Comment on lines +15 to +27
Copy link

Choose a reason for hiding this comment

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

MVC 패턴에 따라 설게구조를 변경해도 좋을 듯 합니다! 이 부분을 컨트롤러로 분리해도 좋을 것 같아요!

}

}
}
56 changes: 54 additions & 2 deletions src/main/java/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package lotto;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Lotto {
private final List<Integer> numbers;
Expand All @@ -11,10 +14,59 @@ public Lotto(List<Integer> numbers) {
}

private void validate(List<Integer> numbers) {

Choose a reason for hiding this comment

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

Lotto 내부에서 예외처리를 선언하다보니 코드가 지저분해지는것 같습니다! 클래스를 따로 만들어 예외처리를 하는 방법은 별로인가요??

validateCounts(numbers);
validateDuplication(numbers);
}

private void validateCounts(List<Integer> numbers) {
if (numbers.size() != 6) {
throw new IllegalArgumentException();
throw new IllegalArgumentException("로또 번호의 개수는 6개여야 합니다.");
}
}

private void validateDuplication(List<Integer> numbers) {
if (new HashSet<>(numbers).size() != numbers.size()) {
throw new IllegalArgumentException("로또 번호는 중복되어선 안됩니다.");
}
}

public int calculateRank(List<Integer> answers, int bonus) {

Set<Integer> set = new HashSet<>(numbers);

int correct = getCorrect(answers, set);
int bonusCorrect = getBonusCorrect(bonus, set, correct);

return rankByCondition(correct, bonusCorrect);
}

private static int getCorrect(List<Integer> answers, Set<Integer> set) {
return (int) answers.stream()
.filter(set::contains)
.count();
}

private static int getBonusCorrect(int bonus, Set<Integer> set, int correct) {
if (set.contains(bonus) && correct == 5) {
return 1;
}
return 0;
}

// TODO: 추가 기능 구현
private int rankByCondition(int correct, int bonusCorrect) {
if (correct == 6) {
return 1;
}

if (correct + bonusCorrect <= 2) {
return 6;
}

return 8 - (correct + bonusCorrect);
}
Comment on lines +49 to +66
Copy link

Choose a reason for hiding this comment

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

아까 피드백 드렸듯이, 매직 넘버를 상수로 치환하는 것이 좋습니다. 더 의미있는 상수로 치환해주세요!


@Override
public String toString() {
return numbers.toString();
}
}
Comment on lines +69 to 72
Copy link

Choose a reason for hiding this comment

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

객체를 더 객체답게 사용하려면 toString() 더 의미있는 내용을 리턴할 수 있도록 할 수 있을 것 같아요. 단순히 getter 와 비슷하게 데이터를 리턴하는게 아니라, 객체의 상태를 표현할 수 있는 더 의미있는 정보를 리턴해도 좋을 것 같습니다!

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

import camp.nextstep.edu.missionutils.Randoms;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Seller {

private final int lottoPrice;
private static final List<Integer> prize = List.of(2_000_000_000, 30_000_000, 1_500_000, 50_000, 5_000, 0);

public Seller(int lottoPrice) {
this.lottoPrice = lottoPrice;
}

public int parsePurchaseMoneyStr(String purchaseMoneyStr) {
try {
return Integer.parseInt(purchaseMoneyStr);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("입력은 1000으로 나눠지는 값이어야 합니다.");
}
}

public int calculateLottoCount(int money) {
return money / lottoPrice;
}

public Lotto getLotto() {
List<Integer> randoms = new ArrayList<>(Randoms.pickUniqueNumbersInRange(1, 45, 6));
Collections.sort(randoms);
return new Lotto(randoms);
}

public List<Lotto> getLottos(int count) {
return IntStream.range(0, count)
.mapToObj(i -> getLotto())
.collect(Collectors.toList());
}

public List<Integer> getResult(List<Lotto> lottos, List<Integer> answers, int bonus) {
List<Integer> result = new ArrayList<>(List.of(0, 0, 0, 0, 0, 0));

for (Lotto lotto: lottos) {
int index = lotto.calculateRank(answers, bonus) - 1;
result.set(index, result.get(index) + 1);
}
return result;
}

public BigDecimal calculateRate(List<Integer> ranks, int count) {

long prizeSum = IntStream.range(0, 6)
.mapToLong(index -> (long) ranks.get(index) * prize.get(index))
.sum();
int cost = lottoPrice * count;

double rate = prizeSum * 100 / (double) cost;
return new BigDecimal(rate).setScale(1, BigDecimal.ROUND_HALF_UP);
}
}
62 changes: 62 additions & 0 deletions src/main/java/lotto/UI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package lotto;

import camp.nextstep.edu.missionutils.Console;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class UI {

private static final Map<Integer, String> resultOutputByRank = Map.of(
1, "6개 일치 (2,000,000,000원)",
2, "5개 일치, 보너스 볼 일치 (30,000,000원)",
3, "5개 일치 (1,500,000원)",
4, "4개 일치 (50,000원)",
5, "3개 일치 (5,000원)"
);

public static String receivePurchaseMoney() {
System.out.println("구입금액을 입력해 주세요.");
return Console.readLine();
}

public static void printBoughtLottos(List<Lotto> lottos) {
System.out.println("\n" + lottos.size() + "개를 구매했습니다.");
for (Lotto lotto : lottos) {
System.out.println(lotto);
}
}

public static List<Integer> receiveAnswers() {
System.out.println("\n당첨 번호를 입력해 주세요.");
String[] split = Console.readLine().split(",");
return Arrays.stream(split)
.map(Integer::parseInt)
.collect(Collectors.toList());
}

public static int receiveBonus() {
System.out.println("\n보너스 번호를 입력해 주세요.");
return Integer.parseInt(Console.readLine());
}

public static void printResult(List<Integer> result) {
System.out.println("\n당첨 통계");
System.out.println("---");

for (int index = 4; index >= 0; index--) {
System.out.println(resultOutputByRank.get(index + 1) + " - " + result.get(index) + "개");
}
}

public static void printRate(BigDecimal rate) {
System.out.println("총 수익률은 " + rate.toPlainString() + "%입니다.");
}

public static void handlingIllegalArgumentException(IllegalArgumentException e) {
System.out.println("[ERROR] " + e.getMessage());
}
}
93 changes: 91 additions & 2 deletions src/test/java/lotto/LottoTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package lotto;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class LottoTest {
Expand All @@ -18,10 +20,97 @@ void createLottoByOverSize() {
@DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.")
@Test
void createLottoByDuplicatedNumber() {
// TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5)))
.isInstanceOf(IllegalArgumentException.class);
}

// 아래에 추가 테스트 작성 가능
@DisplayName("6개 일치하면, 1을 반환한다.")

Choose a reason for hiding this comment

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

테스트를 직접 만드신 부분 대단합니다 👍

@Test
void calculateRank_1st() {
//given
Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6));
List<Integer> answers = List.of(1, 2, 3, 4, 5, 6);
int bonus = 7;

//when
int rank = lotto.calculateRank(answers, bonus);

//then
assertThat(rank).isEqualTo(1);
}

@DisplayName("5개 일치하고, 보너스 볼 일치하면, 2을 반환한다.")
@Test
void calculateRank_2nd() {
//given
Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 7));
List<Integer> answers = List.of(1, 2, 3, 4, 5, 6);
int bonus = 7;

//when
int rank = lotto.calculateRank(answers, bonus);

//then
assertThat(rank).isEqualTo(2);
}

@DisplayName("5개 일치하면, 3을 반환한다.")
@Test
void calculateRank_3rd() {
//given
Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 8));
List<Integer> answers = List.of(1, 2, 3, 4, 5, 6);
int bonus = 7;

//when
int rank = lotto.calculateRank(answers, bonus);

//then
assertThat(rank).isEqualTo(3);
}

@DisplayName("4개 일치하면, 4을 반환한다.")
@Test
void calculateRank_4th() {
//given
Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 7, 8));
List<Integer> answers = List.of(1, 2, 3, 4, 5, 6);
int bonus = 7;

//when
int rank = lotto.calculateRank(answers, bonus);

//then
assertThat(rank).isEqualTo(4);
}

@DisplayName("3개 일치하면, 5을 반환한다.")
@Test
void calculateRank_5th() {
//given
Lotto lotto = new Lotto(List.of(1, 2, 3, 7, 8, 9));
List<Integer> answers = List.of(1, 2, 3, 4, 5, 6);
int bonus = 7;

//when
int rank = lotto.calculateRank(answers, bonus);

//then
assertThat(rank).isEqualTo(5);
}

@DisplayName("2개 이하 일치하면, 6을 반환한다.")
@Test
void calculateRank_6th() {
//given
Lotto lotto = new Lotto(List.of(1, 2, 7, 8, 9, 10));
List<Integer> answers = List.of(1, 2, 3, 4, 5, 6);
int bonus = 7;

//when
int rank = lotto.calculateRank(answers, bonus);

//then
assertThat(rank).isEqualTo(6);
}
}
Loading