Skip to content

Commit e2cfedc

Browse files
IntegerousYoungJae Kim
authored and
YoungJae Kim
committed
2주차 - Step2 로또(자동) 피드백 요청드립니다. (#226)
* [step2] docs : 구현 기능 목록 생성, step1 피드백 반영 * [step2] feat : LottoNumber 객체 생성, 구현 기능 목록 수정 * [step2] refactor : 매직넘버 대신 상수 사용, 로또번호 범위 예외처리 테스트 * [step2] feat : LottoTicket 테스트, 객체 생성 * [step2] feat : 로또번호 생성 테스트, 메서드 추가 * [step2] refactor : stream 사용, 매직넘버 대신 상수 사용 * [step2] refactor : LottoTicket 정적팩토리 메서드명 변경, 정렬 메서드 추가 * [step2] feat : 로또복권 생성기능을 LottoTicketGenerator로 위임 * [step2] feat : 중복된 로또번호 검증 메소드, 예외처리 테스트 추가 * [step2] feat : 로또복권의 로또번호 개수가 6개가 아닌 경우 검증 메서드, 테스트 추가 * [step2] docs : 구현 기능 목록 업데이트 * [step2] feat : 당첨번호(LuckyNumber) 객체 생성, toString()메서드 추가 * [step2] refactor : LottoTicket을 List<LottoNumber>로 변경, 중복번호 검증 메서드 수정 * [step2] feat : 당첨번호 비교 테스트, 메서드 추가 * [step2] feat : 로또자동판매기(LottoSeller) 객체, 테스트 생성 * [step2] refactor : 예외처리 추가, 구입할 로또복권 개수 계산 메서드 추가 * [step2] refactor : 구입할 개수만큼 로또복권 발행하는 메서드 추가, LottoTickets 객체 삭제 * [step2] refactor : 복권 생성할때마다 lottoBalls 생성하지 않고 객체로 분리 * [step2] feat : Enum 클래스(LottoRank) 추가 * [step2] feat : 당첨 통계(LottoResult) 클래스 생성 및 메서드 작성중 * [step2] refactor : (커밋 병합) view, apllication 생성, LuckNumber 객체 제거 등 * [step2] refactor : 패키지 분리, 총 수익률계산 메서드 수정, 상수 사용
1 parent 5da0c84 commit e2cfedc

17 files changed

+646
-26
lines changed

README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,43 @@
5252
- [x] REFACTORING
5353
- [x] 외부로 위임된 StringCalculator 안의 메서드들 정리
5454

55-
5655
### step1 - 이슈
57-
- Element 객체를 불변 객체로 만들 필요가 있을지
56+
- Element 객체를 불변 객체로 만들 필요가 있을지
57+
58+
59+
### step2 - 로또(자동)
60+
- [ ] 로또번호(LottoNumber) 객체 생성
61+
- [x] TEST - 로또번호의 범위는 1~45의 자연수
62+
- [x] REFACTORING
63+
- [ ] 로또복권(LottoTicket) 객체 생성
64+
- [x] TEST - 로또복권의 로또번호는 중복이 없다
65+
- [x] TEST - 로또복권의 로또번호는 6개다
66+
- [x] TEST - 당첨번호와 비교해서 일치하는 개수를 반환한다 (ArrayList.contains())
67+
- [ ] REFACTORING
68+
- [ ] 로또복권생성기(LottoTicketGenerator) 객체 생성
69+
- [x] TEST - 로또번호(1~45)가 담긴 리스트를 생성한다
70+
- [x] TEST - 생성된 로또번호를 섞는다 (Collections.shuffle())
71+
- [x] TEST - 섞인 로또번호 리스트에서 6개의 로또번호를 선택한다
72+
- [x] TEST - 선택된 로또번호를 정렬한다 (Collections.sort())
73+
- [ ] REFACTORING
74+
- [ ] 당첨번호(LuckyNumber) 객체 생성
75+
- [x] TEST - 입력받은 당첨번호로 일급 컬렉션을 생성한다
76+
- [ ] REFACTORING
77+
- [ ] 로또자동판매기(LottoSeller) 객체 생성
78+
- [x] TEST - 금액을 입력 받아 구입할 로또 복권 개수를 반환한다
79+
- [x] TEST - 구입할 개수만큼 로또복권을 생성한다
80+
- [x] TEST - 각 일치 개수 별 당첨된 복권의 개수를 반환한다
81+
- [x] TEST - 생성된 로또복권의 수익률을 계산한다
82+
- [ ] REFACTORING
83+
- [ ] 당첨결과를 매칭할 Enum(LottoRank) 객체 생성
84+
- [x] REFACTORING
85+
- [ ] InputView 객체 생성
86+
- [x] TEST - 구입 금액을 입력받고, 출력한다
87+
- [x] TEST - 구매 개수를 LottoSeller로 부터 반환받아서 출력한다
88+
- [x] TEST - 구매 개수만큼 생성된 로또복권들을 출력한다
89+
- [x] TEST - 지난주 당첨 번호를 입력 받는다
90+
- [ ] REFACTORING
91+
- [ ] OutputView 객체 생성
92+
- [x] TEST - 각 일치 개수 별 당첨된 복권의 개수를 LottoSeller로 부터 반환받아서 출력한다
93+
- [ ] TEST - 총 수익률을 LottoSeller로 부터 반환받아서 출력한다.
94+
- [ ] REFACTORING

src/main/java/step1/Element.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class Element {
1010

1111
private int value;
1212

13-
public Element(int value) {
13+
private Element(int value) {
1414
this.value = value;
1515
}
1616

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package step2;
2+
3+
import step2.domain.LottoRank;
4+
import step2.domain.LottoSeller;
5+
import step2.domain.LottoTicket;
6+
import step2.view.InputView;
7+
import step2.view.OutputView;
8+
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
public class LottoApplication {
13+
14+
public static void main(String[] args) {
15+
final int inputMoney = InputView.askMoneyToInput();
16+
final int numberOfTicketsToBuy = LottoSeller.countLottoTickets(inputMoney);
17+
18+
OutputView.printNumberOfLottoTickets(numberOfTicketsToBuy);
19+
20+
List<LottoTicket> lottoTickets = LottoSeller.issueLottoTicket(numberOfTicketsToBuy);
21+
OutputView.printLottoTickets(lottoTickets);
22+
23+
LottoTicket luckyNumber = LottoSeller.getLuckyNumber(InputView.askLuckyNumber());
24+
Map<LottoRank, List<LottoTicket>> lottoResultMap = LottoSeller.getLottoResult(lottoTickets, luckyNumber);
25+
26+
OutputView.printResult(lottoResultMap, numberOfTicketsToBuy);
27+
}
28+
}
29+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package step2.domain;
2+
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.IntStream;
6+
7+
public class LottoBalls {
8+
9+
private static List<Integer> lottoBalls = IntStream
10+
.rangeClosed(LottoNumber.MINIMUM_LOTTO_NUMBER, LottoNumber.MAXIMUM_LOTTO_NUMBER)
11+
.boxed()
12+
.collect(Collectors.toList());
13+
14+
public static List<Integer> getLottoBalls() {
15+
return lottoBalls;
16+
}
17+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package step2.domain;
2+
3+
import java.util.Objects;
4+
5+
public class LottoNumber {
6+
static final String ALERT_OUT_OF_RANGE_OF_LOTTO_NUMBER = "로또번호는 1부터 45까지의 자연수만 가능합니다.";
7+
static final int MINIMUM_LOTTO_NUMBER = 1;
8+
static final int MAXIMUM_LOTTO_NUMBER = 45;
9+
private int number;
10+
11+
private LottoNumber(int number) {
12+
if (isOutOfRange(number)) {
13+
throw new IllegalArgumentException(ALERT_OUT_OF_RANGE_OF_LOTTO_NUMBER);
14+
}
15+
this.number = number;
16+
}
17+
18+
public static LottoNumber from(Integer number) {
19+
return new LottoNumber(number);
20+
}
21+
22+
private boolean isOutOfRange(int number) {
23+
return number < MINIMUM_LOTTO_NUMBER || number > MAXIMUM_LOTTO_NUMBER;
24+
}
25+
26+
public int getNumber() {
27+
return number;
28+
}
29+
30+
@Override
31+
public boolean equals(Object o) {
32+
if (this == o) return true;
33+
if (o == null || getClass() != o.getClass()) return false;
34+
LottoNumber that = (LottoNumber) o;
35+
return number == that.number;
36+
}
37+
38+
@Override
39+
public int hashCode() {
40+
return Objects.hash(number);
41+
}
42+
43+
@Override
44+
public String toString() {
45+
return number + "";
46+
}
47+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package step2.domain;
2+
3+
import java.util.Arrays;
4+
5+
public enum LottoRank {
6+
7+
FIRST_PLACE(6, 2000000000),
8+
SECOND_PLACE(5, 1500000),
9+
THIRD_PLACE(4, 50000),
10+
FOURTH_PLACE(3, 5000),
11+
LOSER(0, 0);
12+
13+
private int numberOfMatchedToLuckyNumber;
14+
private int prizeMoney;
15+
16+
LottoRank(int numberOfMatchedToLuckyNumber, int prizeMoney) {
17+
this.numberOfMatchedToLuckyNumber = numberOfMatchedToLuckyNumber;
18+
this.prizeMoney = prizeMoney;
19+
}
20+
21+
public static LottoRank from(int countMatchedNumbers) {
22+
return Arrays.stream(LottoRank.values())
23+
.filter(lottoRank -> lottoRank.numberOfMatchedToLuckyNumber == countMatchedNumbers)
24+
.findFirst()
25+
.orElse(LOSER);
26+
}
27+
28+
public int getNumberOfMatchedToLuckyNumber() {
29+
return numberOfMatchedToLuckyNumber;
30+
}
31+
32+
public int getPrizeMoney() {
33+
return prizeMoney;
34+
}
35+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package step2.domain;
2+
3+
import java.util.*;
4+
import java.util.stream.Collectors;
5+
6+
public class LottoSeller {
7+
static final int PRICE_OF_A_LOTTO_TICKET = 1000;
8+
static final String ALERT_MISSING_MONEY = "돈을 넣어주세요. 로또복권은 한 장당 1000원 입니다.";
9+
10+
public static int countLottoTickets(int inputMoney) {
11+
validationInputMoney(inputMoney);
12+
return inputMoney / PRICE_OF_A_LOTTO_TICKET;
13+
}
14+
15+
public static List<LottoTicket> issueLottoTicket(int numberOfTicketsToBuy) {
16+
List<LottoTicket> lottoTickets = new ArrayList<>();
17+
18+
for (int i = 0; i < numberOfTicketsToBuy; i++) {
19+
lottoTickets.add(LottoTicketGenerator.issue());
20+
}
21+
return lottoTickets;
22+
}
23+
24+
public static LottoTicket getLuckyNumber(List<Integer> inputLuckyNumber) {
25+
List<LottoNumber> lottoTicket = inputLuckyNumber.stream()
26+
.map(LottoNumber::from)
27+
.collect(Collectors.toList());
28+
return LottoTicketGenerator.issue(lottoTicket);
29+
}
30+
31+
public static Map<LottoRank, List<LottoTicket>> getLottoResult(List<LottoTicket> lottoTickets, LottoTicket luckyNumber) {
32+
Map<LottoRank, List<LottoTicket>> lottoResultMap = new HashMap<>();
33+
34+
for (LottoTicket ticket : lottoTickets) {
35+
LottoRank lottoRank = LottoRank.from(ticket.getNumberOfMatchedToLuckyNumber(luckyNumber));
36+
List<LottoTicket> ticketsCorrespondingToRank = lottoResultMap.getOrDefault(lottoRank, new ArrayList<>());
37+
ticketsCorrespondingToRank.add(ticket);
38+
lottoResultMap.put(lottoRank, ticketsCorrespondingToRank);
39+
}
40+
return lottoResultMap;
41+
}
42+
43+
private static void validationInputMoney(int inputMoney) {
44+
if (inputMoney < PRICE_OF_A_LOTTO_TICKET) {
45+
throw new IllegalArgumentException(ALERT_MISSING_MONEY);
46+
}
47+
}
48+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package step2.domain;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.Objects;
6+
7+
public class LottoTicket {
8+
static final String ALERT_NUMBER_OVERLAP = "중복된 로또 번호가 존재합니다.";
9+
static final String AlERT_DIFFERENT_SIZE_OF_NUMBERS = "로또번호의 개수가 6개가 아닙니다.";
10+
11+
private final List<LottoNumber> lottoTicket;
12+
13+
LottoTicket(List<LottoNumber> lottoTicket) {
14+
validationNumberSize(lottoTicket);
15+
validationNumberOverlap(lottoTicket);
16+
this.lottoTicket = lottoTicket;
17+
}
18+
19+
private void validationNumberSize(List<LottoNumber> lottoTicket) {
20+
if (lottoTicket.size() != LottoTicketGenerator.BASIC_LOTTO_SIZE) {
21+
throw new IllegalArgumentException(AlERT_DIFFERENT_SIZE_OF_NUMBERS);
22+
}
23+
}
24+
25+
private void validationNumberOverlap(List<LottoNumber> lottoTicket) {
26+
long SizeOfLottoTicket = lottoTicket.stream()
27+
.mapToLong(LottoNumber::getNumber)
28+
.distinct()
29+
.count();
30+
31+
if (SizeOfLottoTicket != LottoTicketGenerator.BASIC_LOTTO_SIZE) {
32+
throw new IllegalArgumentException(ALERT_NUMBER_OVERLAP);
33+
}
34+
}
35+
36+
public int getNumberOfMatchedToLuckyNumber(LottoTicket luckyNumber) {
37+
return (int) luckyNumber.getLottoTicket().stream()
38+
.filter(lottoTicket::contains)
39+
.count();
40+
}
41+
42+
public List<LottoNumber> getLottoTicket() {
43+
return Collections.unmodifiableList(lottoTicket);
44+
}
45+
46+
47+
@Override
48+
public boolean equals(Object o) {
49+
if (this == o) return true;
50+
if (o == null || getClass() != o.getClass()) return false;
51+
LottoTicket that = (LottoTicket) o;
52+
return Objects.equals(lottoTicket, that.lottoTicket);
53+
}
54+
55+
@Override
56+
public int hashCode() {
57+
return Objects.hash(lottoTicket);
58+
}
59+
60+
@Override
61+
public String toString() {
62+
return "LottoTicket{" +
63+
"lottoTicket=" + lottoTicket +
64+
'}';
65+
}
66+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package step2.domain;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.stream.Collectors;
6+
7+
public class LottoTicketGenerator {
8+
static final int BASIC_LOTTO_SIZE = 6;
9+
10+
static LottoTicket issue() {
11+
return new LottoTicket(pickLottoBalls(LottoBalls.getLottoBalls()));
12+
}
13+
14+
static LottoTicket issue(List<LottoNumber> lottoTicket) {
15+
return new LottoTicket(lottoTicket);
16+
}
17+
18+
static List<LottoNumber> pickLottoBalls(List<Integer> lottoBalls) {
19+
shuffleLottoBalls(lottoBalls);
20+
return lottoBalls.stream()
21+
.limit(BASIC_LOTTO_SIZE)
22+
.sorted()
23+
.map(LottoNumber::from)
24+
.collect(Collectors.toList());
25+
}
26+
27+
static void shuffleLottoBalls(List<Integer> lottoBalls) {
28+
Collections.shuffle(lottoBalls);
29+
}
30+
31+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package step2.view;
2+
3+
import java.util.Arrays;
4+
import java.util.List;
5+
import java.util.Scanner;
6+
import java.util.stream.Collectors;
7+
8+
public class InputView {
9+
private static final String LUCKY_NUMBER_SEPARATOR = ",";
10+
private static Scanner scanner = new Scanner(System.in);
11+
12+
public static int askMoneyToInput() {
13+
System.out.println("구입금액을 입력해 주세요.");
14+
return scanner.nextInt();
15+
}
16+
17+
public static List<Integer> askLuckyNumber() {
18+
printBlackLine();
19+
scanner.nextLine();
20+
System.out.println("지난 주 당첨 번호를 입력해주세요.");
21+
String inputLuckyNumber = scanner.nextLine();
22+
printBlackLine();
23+
24+
return Arrays.stream(inputLuckyNumber.split(LUCKY_NUMBER_SEPARATOR))
25+
.map(String::trim)
26+
.mapToInt(Integer::parseInt)
27+
.boxed()
28+
.collect(Collectors.toList());
29+
}
30+
31+
private static void printBlackLine() {
32+
System.out.println();
33+
}
34+
}

0 commit comments

Comments
 (0)