-
Notifications
You must be signed in to change notification settings - Fork 92
[로또 - 1단계] 안석환 미션 제출합니다 #31
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
Changes from all commits
33d6a24
cbc0be4
be6f186
b9d46d4
d88bd87
f5133af
6a0dcd2
957b5af
fdffc5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
## 로또 미션 | ||
### 1단계 | ||
- [x] 로또 구입 | ||
- [x] 숫자 입력이 들어와야 한다. | ||
- [x] 로또 구입 금액은 음수가 될 수 없다. | ||
- [x] 로또 구입 금액은 1000단위이어야 한다. | ||
- [x] 로또 생성 | ||
- [x] 로또는 6자리로 구성되어 있다. | ||
- [x] 하나의 로또 내 숫자는 중복될 수 없다. | ||
- [x] 로또 숫자는 1 ~ 45 사이의 숫자로만 구상되어야 한다. | ||
- [x] 화면 출력 | ||
- [x] 한 로또 내 숫자는 오름차순으로 출력한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import model.LottoGenerator; | ||
import model.Lottos; | ||
import model.LottoPurchaseMoney; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
final LottoPurchaseMoney lottoPurchaseMoney = InputView.inputMoney(); | ||
final LottoGenerator lottoGenerator = new LottoGenerator(); | ||
final Lottos lottos = lottoGenerator.generateRandomLotto(lottoPurchaseMoney); | ||
OutputView.showLotto(lottos); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package model; | ||
|
||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class Lotto { | ||
|
||
private static final int LOTTO_NUMBER_SIZE = 6; | ||
private static final int LOTTO_MIN_NUMBER = 1; | ||
private static final int LOTTO_MAX_NUMBER = 45; | ||
|
||
private final List<Integer> numbers; | ||
|
||
public Lotto(final List<Integer> numbers) { | ||
validateNumbers(numbers); | ||
this.numbers = numbers; | ||
} | ||
|
||
private void validateNumbers(final List<Integer> numbers) { | ||
validateNumbersSize(numbers); | ||
validateDuplicateNumbers(numbers); | ||
validateNumber1to45(numbers); | ||
} | ||
|
||
private void validateNumbersSize(final List<Integer> numbers) { | ||
if (numbers.size() != LOTTO_NUMBER_SIZE) { | ||
throw new IllegalArgumentException("로또는 6개의 숫자로 구성되어야 합니다."); | ||
} | ||
} | ||
|
||
private void validateDuplicateNumbers(final List<Integer> numbers) { | ||
Set<Integer> notDuplicatedNumbers = new HashSet<>(numbers); | ||
if (notDuplicatedNumbers.size() != numbers.size()) { | ||
throw new IllegalArgumentException("로또 내 동일한 숫자가 있으면 안됩니다."); | ||
} | ||
} | ||
|
||
private void validateNumber1to45(final List<Integer> numbers) { | ||
for (Integer number : numbers) { | ||
if (number < LOTTO_MIN_NUMBER || number > LOTTO_MAX_NUMBER) { | ||
throw new IllegalArgumentException("로또 번호는 1과 45사이의 숫자이어야 합니다."); | ||
} | ||
} | ||
} | ||
|
||
public List<Integer> getNumbers() { | ||
return numbers; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package model; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class LottoGenerator { | ||
|
||
private static final List<Integer> NUMBERS = Arrays.asList( | ||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, | ||
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, | ||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, | ||
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | ||
41, 42, 43, 44, 45); | ||
|
||
public Lottos generateRandomLotto(final LottoPurchaseMoney lottoPurchaseMoney) { | ||
final List<Lotto> lottos = new ArrayList<>(); | ||
for (int i = 0; i < lottoPurchaseMoney.getPurchaseQuantity(); i++) { | ||
Collections.shuffle(NUMBERS); | ||
lottos.add(new Lotto(new ArrayList<>(NUMBERS.subList(0, 6)))); | ||
} | ||
return new Lottos(lottos); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package model; | ||
|
||
public class LottoPurchaseMoney { | ||
|
||
private static final int LOTTO_EXPENSE = 1000; | ||
|
||
private final int value; | ||
|
||
public LottoPurchaseMoney(int value) { | ||
validateValue(value); | ||
this.value = value; | ||
} | ||
|
||
private void validateValue(final int value) { | ||
if (value < 0) { | ||
throw new IllegalArgumentException("구입금액은 음수가 될 수 없습니다."); | ||
} | ||
|
||
if (value % LOTTO_EXPENSE != 0) { | ||
throw new IllegalArgumentException("구입금액은 1000단위이어야 합니다."); | ||
} | ||
} | ||
|
||
public int getPurchaseQuantity() { | ||
return value / LOTTO_EXPENSE; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package model; | ||
|
||
import java.util.List; | ||
|
||
public class Lottos { | ||
|
||
private final List<Lotto> lottos; | ||
|
||
public Lottos(List<Lotto> lottos) { | ||
this.lottos = lottos; | ||
} | ||
|
||
public int getBuyLottoCount() { | ||
return lottos.size(); | ||
} | ||
|
||
public List<Lotto> getLottos() { | ||
return lottos; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package view; | ||
|
||
import model.LottoPurchaseMoney; | ||
|
||
import java.util.Scanner; | ||
|
||
public class InputView { | ||
|
||
public static LottoPurchaseMoney inputMoney() { | ||
Scanner sc = new Scanner(System.in); | ||
System.out.println("구입금액을 입력해 주세요"); | ||
return new LottoPurchaseMoney(sc.nextInt()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package view; | ||
|
||
import model.Lotto; | ||
import model.Lottos; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.StringJoiner; | ||
|
||
public class OutputView { | ||
|
||
private static final String DELIMITER = "\n"; | ||
|
||
public static void showLotto(final Lottos lottos) { | ||
System.out.printf("%d개를 구매했습니다.%n", lottos.getBuyLottoCount()); | ||
|
||
StringJoiner stringJoiner = new StringJoiner(DELIMITER); | ||
|
||
for (Lotto lotto : lottos.getLottos()) { | ||
final List<Integer> numbers = lotto.getNumbers(); | ||
Collections.sort(numbers); | ||
stringJoiner.add(numbers.toString()); | ||
} | ||
|
||
System.out.println(stringJoiner); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package model; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
class LottoPurchaseMoneyTest { | ||
|
||
@DisplayName("구매금액이 음수이면 예외를 발생한다.") | ||
@Test | ||
void money_not_under_zero() { | ||
// given | ||
int value = -1; | ||
// when | ||
// then | ||
assertThatThrownBy(() -> new LottoPurchaseMoney(value)) | ||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessage("구입금액은 음수가 될 수 없습니다."); | ||
} | ||
|
||
@DisplayName("구매금액이 1000단위가 아니면 예외를 발생한다.") | ||
@ValueSource(ints = {1100, 1010, 1001}) | ||
@ParameterizedTest | ||
void money_divide_with_1000(int value) { | ||
// given | ||
// when | ||
// then | ||
assertThatThrownBy(() -> new LottoPurchaseMoney(value)) | ||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessage("구입금액은 1000단위이어야 합니다."); | ||
} | ||
|
||
@DisplayName("로또 구매 개수 구한다.") | ||
@Test | ||
void get_lotto_count() { | ||
// given | ||
final int pay = 10000; | ||
final LottoPurchaseMoney lottoPurchaseMoney = new LottoPurchaseMoney(pay); | ||
|
||
// when | ||
final int result = lottoPurchaseMoney.getPurchaseQuantity(); | ||
|
||
// then | ||
assertThat(result).isEqualTo(10); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package model; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
import java.util.List; | ||
import java.util.stream.Stream; | ||
|
||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
class LottoTest { | ||
|
||
@DisplayName("로또의 숫자가 6개가 아니면 예외를 발생한다.") | ||
@MethodSource("lottoWithWrongSize") | ||
@ParameterizedTest | ||
void lotto_size_6(List<Integer> numbers) { | ||
// given | ||
// when | ||
// then | ||
assertThatThrownBy(() -> new Lotto(numbers)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트코드 관련해서 궁금한 점이 있습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 제가 작성한 검증 로직에 대해서는 모두 테스트를 작성하는 편입니다. 그 이유는 먼저 제가 작성한 코드가 모두 잘 동작하는지 파악하는데 큰 도움이 되는 것이 첫번째 이유이고 두번째는 이유로는 테스트 코드가 다른 개발자가 볼 때 그 사람이 구현한 기능을 파악하는데 도움이 된다고 생각했습니다. 두번째 이유는 제가 개인적으로 겪은 경험인데 다른 분의 코드를 리뷰할 때 테스트 코드를 바탕으로 구현한 로직을 보면 그 사람의 의도와 목적을 더 쉽게 파악하는데 도움이 되었던 것 같습니다! (또한 구현한 로직이 명확하게 파악되었습니다.) |
||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessage("로또는 6개의 숫자로 구성되어야 합니다."); | ||
} | ||
|
||
private static Stream<Arguments> lottoWithWrongSize() { | ||
return Stream.of( | ||
Arguments.arguments(List.of(1, 2, 3, 4, 5)), | ||
Arguments.of(List.of(1, 2, 3, 4, 5, 6, 7))); | ||
} | ||
|
||
@DisplayName("로또 내 숫자는 중복되면 예외를 발생한다.") | ||
@MethodSource("lottoWithDuplicatedNumber") | ||
@ParameterizedTest | ||
void lotto_with_duplicated_number(List<Integer> numbers) { | ||
// given | ||
// when | ||
// then | ||
assertThatThrownBy(() -> new Lotto(numbers)) | ||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessage("로또 내 동일한 숫자가 있으면 안됩니다."); | ||
} | ||
|
||
private static Stream<Arguments> lottoWithDuplicatedNumber() { | ||
return Stream.of( | ||
Arguments.arguments(List.of(1, 2, 3, 4, 5, 5)), | ||
Arguments.of(List.of(1, 1, 3, 4, 5, 6))); | ||
} | ||
|
||
@DisplayName("로또 내 숫자가 1과 45사이의 숫자가 아니면 예외를 발생한다.") | ||
@MethodSource("lottoWithWrongNumber") | ||
@ParameterizedTest | ||
void lotto_with_not_1_to_45(List<Integer> numbers) { | ||
// given | ||
// when | ||
// then | ||
assertThatThrownBy(() -> new Lotto(numbers)) | ||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessage("로또 번호는 1과 45사이의 숫자이어야 합니다."); | ||
} | ||
|
||
private static Stream<Arguments> lottoWithWrongNumber() { | ||
return Stream.of( | ||
Arguments.arguments(List.of(1, 2, 3, 4, 5, 46)), | ||
Arguments.of(List.of(0, 1, 3, 4, 5, 6))); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Money 객체에 단 리뷰와 마찬가지로 저는 이렇게 되면 "lottos가 관리하는 구매한 로또 개수", "money가 관리하는 구매한 로또 개수"가 같은 의미인데 관리 지점이 두 개가 생기게 되어 문제가 되지 않을까..! 라고 생각했었습니다!
근데 지금 코드를 쭉 읽어보니, 결국 money의 로또 개수를 기반으로 lottos를 만드는 것이기 때문에 로직 상으로는 별 문제가 없을 것 같긴 한데........ 여튼 저는 저렇게 생각했었는데 석환님의 생각은 어떠신지 궁금합니다!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
관리 지점이 두개라고 보기보다는 구매한 로또 개수를 파악할 수 있는 출처가 Lottos와 Money라고 보면 좋을 것 같습니다. (Money는 이제 LottoPurchaseMoney로 변경될 예정입니다.) 그 이유는 민주님이 리뷰달아주신 것처럼 Lottos를 통해서 얻은 로또 개수와 Money를 통해서 얻는 로또 개수가 다르지 않기 때문입니다.