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

[로또 미션] 이지은 미션 제출합니다. #13

Open
wants to merge 12 commits into
base: jelee2555
Choose a base branch
from
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Lotto

### step1

- 크기가 6인 int List : numbers
- [x] 1 ~ 45 사이의 숫자
- [x] 중복되지 않아야 함
- [x] 크기가 6이어야 함

- [x] 로또 금액을 입력 받아야 함
- [x] 1000원 단위로 입력
- [x] 최소 1000원 이상
- [x] 로또 번호는 랜덤으로 설정
- [x] 숫자들끼리 중복되지 않도록
- [x] 1 ~ 45 사이
- [x] 출력 시 숫자들이 오름차순으로 정렬

Choose a reason for hiding this comment

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

저도 리드미를 작성하면서 체크 리스트를 만드는데 지은님도 저와 같은 방식을 사용하시네요! 점점 미션에서 요구하는 사항이 많아지다 보니 하나하나 체크하는 습관을 들이는게 좋은 것 같습니다👍

### step2

<프로그래밍 요구사항>

- [ ] 원시값 포장
- [ ] 문자열 포장
- [ ] 일급 컬렉션 사용

<구현 해야하는 기능>

- [x] 당첨 번호 입력
- [x] 문자열의 형태로 입력
- [ ] 당첨 통계 출력
- [ ] 당첨 금액 & 개수 별 출력
- [ ] 총 수익률 계산
26 changes: 26 additions & 0 deletions src/main/java/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class InputView {

public static final Scanner SCANNER = new Scanner(System.in);

Choose a reason for hiding this comment

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

저는 View에서 구현한 모든 메서드마다 Scanner를 생성해서 사용하였는데요! 지은님처럼 static 객체를 생성하면 불필요한 객체 생성을 피할 수 있을 것 같네요! 좋은 내용 배우고 갑니다 👍


public int inputLottoPrice() {
System.out.println("구입금액을 입력해 주세요.");
return SCANNER.nextInt();
}

public Lotto inputLottoNumbers() {
System.out.println("지난 주 당첨 번호를 입력해 주세요.");
return stringToLotto(SCANNER.nextLine());
}

public Lotto stringToLotto(String string) {
List<Integer> numbers = Arrays.stream(string.split(",")).map(Integer::parseInt).toList();
List<LottoNumber> lottoNumbers = numbers.stream()
.map(LottoNumber::new)
.toList();
return new Lotto(lottoNumbers);
}

Choose a reason for hiding this comment

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

현재 이 코드는 입력받은 문자열(숫자와 콤마로 이루어진 입력 값)을 Lotto라는 객체를 생성하여 반환하는 코드네요.
스트림을 사용하여 코드를 간소화하고, 가독성이 좋아진 것 같습니다! List<Integer>를 생성하는 코드와 List<LottoNumber>를 생성하는 코드를 각각 메서드로 분리시키면 메서드의 역할을 좀 더 구분하기에 좋을 것 같아요!

}
41 changes: 41 additions & 0 deletions src/main/java/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import java.util.Collections;
import java.util.List;

public class Lotto {

public static final int LOTTO_SIZE = 6;

Choose a reason for hiding this comment

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

LOTTO_SIZE는 List<LottoNumber>를 생성하는 과정에서 검증할 때, 사용이 되는 것 같아요.
이 값이 외부에서 활용이 되지 않는거라면 private으로 선언하는건 어떨까요? 혹시 이 값이 외부에서도 쓰인다면 아예 별도의 클래스로 상수 값을 분리시키는 것도 생각해볼 수 있을 것 같습니다.


private final List<LottoNumber> numbers;

public Lotto(List<LottoNumber> numbers) {
validate(numbers);
this.numbers = sortNumbers(numbers);
}

private void validate(List<LottoNumber> numbers) {
validateSize(numbers);
validateDuplication(numbers);
}

private void validateSize(List<LottoNumber> numbers) {
if (numbers.size() != LOTTO_SIZE) {
throw new IllegalArgumentException("로또는 6개의 수로 이루어져야 한다. ");
}
}

private void validateDuplication(List<LottoNumber> numbers) {
long duplicatedSize = numbers.stream().distinct().count();
if (numbers.size() != duplicatedSize) {
throw new IllegalArgumentException("로또 숫자는 중복이 없어야 한다. ");
}
}

private List<LottoNumber> sortNumbers(List<LottoNumber> numbers) {
Collections.sort(numbers);
return numbers;
}

public List<LottoNumber> numbers() {
return List.copyOf(numbers);
}
}
32 changes: 32 additions & 0 deletions src/main/java/LottoGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import java.util.ArrayList;
import java.util.List;

public class LottoGame {

private final InputView inputView;
private final OutputView outputView;

public LottoGame(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
int inputPrice = inputView.inputLottoPrice();
LottoPrice price = new LottoPrice(inputPrice);
List<Lotto> lottos = getLottos(price);
outputView.printLotto(lottos);
}

private List<Lotto> getLottos(LottoPrice price) {
List<Lotto> lottos = new ArrayList<>();

RandomLottoGenerator randomLottoGenerator = new RandomLottoGenerator();

for (int i = 0; i < price.getPrice() / 1000; i++) {

Choose a reason for hiding this comment

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

for문에 사용된 price.getPriceE() / 1000은 사용자가 입력한 금액에 따라 발행해야 할 로또 개수를 의미하는 것 같아요.
이 값을 별도의 변수로 빼내어 이름을 붙여준다면 이 코드를 처음 보는 사람도 쉽게 이해가 갈 것 같아요!

lottos.add(new Lotto(randomLottoGenerator.generateLotto().numbers()));
}

return lottos;
}
}
23 changes: 23 additions & 0 deletions src/main/java/LottoNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public class LottoNumber implements Comparable<LottoNumber> {

public static final int MIN_VALUE = 0;
public static final int MAX_VALUE = 45;

private final int number;

public LottoNumber(int number) {
validateRange(number);
this.number = number;
}

private void validateRange(int number) {
if (number < MIN_VALUE || number > MAX_VALUE) {
throw new IllegalArgumentException();

Choose a reason for hiding this comment

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

IllegalArgumentException을 던질 때, 해당 예외와 관련된 메시지를 같이 전달해보는건 어떨까요? 추후 LottoNumber라는 객체를 생성하는 과정에서 해당 메서드에서 예외를 발생했을 때, 예외 메시지가 출력된다면 코드를 수정하기 더 수월할 것 같아요!

}
}

@Override
public int compareTo(LottoNumber o) {
return Integer.compare(this.number, o.number);
}
}
34 changes: 34 additions & 0 deletions src/main/java/LottoPrice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
public class LottoPrice {

public static final int MIN_PRICE = 0;

Choose a reason for hiding this comment

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

LottoPrice 내부에서 쓰이는 상수라면 private으로 선언해보는건 어떨까요?

public static final int PRICE_UNIT = 1_000;

private final int price;

public LottoPrice(int price) {
validate(price);
this.price = price;
}

private void validate(int price) {
validateRange(price);
validateUnit(price);
}


private void validateRange(int price) {
if (price < MIN_PRICE) {
throw new IllegalArgumentException("로또 금액은 0이상 이어야 한다.");
}
}

private void validateUnit(int price) {
if (price % PRICE_UNIT != 0) {
throw new IllegalArgumentException("로또 금액은 1000원 단위여야 한다.");
}
}

public int getPrice() {
return price;
}
}
11 changes: 11 additions & 0 deletions src/main/java/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import java.util.List;

public class OutputView {

public void printLotto(List<Lotto> lottos) {
System.out.println();
System.out.println(lottos.size() + "개를 구매했습니다.");
lottos.stream().map(Lotto::numbers).forEach(System.out::println);

Choose a reason for hiding this comment

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

스트림 문법을 적극적으로 활용하고 계시네요 👍

}

}
23 changes: 23 additions & 0 deletions src/main/java/RandomLottoGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class RandomLottoGenerator {


public static final Random RANDOM = new Random();

public Lotto generateLotto() {
Set<Integer> numbers = new HashSet<>();
while (numbers.size() < 6) {
int randomNumber = RANDOM.nextInt(1, 45);
numbers.add(randomNumber);
}

List<LottoNumber> lottoNumbers = numbers.stream()
.map(LottoNumber::new)
.toList();
return new Lotto(lottoNumbers);
}
}
19 changes: 19 additions & 0 deletions src/test/java/LottoPriceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

public class LottoPriceTest {
@Test
void 로또_금액은_0이상_이어야_한다() {
int price = -1000;

assertThatThrownBy(() -> new LottoPrice(price)).isInstanceOf(IllegalArgumentException.class).hasMessage("로또 금액은 0이상 이어야 한다.");
}

@Test
void 로또_금액은_1000원_단위여야_한다() {
int price = 9999;

assertThatThrownBy(() -> new LottoPrice(price)).isInstanceOf(IllegalArgumentException.class).hasMessage("로또 금액은 1000원 단위여야 한다.");
}
}
36 changes: 36 additions & 0 deletions src/test/java/LottoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import org.junit.jupiter.api.Test;

import java.util.List;

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

public class LottoTest {
@Test
void 로또는_6개의_수로_이루어져야_한다() {
var numbers = List.of(new LottoNumber(1), new LottoNumber(2), new LottoNumber(3));

assertThatThrownBy(() -> new Lotto(numbers)).isInstanceOf(IllegalArgumentException.class).hasMessage("로또는 6개의 수로 이루어져야 한다. ");

Choose a reason for hiding this comment

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

메서드 체이닝으로 인해 .의 사용이 많아질 경우, 줄바꿈을 통해 코드의 가독성을 높일 수 있을 것 같아요!

assertThatThrownBy(() -> new Lotto(numbers))
                       .isInstanceOf(IllegalArgumentException.class)
                       .hasMessage("로또는 6개의 수로 이루어져야 한다. ");

}

// fail
@Test
void 로또_숫자는_중복이_없어야_한다() {
var numbers = List.of(new LottoNumber(1), new LottoNumber(1), new LottoNumber(2), new LottoNumber(3), new LottoNumber(4), new LottoNumber(5));

assertThatThrownBy(() -> new Lotto(numbers)).isInstanceOf(IllegalArgumentException.class).hasMessage("로또 숫자는 중복이 없어야 한다. ");
}

// fail
@Test
void 로또_숫자는_오름차순_정렬되어있다() {
var unsortedNumbers = List.of(new LottoNumber(6), new LottoNumber(5), new LottoNumber(4), new LottoNumber(3), new LottoNumber(2), new LottoNumber(1));
// var sortedNumbers = List.of(new LottoNumber(1), new LottoNumber(2), new LottoNumber(3), new LottoNumber(4), new LottoNumber(5), new LottoNumber(6));

var unsortedLotto = new Lotto(unsortedNumbers);
// var sortedLotto = new Lotto(sortedNumbers);

//assertThat(unsortedLotto.numbers().equals(sortedLotto.numbers()));
assertThat(unsortedLotto.numbers()).isSorted();
}
}
15 changes: 15 additions & 0 deletions src/test/java/RandomLottoGeneratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.RepeatedTest;

import java.util.Random;

class RandomLottoGeneratorTest {

@RepeatedTest(1000)
void test() {
int result = new Random().nextInt(1, 45);

Assertions.assertThat(result).isBetween(1, 45);
}

}