Skip to content
26 changes: 26 additions & 0 deletions docs/step4요구사항.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 로또 구현

## 기능 요구사항

## 입력

- [x] 수동으로 구매할 로또 수를 입력받는다
- [x] 수동 구매 장수는 음수일수 없다.
- [x] 0개 입력시 전부 자동으로 구매한다.
- [x] 공백 입력 시 재입력받도록 한다.
- [x] 수동번호를 수동 갯수만큼 로또를 입력받는다.

## 수동 로또

- [x] 입력받은 로또 리스트를 받아서 생성한다.
- [x] 빈List이거나 null인경우 빈 값으로 판단한다.
- [x] 수동로또가 비어있으면 빈 리스트를 반환한다.

## 로또 티켓

- [x] 구입금액과 수동 로또를 받아서 생성한다.
- [x] 수동로또를 추가하고 남은 금만큼 자동 로또를 생성한다.

## 출력

- [x] 수동, 자동 장수를 출력한다.
8 changes: 6 additions & 2 deletions src/main/java/lotto/LottoApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lotto.domain.Lotto;
import lotto.domain.LottoNumber;
import lotto.domain.LottoTickets;
import lotto.domain.ManualLottos;
import lotto.domain.PurchaseAmount;
import lotto.domain.WinningNumbers;
import lotto.domain.WinningResult;
Expand All @@ -12,9 +13,12 @@
public class LottoApplication {
public static void main(String[] args) {
PurchaseAmount purchaseAmount = new PurchaseAmount(InputView.inputPurchaseAmount());
LottoTickets tickets = LottoTickets.create(purchaseAmount);

ResultView.printTicketCount(tickets.size());
int manualCount = InputView.inputManualLottoCount();
ManualLottos manualLottos = InputView.inputManualLottos(manualCount);

LottoTickets tickets = LottoTickets.create(purchaseAmount, manualLottos);
ResultView.printTicketCount(tickets);
ResultView.printLottoTickets(tickets);

Lotto winningLotto = InputView.inputWinningNumbers();
Expand Down
15 changes: 2 additions & 13 deletions src/main/java/lotto/domain/Lotto.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,19 @@ public Lotto(Set<LottoNumber> numbers) {
}

private static Set<LottoNumber> numbersToSet(int[] intNumbers) {
validateInputSize(intNumbers.length);
Set<LottoNumber> numbers = new HashSet<>();
for (int number : intNumbers) {
numbers.add(LottoNumber.of(number));
}
validateNoDuplicate(numbers.size(), intNumbers.length);
return numbers;
}

public static Lotto from(List<Integer> intNumbers) {
return new Lotto(createLottoNumbers(intNumbers));
}

private static void validateNoDuplicate(int setSize, int inputSize) {
if (setSize != inputSize) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
}

private static void validateInputSize(int length) {
if (length != LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
throw new IllegalArgumentException("로또 번호는 중복없이 6개여야 합니다.");
}
}

Expand All @@ -54,8 +45,6 @@ private static Set<LottoNumber> createLottoNumbers(List<Integer> intNumbers) {
lottoNumbers.add(LottoNumber.of(number));
}

validateNoDuplicate(lottoNumbers.size(), intNumbers.size());
validateInputSize(lottoNumbers.size());
return lottoNumbers;
}

Expand All @@ -65,7 +54,7 @@ public List<Integer> getNumbers() {
result.add(number.getValue());
}
Collections.sort(result);
return result;
return List.copyOf(result);
}

public boolean contains(LottoNumber number) {
Expand Down
16 changes: 4 additions & 12 deletions src/main/java/lotto/domain/LottoNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,16 @@ public class LottoNumber {

private final int value;

private LottoNumber(String value) {
this(Integer.parseInt(value));
}

private LottoNumber(int value) {
validate(value);
this.value = value;
}

public static LottoNumber of(int number) {
validate(number);
return CACHE.get(number);
}

private static void validate(int value) {
if (value < MIN_NUMBER || value > MAX_NUMBER) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 한다");
LottoNumber lottoNumber = CACHE.get(number);
if (lottoNumber == null) {
throw new IllegalArgumentException(String.format("로또 번호는 %d부터 %d 사이의 숫자여야 한다", MIN_NUMBER, MAX_NUMBER));
}
return lottoNumber;
}

public int getValue() {
Expand Down
39 changes: 37 additions & 2 deletions src/main/java/lotto/domain/LottoTickets.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@

public class LottoTickets {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 객체가 담당하는 책임이 많아지고 있다.
Lotto 목록을 관리하면서 매칭 책임과 Lotto 목록을 생성하는 책임으로 두 가지 이상을 담당하고 있는 것 같다.
Lotto 목록을 생성하는 책임을 LottosFactory와 같은 다른 객체를 생성해 분리하면 어떨까?

'LottoTickets tickets = LottosFactory.createLottos(int purchaseAmount, List manualLottos)'

private final List<Lotto> lottos;
private final ManualLottos manualLottos;

public LottoTickets(List<Lotto> lottos) {
this(lottos, new ManualLottos(lottos));
}

public LottoTickets(List<Lotto> lottos, ManualLottos manualLottos) {
this.lottos = lottos;
this.manualLottos = manualLottos;
}

public static LottoTickets create(PurchaseAmount purchaseAmount) {
Expand All @@ -21,15 +27,44 @@ public static LottoTickets create(PurchaseAmount purchaseAmount) {
return new LottoTickets(lottos);
}

public static LottoTickets create(PurchaseAmount purchaseAmount, ManualLottos manualLottos) {
validateManualLottoCount(purchaseAmount, manualLottos);
List<Lotto> lottos = createLottos(purchaseAmount, manualLottos);
return new LottoTickets(lottos, manualLottos);
}

private static void validateManualLottoCount(PurchaseAmount purchaseAmount, ManualLottos manualLottos) {
if (manualLottos.getCount() > purchaseAmount.getLottoCount()) {
throw new IllegalArgumentException("수동로또 개수가 구입 가능한 개수보다 많습니다.");
}
}

private static List<Lotto> createLottos(PurchaseAmount purchaseAmount, ManualLottos manualLottos) {
List<Lotto> lottos = new ArrayList<>(manualLottos.getManualLottos());
int autoCount = purchaseAmount.getLottoCount() - manualLottos.getCount();
for (int i = 0; i < autoCount; i++) {
lottos.add(Lotto.from(LottoNumberGenerator.generate()));
}
return lottos;
}

public int getManualCount() {
return manualLottos.getCount();
}

public int getAutoCount() {
return lottos.size() - manualLottos.getCount();
}

public int size() {
return lottos.size();
}

public List<Lotto> getLottos() {
return new ArrayList<>(lottos);
return List.copyOf(lottos);
}

public WinningResult matchWith(WinningNumbers winningNumbers){
public WinningResult matchWith(WinningNumbers winningNumbers) {
Map<Rank, Integer> result = initializeResult();
for (Lotto lotto : lottos) {
Rank rank = winningNumbers.match(lotto);
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/lotto/domain/ManualLottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package lotto.domain;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

public class ManualLottos {
private final List<Lotto> manualLottos;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
private final List<Lotto> manualLottos;
private final List<String> manualLottos;

위와 같이 사용자가 입력한 값을 String으로 저장하는 역할만 담당해도 되지 않을까?
List로 구현하니 LottoTickets과의 다른 역할이 없을 수도 있겠다.


public ManualLottos(List<Lotto> manualLottos) {
this.manualLottos = Optional.ofNullable(manualLottos)
.orElse(Collections.emptyList());
}

public int getCount() {
return manualLottos.size();
}

public List<Lotto> getManualLottos() {
return List.copyOf(manualLottos);
}
}
53 changes: 51 additions & 2 deletions src/main/java/lotto/view/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lotto.domain.Lotto;
import lotto.domain.LottoNumber;
import lotto.domain.ManualLottos;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -11,8 +12,16 @@ public class InputView {
private static final Scanner scanner = new Scanner(System.in);

public static int inputPurchaseAmount() {
System.out.println("구입금액을 입력해 주세요.");
return Integer.parseInt(scanner.nextLine());
while (true) {
try {
System.out.println("구입금액을 입력해 주세요.");
String input = scanner.nextLine();
validateNotEmpty(input);
return Integer.parseInt(input.trim());
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}

public static Lotto inputWinningNumbers() {
Expand All @@ -33,6 +42,46 @@ private static Lotto parseWinningNumbers(String input) {
public static LottoNumber inputBonusNumber() {
System.out.println("보너스 볼을 입력해 주세요.");
return LottoNumber.of(Integer.parseInt(scanner.nextLine()));
}

public static int inputManualLottoCount() {
while (true) {
try {
System.out.println("\n수동으로 구매할 로또 수를 입력해 주세요.");
String input = scanner.nextLine();
validateNotEmpty(input);
return Integer.parseInt(input.trim());
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}

public static ManualLottos inputManualLottos(int count) {
validateCount(count);
if (count == 0) {
return new ManualLottos(null);
}

System.out.println("수동으로 구매할 번호를 입력해 주세요.");
List<Lotto> manualLottos = new ArrayList<>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
List<Lotto> manualLottos = new ArrayList<>();
List<String> manualLottos = new ArrayList<>();

앞에서 피드백한 바와 같이 String으로 저장하고 반환하는 것은 어떨까?

for (int i = 0; i < count; i++) {
String input = scanner.nextLine();
validateNotEmpty(input);
manualLottos.add(parseWinningNumbers(input));
}
return new ManualLottos(manualLottos);
}

private static void validateCount(int count) {
if (count < 0) {
throw new IllegalArgumentException("음수를 입력할 수 없습니다.");
}
}

private static void validateNotEmpty(String input) {
if (input == null || input.trim().isEmpty()) {
throw new IllegalArgumentException("빈 값을 입력할 수 없습니다.");
}
}
}
6 changes: 4 additions & 2 deletions src/main/java/lotto/view/ResultView.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import java.util.List;

public class ResultView {
public static void printTicketCount(int count) {
System.out.println(String.format("%d개를 구매했습니다.", count));
public static void printTicketCount(LottoTickets tickets) {
int manualCount = tickets.getManualCount();
int autoCount = tickets.getAutoCount();
System.out.println(String.format("수동으로 %d장, 자동으로 %d개를 구매했습니다.", manualCount, autoCount));
}

public static void printLottoTickets(LottoTickets tickets) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/lotto/domain/LottoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public class LottoTest {
void 로또_번호_6개가_아니면_예외발생() {
assertThatThrownBy(() -> new Lotto(1, 2, 3, 4, 5))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("로또 번호는 6개여야 합니다.");
.hasMessage("로또 번호는 중복없이 6개여야 합니다.");
}

@Test
void 로또_번호가_중복되면_예외발생() {
assertThatThrownBy(() -> new Lotto(1, 2, 3, 4, 5, 5))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("로또 번호는 중복될 수 없습니다.");
.hasMessage("로또 번호는 중복없이 6개여야 합니다.");
}

@Test
Expand Down
43 changes: 43 additions & 0 deletions src/test/java/lotto/domain/LottoTicketsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

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


//- [ ] 구입 금액을 1000으로 나눈 갯수반큼 로또 티켓을 발행한다.
Expand Down Expand Up @@ -32,4 +34,45 @@ public class LottoTicketsTest {
assertThat(lotto.getNumbers()).hasSize(6);
}
}

@Test
void 구매금액과_수동로또개수가_같은경우_수동로또만_구매() {
PurchaseAmount purchaseAmount = new PurchaseAmount(2000);
Lotto manual1 = new Lotto(1, 2, 3, 4, 5, 6);
Lotto manual2 = new Lotto(7, 8, 9, 10, 11, 12);
ManualLottos manualLottos = new ManualLottos(Arrays.asList(manual1, manual2));

LottoTickets tickets = LottoTickets.create(purchaseAmount, manualLottos);

assertThat(tickets.size()).isEqualTo(2);
assertThat(tickets.getManualCount()).isEqualTo(2);
assertThat(tickets.getAutoCount()).isEqualTo(0);
}

@Test
void 수동_자동_혼합_구매() {
PurchaseAmount purchaseAmount = new PurchaseAmount(5000);
Lotto manual1 = new Lotto(1, 2, 3, 4, 5, 6);
Lotto manual2 = new Lotto(7, 8, 9, 10, 11, 12);
ManualLottos manualLottos = new ManualLottos(Arrays.asList(manual1, manual2));

LottoTickets tickets = LottoTickets.create(purchaseAmount, manualLottos);

assertThat(tickets.size()).isEqualTo(5);
assertThat(tickets.getManualCount()).isEqualTo(2);
assertThat(tickets.getAutoCount()).isEqualTo(3);
}

@Test
void 수동로또_개수가_구입가능_개수보다_많으면_예외를던진() {
PurchaseAmount purchaseAmount = new PurchaseAmount(2000);
Lotto manual1 = new Lotto(1, 2, 3, 4, 5, 6);
Lotto manual2 = new Lotto(7, 8, 9, 10, 11, 12);
Lotto manual3 = new Lotto(13, 14, 15, 16, 17, 18);
ManualLottos manualLottos = new ManualLottos(Arrays.asList(manual1, manual2, manual3));

assertThatThrownBy(() -> LottoTickets.create(purchaseAmount, manualLottos))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("수동로또 개수가 구입 가능한 개수보다 많습니다.");
}
}
Loading