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

[Step5] 로또(수동) #1698

Merged
merged 5 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 83 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,86 @@ LotteryTicket - 로또

# [5단계] 로또(수동)

// TODO
**기능 요구사항**

- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다.
- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.

```
구입금액을 입력해 주세요.
14000

수동으로 구매할 로또 수를 입력해 주세요.
3

수동으로 구매할 번호를 입력해 주세요.
8, 21, 23, 41, 42, 43
3, 5, 11, 16, 32, 38
7, 11, 16, 35, 36, 44

수동으로 3장, 자동으로 11개를 구매했습니다.
[8, 21, 23, 41, 42, 43]
[3, 5, 11, 16, 32, 38]
[7, 11, 16, 35, 36, 44]
[1, 8, 11, 31, 41, 42]
[13, 14, 16, 38, 42, 45]
[7, 11, 30, 40, 42, 43]
[2, 13, 22, 32, 38, 45]
[23, 25, 33, 36, 39, 41]
[1, 3, 5, 14, 22, 45]
[5, 9, 38, 41, 43, 44]
[2, 8, 9, 18, 19, 21]
[13, 14, 18, 21, 23, 35]
[17, 21, 29, 37, 42, 45]
[3, 8, 27, 30, 35, 44]

지난 주 당첨 번호를 입력해 주세요.
1, 2, 3, 4, 5, 6
보너스 볼을 입력해 주세요.
7

당첨 통계
---------
3개 일치 (5000원)- 1개
4개 일치 (50000원)- 0개
5개 일치 (1500000원)- 0개
5개 일치, 보너스 볼 일치(30000000원) - 0개
6개 일치 (2000000000원)- 0개
총 수익률은 0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
```

## 프로그래밍 요구사항

- **규칙 3: 모든 원시값과 문자열을 포장한다.**
- **규칙 5: 줄여쓰지 않는다(축약 금지).**
- **예외 처리를 통해 에러가 발생하지 않도록 한다.**
- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외
- java enum을 적용해 프로그래밍을 구현한다.
- 규칙 8: 일급 콜렉션을 쓴다.
- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.
- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- else 예약어를 쓰지 않는다.

힌트
- 규칙 3: 모든 원시값과 문자열을 포장한다.
- 로또 숫자 하나는 int 타입이다. 이 숫자 하나를 추상화한 LottoNo 객체를 추가해 구현한다.
- 예외 처리를 통해 에러가 발생하지 않도록 한다.
- 사용자가 잘못된 값을 입력했을 때 java exception으로 에러 처리를 한다.
- java8에 추가된 Optional을 적용해 NullPointerException이 발생하지 않도록 한다.

## 기능목록 리스트

- [X] 수동으로 구매시 사용자로부터 입력을 받는다.
- [X] 수동으로 만들어진 로또와 자동으로 만들어진 로또 분리한다.
- [X] 사용자 입력값을 예외처리한다.
- [X] 수동 입력시 출력 내용 변경

- [X] int 값 로또 번호를 LottoNo 으로 변경한다.

Choose a reason for hiding this comment

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

머지 전에 전체적인 코드를 한번 훑어봤는데 아래 부분에 궁금한 점이 있어 코멘트 남깁니다.

if (Objects.isNull(matchTypeMap) || matchTypeMap.isEmpty()){

파일 체인지론 잡히지 않아서 링크를 걸었어요.

위 부분에서 아래와 같이 검증해주는 부분이 있는데

private void setMatchTypeMap(Map<LotteryMatchType, Integer> matchTypeMap) {
	if (Objects.isNull(matchTypeMap) || matchTypeMap.isEmpty()){
		throw new IllegalArgumentException(NOT_FOUND_LOTTERY_MATCH_TYPE);
	}
	this.matchTypeMap = matchTypeMap;
}

Map에 데이터가 하나도 없는 경우도 아래와 같이 출력되어야 하지 않을까요?
위와 같이 처리하면 예외가 발생할 것 같아서요.

3개 일치 (5000원)- 0개
4개 일치 (50000원)- 0개
5개 일치 (1500000원)- 0개
5개 일치, 보너스 볼 일치 (30000000원)- 0개
6개 일치 (2000000000원)- 0개

Copy link
Author

Choose a reason for hiding this comment

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

맞네요 👍 테스트 케이스가 빈약하네요. 테스트 코드 추가 후, 코드 변경하겠습니다ㅎ








10 changes: 8 additions & 2 deletions src/main/java/lottery/Buyer.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ public Tickets tickets() {
return tickets;
}

public void buyTicket(Store store) {
public void buyTicket(Store store, Tickets manualTickets) {
requireNonNull(store);
this.tickets = store.getTicket(this);
hasAffordableTicketMoney(manualTickets.size());
this.tickets = store.getTicket(this, manualTickets);
}

private void hasAffordableTicketMoney(int size) {
Money totalManualPrice = Store.TICKET_PRICE.multiply(size);
this.money.minus(totalManualPrice);
}

public Result checkTicket(InfoCenter infoCenter) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/lottery/LotteryMatchTypeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private LotteryMatchTypeMap(Map<LotteryMatchType, Integer> matchTypeMap) {
}

private void setMatchTypeMap(Map<LotteryMatchType, Integer> matchTypeMap) {
if (Objects.isNull(matchTypeMap) || matchTypeMap.isEmpty()){
if (Objects.isNull(matchTypeMap)){
throw new IllegalArgumentException(NOT_FOUND_LOTTERY_MATCH_TYPE);
}
this.matchTypeMap = matchTypeMap;
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/lottery/LottoNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package lottery;

import java.util.Objects;

public class LottoNumber implements Comparable<LottoNumber> {

private int value;

public static LottoNumber of(int value) {
return new LottoNumber(value);
}

public static LottoNumber of(String value) {
return new LottoNumber(Integer.parseInt(value));
}

private LottoNumber(int value) {
setValue(value);
}

public int value() {
return value;
}

private void setValue(int value) {
if (value <= 0 || value > 45) {
throw new IllegalArgumentException("유효하지 않는 숫자입니다.");
}
this.value = value;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof LottoNumber))
return false;
LottoNumber lottoNumber = (LottoNumber)o;
return value == lottoNumber.value;
}

@Override
public int hashCode() {
return Objects.hash(value);
}

@Override
public int compareTo(LottoNumber o) {
return Integer.compare(this.value, o.value);
}
}
21 changes: 15 additions & 6 deletions src/main/java/lottery/LottoNumbers.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,46 @@

import static lottery.Ticket.*;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import lottery.exceptions.InvalidTicketSizeException;

public class LottoNumbers {

private static final String NOT_FOUND_LOTTO_NUMBERS_EXCEPTION_MESSAGE = "로또 번호에 대한 정보가 없습니다";

private Set<Integer> numbers;
private Set<LottoNumber> numbers;

public static LottoNumbers from(Set<Integer> numbers) {
public static LottoNumbers from(Set<LottoNumber> numbers) {
if (numbers.size() != SIZE_OF_TICKET) {
throw new InvalidTicketSizeException();
}
return new LottoNumbers(numbers);
}

private LottoNumbers(Set<Integer> numbers) {
public static LottoNumbers from(String[] split) {
return new LottoNumbers(Arrays.stream(split).map(LottoNumber::of)
.sorted()
.collect(Collectors.toCollection(LinkedHashSet::new)));
}

private LottoNumbers(Set<LottoNumber> numbers) {
setNumbers(numbers);
}

private void setNumbers(Set<Integer> numbers) {
if (Objects.isNull(numbers) || numbers.isEmpty()){
private void setNumbers(Set<LottoNumber> numbers) {
if (Objects.isNull(numbers) || numbers.isEmpty()) {
throw new IllegalArgumentException(NOT_FOUND_LOTTO_NUMBERS_EXCEPTION_MESSAGE);
}
this.numbers = numbers;
}

public Set<Integer> values() {
public Set<LottoNumber> values() {
return Collections.unmodifiableSet(numbers);
}

Expand Down
87 changes: 59 additions & 28 deletions src/main/java/lottery/Main.java
Original file line number Diff line number Diff line change
@@ -1,54 +1,85 @@
package lottery;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Set;
import java.util.stream.Collectors;

import lottery.view.InputView;
import lottery.view.ResultView;

public class Main {

private static InputView inputView;
private static ResultView resultView;
private static Scanner sc;
private static Store store;
private static InfoCenter infoCenter;

public static void init(){
inputView = new InputView();
resultView = new ResultView();
sc = new Scanner(System.in);
store = new Store();
infoCenter = new InfoCenter();
}
public static void main(String[] args) {
init();
private static final InputView inputView = new InputView();
private static final ResultView resultView = new ResultView();
private static final Scanner sc = new Scanner(System.in);
private static final Store store = new Store();
private static final InfoCenter infoCenter = new InfoCenter();

public static void main(String[] args) {
inputView.askForBuyerMoney();
Money initialMoney = Money.won(Long.parseLong(sc.nextLine().trim()));
Money initialMoney = Money.won(getLongValue());
Buyer buyer = Buyer.of(initialMoney);

buyer.buyTicket(store);
inputView.askForManualTicketCount();
int manualTicketQuantity = getIntegerValue();

inputView.askForManualTicketNumbers();
Tickets manualTickets = getManualTickets(manualTicketQuantity);
buyer.buyTicket(store, manualTickets);

Tickets tickets = buyer.tickets();
inputView.inputCountOfTickets(tickets.size());
inputView.inputCountOfTickets(tickets.sizeOfManualTickets(), tickets.sizeOfAutoTickets());
inputView.printTickets(tickets);

inputView.askForLastWeekTickets();
Set<Integer> ints = Arrays.stream(sc.nextLine().split(","))
.map(a -> Integer.parseInt(a.trim()))
.sorted()
.collect(Collectors.toCollection(LinkedHashSet::new));
Ticket lastWeekTicket = Ticket.of(LottoNumbers.from(ints));
Ticket lastWeekTicket = Ticket.of(LottoNumbers.from(sc.nextLine().split(",")), TicketType.WINNING);

inputView.askForBonusNumber();
int bonus = Integer.parseInt(sc.nextLine().trim());
infoCenter.setLastWeekWinningTicket(new WinningTicket(lastWeekTicket, bonus));
infoCenter.setLastWeekWinningTicket(new WinningTicket(lastWeekTicket, LottoNumber.of(bonus)));

Result result = buyer.checkTicket(infoCenter);

float totalYield = result.getTotalYield(initialMoney);
resultView.printResult(result, totalYield);
}

private static Tickets getManualTickets(int manualTicketQuantity) {
List<Ticket> tickets = new ArrayList<>();
for (int i = 0; i < manualTicketQuantity; i++) {
String s = sc.nextLine();
tickets.add(Ticket.of(LottoNumbers.from(s.split(",")), TicketType.MANUAL));
}
return Tickets.of(tickets);
}

private static long getLongValue() {
try {
return sc.nextLong();
} catch (InputMismatchException e) {
throw new IllegalArgumentException("정수를 입력해주세요.", e);
}
}

private static int getIntegerValue() {
try {
return sc.nextInt();
} catch (InputMismatchException e) {
throw new IllegalArgumentException("정수를 입력해주세요.", e);
} finally {
sc.nextLine();
}
}

private static String getStringValue() {
try {
return sc.nextLine();
} catch (NoSuchElementException e) {
throw new IllegalArgumentException("6자리에 콤마(,)로 구분된 숫자를 입력해주세요", e);
} finally {
sc.nextLine();
}
}
}
12 changes: 8 additions & 4 deletions src/main/java/lottery/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ public TicketMachine getMachine() {
return machine;
}

public Tickets getTicket(Buyer buyer) {
public Tickets getTicket(Buyer buyer, Tickets manualTickets) {
Money buyerMoney = buyer.money();
long ticketCount = buyerMoney.divide(TICKET_PRICE);
buyer.setMoney(buyerMoney.minus(TICKET_PRICE.multiply(ticketCount)));
return machine.create(ticketCount);
int countOfManualTicket = manualTickets.size();

Money minus = buyerMoney.minus(TICKET_PRICE.multiply(countOfManualTicket));
long countOfAutomaticTicket = minus.divide(TICKET_PRICE);
Tickets tickets = machine.automaticTicketCreate(countOfAutomaticTicket);
buyer.setMoney(buyerMoney.minus(TICKET_PRICE.multiply(countOfAutomaticTicket + countOfManualTicket)));
return tickets.merge(manualTickets);
}
}
24 changes: 20 additions & 4 deletions src/main/java/lottery/Ticket.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,39 @@ public final class Ticket {

public final static int SIZE_OF_TICKET = 6;
private LottoNumbers lottoNumbers;
private TicketType type;

public static Ticket of(LottoNumbers lottoNumbers) {
return new Ticket(lottoNumbers);
public static Ticket of(LottoNumbers lottoNumbers, TicketType type) {
return new Ticket(lottoNumbers,type);
}

private Ticket(LottoNumbers lottoNumbers) {
private Ticket(LottoNumbers lottoNumbers, TicketType type) {
setNumbers(lottoNumbers);
setType(type);
}

private void setType(TicketType type) {
if (Objects.isNull(type)){
throw new IllegalArgumentException("로또 티켓 타입을 찾을 수 없습니다");
}
this.type = type;
}

private void setNumbers(LottoNumbers lottoNumbers) {
if (Objects.isNull(lottoNumbers)){
throw new IllegalArgumentException("로또 번호를 찾을 수 없습니다");
}
this.lottoNumbers = lottoNumbers;
}

public Set<Integer> numbers() {
public Set<LottoNumber> numbers() {
return lottoNumbers.values();
}

public TicketType getType() {
return type;
}

@Override
public boolean equals(Object o) {
if (this == o)
Expand Down