Skip to content

Commit f39e2a8

Browse files
고생하셨습니다.
🎉 PR 머지 완료! 🎉
1 parent b3ba83a commit f39e2a8

23 files changed

+1306
-0
lines changed

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 로또 - 클린코드
2+
3+
## 1단계 - 로또 자동 구매
4+
5+
### 기능 요구사항
6+
- 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다.
7+
- 사용자에게 input으로 금액을 입력받아 파싱한다.
8+
9+
10+
- 로또 1장의 가격은 1000원이다.
11+
- 로또 구매 금액을 입력 받는다. ex) 14,000원
12+
- 1,000원에 1개의 로또를 살 수 있다. ex) 14,000원의 경우 14장의 로또가 지급된다.
13+
14+
15+
- 로또 자동 생성은 **Collections.shuffle()** 메소드 활용한다.
16+
- **shuffle 메서드**를 사용해야하는 이유는, NumberGenerate 함수를 만들어서 사용시에, 중복되는 숫자가 존재 할 경우가 존재함.
17+
- **shuffle 메서드**는 1~45 까지의 숫자를 먼저 random하게 섞고 이후 앞의 수 6개를 가져올 수 있기 때문에 중복 수가 존재하지 않도록 관리가 가능함.
18+
19+
20+
- Collections.sort() 메소드를 활용해 정렬 가능하다.
21+
- Collections.shuffle()을 통해 숫자를 가져온 후, Collections.sort()로 정렬 후 반환.
22+
23+
24+
- ArrayList의 contains() 메소드를 활용하면 어떤 값이 존재하는지 유무를 판단할 수 있다.
25+
26+
## 2단계 - 로또 당첨
27+
28+
### 기능 요구 사항
29+
- 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다.
30+
- 로또 당첨 번호를 체크하는 LottoResultChecker 객체에서 로또 당첨 결과를 반환한다.
31+
- InputParser를 통해 입력값을 체크한다.
32+
33+
34+
- 객체 책임
35+
- LottoRank: 로또 당첨 등수를 나타내는 Enum으로 miss부터 six까지의 등급을 가짐
36+
- LottoResult: 당첨 번호를 가지는 객체 `Lotto`를 상속하여 같은 로또이지만 당첨 결과값이라는 부분이 다름 비교하여 몇개가 맞는지 matchCount를 반환함
37+
- LottoResultChecker: LottoResult를 기반으로 결과를 반환하도록 함.
38+
- WinningResult: LottoResultChecker에서 반환한 결과로, 금액 및 통계 계산을 해줌.
39+
40+
## 3단계 - 로또 2등 당첨
41+
- 2등을 위한 보너스볼을 추첨한다.
42+
- 당첨 통계에 2등을 추가한다.
43+
- LottoRank 객체에 2등 당첨의 경우를 추가한다.
44+
45+
46+
- 2등 당첨 조건은 당첨 번호 5개 일치 + 보너스 볼 일치다.
47+
- LottoResult에서 BonusBall이 일치하는지 검사해준다
48+
- 보너스 볼 기능을 추가
49+
- Rank 열거형에 2등 조건(5개 일치 + 보너스 볼 일치)을 추가
50+
- 보너스 볼 일치 여부를 확인하는 로직을 구현.
51+
52+
## 4단계 - 로또 수동 입력
53+
- 현재 로또 생성기는 자동 생성 기능만 제공한다.
54+
- 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다.
55+
- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.
56+
57+
58+
- 사용자가 직접 로또 번호를 입력할 수 있는 기능을 추가.
59+
- 수동 구매와 자동 구매를 혼합하여 사용할 수 있도록 구현.
60+
- 수동 로또 개수와 금액 검증 로직을 추가.

src/main/java/lotto/LottoMain.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package lotto;
2+
3+
import lotto.controller.LottoController;
4+
5+
public class LottoMain {
6+
7+
public static void main(String[] args) {
8+
final LottoController lottoController = new LottoController();
9+
10+
lottoController.run();
11+
}
12+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package lotto.controller;
2+
3+
import java.util.List;
4+
import lotto.domain.*;
5+
import lotto.ui.InputView;
6+
import lotto.ui.OutputView;
7+
import lotto.util.InputParser;
8+
9+
public class LottoController {
10+
11+
private final LottoStore lottoStore = new LottoStore();
12+
13+
public void run() {
14+
final Long inputPayment = InputView.inputPayment();
15+
16+
final Payment payment = InputParser.parsePayment(inputPayment);
17+
18+
Lottos userLottos = processLottoGeneration(payment);
19+
20+
LottoResultChecker resultChecker = processLottoResult();
21+
22+
displayResults(userLottos, resultChecker, payment);
23+
}
24+
25+
private Lottos processLottoGeneration(Payment payment) {
26+
final int manualLottoCount = InputView.inputManualLottoCount();
27+
final List<String> manualLottoStrings = InputView.inputManualLottoStrings(manualLottoCount);
28+
29+
final Lottos manualLottos = InputParser.parseLottos(manualLottoStrings);
30+
31+
final Lottos userLottos = lottoStore.generateLottosWithManualLottos(manualLottos, payment);
32+
OutputView.printLottos(userLottos);
33+
34+
return userLottos;
35+
}
36+
37+
private LottoResultChecker processLottoResult() {
38+
final LottoResult lottoResult = InputView.inputLottoResult();
39+
final LottoNumber bonusBall = InputView.inputBonusBall();
40+
41+
return new LottoResultChecker(lottoResult, bonusBall);
42+
}
43+
44+
private void displayResults(Lottos userLottos, LottoResultChecker checker, Payment payment) {
45+
final WinningResult winningResult = checker.matchLottos(userLottos);
46+
47+
OutputView.printResults(winningResult);
48+
OutputView.printPrize(winningResult, payment);
49+
}
50+
}

src/main/java/lotto/domain/Lotto.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package lotto.domain;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashSet;
5+
import java.util.List;
6+
import java.util.Set;
7+
8+
public class Lotto {
9+
10+
private static final int LOTTO_SIZE = 6;
11+
12+
private final List<LottoNumber> numbers;
13+
14+
public Lotto(final List<LottoNumber> numbers) {
15+
validateSize(numbers);
16+
validateNoDuplicates(numbers);
17+
this.numbers = new ArrayList<>(numbers);
18+
}
19+
20+
public static Lotto of(final List<Integer> numbers) {
21+
return new Lotto(numbers.stream().map(LottoNumber::of).toList());
22+
}
23+
24+
public String toString() {
25+
return numbers.toString();
26+
}
27+
28+
public int size() {
29+
return numbers.size();
30+
}
31+
32+
public List<LottoNumber> numbers() {
33+
return new ArrayList<>(numbers);
34+
}
35+
36+
private static void validateSize(List<?> numbers) {
37+
if (numbers.size() != LOTTO_SIZE) {
38+
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
39+
}
40+
}
41+
42+
private static void validateNoDuplicates(List<LottoNumber> items) {
43+
Set<LottoNumber> uniqueItems = new HashSet<>(items);
44+
if (uniqueItems.size() != items.size()) {
45+
throw new IllegalArgumentException("로또 번호에 중복된 숫자가 있습니다.");
46+
}
47+
}
48+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package lotto.domain;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public class LottoNumber {
7+
8+
private static final int MIN_LOTTO_NUMBER = 1;
9+
private static final int MAX_LOTTO_NUMBER = 45;
10+
11+
private static final Map<Integer, LottoNumber> CACHE = new HashMap<>();
12+
13+
static {
14+
for (int i = MIN_LOTTO_NUMBER; i <= MAX_LOTTO_NUMBER; i++) {
15+
CACHE.put(i, new LottoNumber(i));
16+
}
17+
}
18+
19+
private final int number;
20+
21+
private LottoNumber(final int number) {
22+
this.number = number;
23+
}
24+
25+
public static LottoNumber of(final int number) {
26+
validateNumberRange(number);
27+
return CACHE.get(number);
28+
}
29+
30+
private static void validateNumberRange(final int number) {
31+
if (number < MIN_LOTTO_NUMBER || number > MAX_LOTTO_NUMBER) {
32+
throw new IllegalArgumentException("로또 번호는 " + MIN_LOTTO_NUMBER + "부터 " +
33+
MAX_LOTTO_NUMBER + " 사이의 숫자여야 합니다.");
34+
}
35+
}
36+
37+
public int number() {
38+
return number;
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return String.valueOf(number);
44+
}
45+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package lotto.domain;
2+
3+
import java.util.HashSet;
4+
import java.util.List;
5+
import java.util.Set;
6+
7+
public class LottoResult extends Lotto {
8+
9+
public LottoResult(final List<LottoNumber> numbers) {
10+
super(numbers);
11+
}
12+
13+
public static LottoResult of(final List<Integer> numbers) {
14+
return new LottoResult(numbers.stream().map(LottoNumber::of).toList());
15+
}
16+
17+
public int matchCount(final Lotto userLotto) {
18+
List<LottoNumber> winningNumbers = this.numbers();
19+
List<LottoNumber> userNumbers = userLotto.numbers();
20+
21+
Set<LottoNumber> winningNumbersSet = new HashSet<>(winningNumbers);
22+
23+
int count = 0;
24+
for (LottoNumber number : userNumbers) {
25+
if (winningNumbersSet.contains(number)) {
26+
count++;
27+
}
28+
}
29+
30+
return count;
31+
}
32+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package lotto.domain;
2+
3+
import java.util.EnumMap;
4+
import java.util.Map;
5+
6+
public class LottoResultChecker {
7+
8+
private final LottoResult winningNumbers;
9+
private final LottoNumber bonusBall;
10+
11+
public LottoResultChecker(final LottoResult winningNumbers, final LottoNumber bonusBall) {
12+
validateNumber(winningNumbers, bonusBall);
13+
this.winningNumbers = winningNumbers;
14+
this.bonusBall = bonusBall;
15+
}
16+
17+
public WinningResult matchLottos(Lottos userLottos) {
18+
Map<Rank, Integer> rankCounts = new EnumMap<>(Rank.class);
19+
20+
for (Rank rank : Rank.values()) {
21+
rankCounts.put(rank, 0);
22+
}
23+
24+
for (Lotto lotto : userLottos) {
25+
Rank rank = getRankByCount(lotto);
26+
rankCounts.put(rank, rankCounts.get(rank) + 1);
27+
}
28+
29+
return new WinningResult(rankCounts);
30+
}
31+
32+
private Rank getRankByCount(final Lotto lotto) {
33+
return Rank.valueOf(matchCount(lotto), hasBonusBall(lotto));
34+
}
35+
36+
private boolean hasBonusBall(final Lotto lotto) {
37+
return lotto.numbers().contains(bonusBall);
38+
}
39+
40+
private int matchCount(Lotto userLotto) {
41+
return winningNumbers.matchCount(userLotto);
42+
}
43+
44+
private void validateNumber(final LottoResult winningNumbers, final LottoNumber bonusBall) {
45+
if (winningNumbers.numbers().contains(bonusBall)) {
46+
throw new RuntimeException("보너스 번호는 기존 번호와 중복될 수 없습니다.");
47+
}
48+
}
49+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package lotto.domain;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.Comparator;
6+
import java.util.List;
7+
8+
public class LottoStore {
9+
10+
private static final int LOTTO_LENGTH = 6;
11+
private static final int MIN_LOTTO_NUMBER = 1;
12+
private static final int MAX_LOTTO_NUMBER = 45;
13+
private static final int LOTTO_PRICE = 1000;
14+
15+
private final List<LottoNumber> lottoNumberPool;
16+
17+
public LottoStore() {
18+
this.lottoNumberPool = new ArrayList<>();
19+
for (int i = MIN_LOTTO_NUMBER; i <= MAX_LOTTO_NUMBER; i++) {
20+
lottoNumberPool.add(LottoNumber.of(i));
21+
}
22+
}
23+
24+
public Lottos generateLottosWithManualLottos(final Lottos manualLottos, final Payment payment) {
25+
long autoLottoCount = payment.value() / LOTTO_PRICE;
26+
List<Lotto> combinedLottos = new ArrayList<>(manualLottos.lottos());
27+
28+
for (int i = 0; i < autoLottoCount - manualLottos.size(); i++) {
29+
combinedLottos.add(generateLotto());
30+
}
31+
32+
return new Lottos(combinedLottos);
33+
}
34+
35+
private Lotto generateLotto() {
36+
List<LottoNumber> numberPool = new ArrayList<>(lottoNumberPool);
37+
38+
Collections.shuffle(numberPool);
39+
40+
List<LottoNumber> selectedNumbers = numberPool.subList(0, LOTTO_LENGTH);
41+
42+
List<LottoNumber> sortedNumbers = new ArrayList<>(selectedNumbers);
43+
sortedNumbers.sort(Comparator.comparing(LottoNumber::number));
44+
45+
return new Lotto(sortedNumbers);
46+
}
47+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package lotto.domain;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
7+
public record Lottos(List<Lotto> lottos) implements Iterable<Lotto> {
8+
9+
public Lottos {
10+
lottos = new ArrayList<>(lottos); // 불변성을 위한 방어적 복사
11+
}
12+
13+
public void add(final Lotto lotto) {
14+
lottos.add(lotto);
15+
}
16+
17+
public void addAll(final Lottos manualLottos) {
18+
lottos.addAll(manualLottos.lottos);
19+
}
20+
21+
public int size() {
22+
return lottos.size();
23+
}
24+
25+
public static Lottos of(List<Lotto> lottos) {
26+
return new Lottos(lottos);
27+
}
28+
29+
@Override
30+
public Iterator<Lotto> iterator() {
31+
return lottos.iterator();
32+
}
33+
34+
@Override
35+
public List<Lotto> lottos() {
36+
return lottos;
37+
}
38+
}

0 commit comments

Comments
 (0)