-
Notifications
You must be signed in to change notification settings - Fork 5
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
[로또] 신승철 미션 제출합니다. #5
base: jjanggu
Are you sure you want to change the base?
Changes from all commits
88b578e
97b34d2
a124c3e
62bbfc9
9add12f
98e0311
0581a33
f0ed79a
d82864a
efc5b51
faec033
0b50826
acaff47
8da1b9d
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,65 @@ | ||
package lotto; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Scanner; | ||
import java.util.stream.Collectors; | ||
|
||
public class InputView { | ||
|
||
private static final Scanner SCANNER = new Scanner(System.in); | ||
|
||
public static int inputMoney() { | ||
try { | ||
System.out.println("구매금액을 입력해 주세요."); | ||
return Integer.parseInt(SCANNER.nextLine()); | ||
} catch (NumberFormatException e) { | ||
throw new NumberFormatException("구매금액은 숫자로 입력해 주세요."); | ||
} | ||
} | ||
|
||
public static int inputManualCount() { | ||
try { | ||
System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); | ||
return Integer.parseInt(SCANNER.nextLine()); | ||
} catch (NumberFormatException e) { | ||
throw new NumberFormatException("구매금액은 숫자로 입력해 주세요."); | ||
} | ||
} | ||
|
||
public static List<Lotto> inputManualLottoNumbers(final int counts) { | ||
System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); | ||
List<Lotto> manualLottos = new ArrayList<>(); | ||
for (int i = 0; i < counts; i++) { | ||
manualLottos.add(Lotto.from(inputLottoNumber())); | ||
} | ||
return manualLottos; | ||
} | ||
|
||
private static List<LottoNumber> inputLottoNumber() { | ||
String[] input = SCANNER.nextLine().split(","); | ||
return Arrays.stream(input) | ||
.map(String::trim) | ||
.map(Integer::parseInt) | ||
.map(LottoNumber::from) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public static List<LottoNumber> inputWinningLottoNumbers() { | ||
System.out.println("지난 주 당첨 번호를 입력해 주세요."); | ||
return Arrays.stream(SCANNER.nextLine().split(",")) | ||
.map(Integer::parseInt) | ||
.map(LottoNumber::from) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public static int inputWinningBonus() { | ||
try { | ||
System.out.println("보너스 볼을 입력해 주세요."); | ||
return Integer.parseInt(SCANNER.nextLine()); | ||
} catch (NumberFormatException e) { | ||
throw new NumberFormatException("구매금액은 숫자로 입력해 주세요."); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package lotto; | ||
|
||
import java.util.List; | ||
|
||
public class Lotto { | ||
|
||
private final List<LottoNumber> lottoNumbers; | ||
|
||
private Lotto(final List<LottoNumber> lottoNumbers) { | ||
this.lottoNumbers = lottoNumbers; | ||
} | ||
|
||
public static Lotto from(final List<LottoNumber> lottoNumbers) { | ||
LottoValidator.validate(lottoNumbers); | ||
return new Lotto(lottoNumbers); | ||
} | ||
|
||
public boolean isContains(final LottoNumber lottoNumber) { | ||
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. 짱구님의 의견이 궁금합니다. contains나 matches가 들어가는 함수명은 is를 안붙여도 boolean을 리턴한다는게 읽혀질 것 같아서요. 어떻게 생각하시나요? |
||
return lottoNumbers.contains(lottoNumber); | ||
} | ||
|
||
public int calculateHitCount(final Lotto lotto) { | ||
return (int) lottoNumbers.stream() | ||
.filter(lotto::isContains) | ||
.count(); | ||
} | ||
|
||
public List<LottoNumber> lottoNumbers() { | ||
return this.lottoNumbers; | ||
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. 다른 곳 처럼 원본 객체 말고 복사본을 리턴해주세용 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package lotto; | ||
|
||
public class LottoApplication { | ||
|
||
public static void main(String[] args) { | ||
final LottoController lottoController = new LottoController(); | ||
lottoController.run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package lotto; | ||
|
||
public class LottoController { | ||
|
||
public void run() { | ||
try { | ||
final Money money = Money.from(InputView.inputMoney()); | ||
final LottoVendor lottoVendor = LottoVendor.from(money); | ||
|
||
final int manualCount = InputView.inputManualCount(); | ||
final Lottos manualLottos = lottoVendor.purchaseManualLottos(InputView.inputManualLottoNumbers(manualCount)); | ||
final Lottos autoLottos = lottoVendor.purchaseAutoLottos(); | ||
OutputView.printLottoPurchaseResult(manualLottos, autoLottos); | ||
|
||
final Lotto lotto = Lotto.from(InputView.inputWinningLottoNumbers()); | ||
final LottoNumber bonusLottoNumber = LottoNumber.from(InputView.inputWinningBonus()); | ||
final WinningLotto winningLotto = WinningLotto.of(lotto, bonusLottoNumber); | ||
|
||
final LottoResult lottoResult = LottoResult.of(manualLottos, autoLottos, winningLotto); | ||
OutputView.printLottoResult(lottoResult); | ||
} catch (IllegalArgumentException e) { | ||
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. 예외 처리를 프록시 패턴을 사용해서 구현하면 |
||
OutputView.printExceptionMessage(e); | ||
run(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package lotto; | ||
|
||
import java.util.Objects; | ||
|
||
public class LottoNumber { | ||
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. (개인적인 의견입니다.) 그렇게 하면 각 객체에 맞는 책임을 나눠줄 수 있을 것 같아요 |
||
|
||
public static final int MIN_LOTTO_NUMBER = 1; | ||
public static final int MAX_LOTTO_NUMBER = 45; | ||
|
||
private final int value; | ||
|
||
private LottoNumber(final int value) { | ||
this.value = value; | ||
} | ||
|
||
public static LottoNumber from(final int value) { | ||
validateNumberRange(value); | ||
return new LottoNumber(value); | ||
} | ||
|
||
private static void validateNumberRange(final int value) { | ||
if (value < MIN_LOTTO_NUMBER || value > MAX_LOTTO_NUMBER) { | ||
throw new IllegalArgumentException("로또 번호는 1 ~ 45 사이의 숫자여야 합니다."); | ||
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. 로또 번호가 1 ~ 45가 아닌 다른 범위로 바뀔때 여러 부분을 변경해야할 수 있을 것 같아요! |
||
} | ||
} | ||
|
||
public int value() { | ||
return this.value; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
final LottoNumber that = (LottoNumber) o; | ||
return value == that.value; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(value); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package lotto; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
public class LottoNumberAutoGenerator implements LottoNumberGenerator { | ||
|
||
private static final List<Integer> lottoNumbers; | ||
|
||
static { | ||
lottoNumbers = IntStream.rangeClosed(LottoNumber.MIN_LOTTO_NUMBER, LottoNumber.MAX_LOTTO_NUMBER) | ||
.boxed() | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
@Override | ||
public List<LottoNumber> generate() { | ||
Collections.shuffle(lottoNumbers); | ||
return lottoNumbers.stream() | ||
.limit(6) | ||
.sorted() | ||
.map(LottoNumber::from) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package lotto; | ||
|
||
import java.util.List; | ||
|
||
public interface LottoNumberGenerator { | ||
|
||
List<LottoNumber> generate(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package lotto; | ||
|
||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; | ||
|
||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
public class LottoNumberTest { | ||
|
||
@ValueSource(ints = {0, 46}) | ||
@ParameterizedTest | ||
void 로또_번호는_1부터_45_사이의_숫자여야_한다(final int invalidLottoNumber) { | ||
assertThatThrownBy(() -> LottoNumber.from(invalidLottoNumber)) | ||
.isInstanceOf(IllegalArgumentException.class) | ||
.hasMessage("로또 번호는 1 ~ 45 사이의 숫자여야 합니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package lotto; | ||
|
||
import java.util.Arrays; | ||
|
||
public enum LottoRank { | ||
|
||
FIRST(6, 2000000000), | ||
SECOND(5, 1500000), | ||
THIRD(5, 50000), | ||
FOURTH(4, 5000), | ||
FIFTH(3, 0), | ||
UNRANKED(0, 0) | ||
; | ||
|
||
private final int hitCount; | ||
private final long reward; | ||
|
||
LottoRank(final int hitCount, final long reward) { | ||
this.hitCount = hitCount; | ||
this.reward = reward; | ||
} | ||
|
||
public static LottoRank of(final int hitCount, final boolean hasBonusNumber) { | ||
return Arrays.stream(LottoRank.values()) | ||
.filter(lottoRank -> lottoRank.isSameHitCount(hitCount)) | ||
.filter(lottoRank -> !lottoRank.equals(SECOND) || hasBonusNumber) | ||
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. 보너스 넘버를 가져야 하는 rank가 추가될때마다 조건이 계속 추가 될 수 있을 것 같아요 |
||
.findFirst() | ||
.orElse(UNRANKED); | ||
} | ||
|
||
private boolean isSameHitCount(final int hitCount) { | ||
return this.hitCount == hitCount; | ||
} | ||
|
||
public boolean hasReward() { | ||
return this.reward != 0; | ||
} | ||
|
||
public int hitCount() { | ||
return this.hitCount; | ||
} | ||
|
||
public long reward() { | ||
return this.reward; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package lotto; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
|
||
public class LottoRankTest { | ||
|
||
@CsvSource(value = { | ||
"6, false, FIRST", | ||
"5, true, SECOND", | ||
"5, false, THIRD", | ||
"4, true, FOURTH", | ||
"4, false, FOURTH", | ||
"3, true, FIFTH", | ||
"3, false, FIFTH", | ||
"2, true, UNRANKED", | ||
"2, false, UNRANKED", | ||
"1, true, UNRANKED", | ||
"1, false, UNRANKED", | ||
"0, false, UNRANKED" | ||
}) | ||
@ParameterizedTest | ||
void 로또_번호_적중_횟수와_보너스_번호_유무에_따른_등수를_반환한다(final int hitCount, final boolean hasBonusNumber, final LottoRank expected) { | ||
final LottoRank actual = LottoRank.of(hitCount, hasBonusNumber); | ||
|
||
assertThat(actual).isEqualTo(expected); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package lotto; | ||
|
||
import static java.util.stream.Collectors.groupingBy; | ||
import static java.util.stream.Collectors.summingInt; | ||
|
||
import java.util.EnumMap; | ||
import java.util.Map; | ||
|
||
public class LottoResult { | ||
|
||
private final Map<LottoRank, Integer> rankResults; | ||
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. 저는 코드읽으면서 이 Map의 Integer값이 무엇을 의미하는지 OutputView를 가서야 이해가 됐어요. |
||
|
||
private LottoResult(final Map<LottoRank, Integer> rankResults) { | ||
this.rankResults = rankResults; | ||
} | ||
|
||
public static LottoResult of(final Lottos manualLottos, final Lottos autoLottos, final WinningLotto winningLotto) { | ||
final Map<LottoRank, Integer> manualLottoResults = groupBy(manualLottos, winningLotto); | ||
final Map<LottoRank, Integer> autoLottoResults = groupBy(autoLottos, winningLotto); | ||
final Map<LottoRank, Integer> results = mergeResults(manualLottoResults, autoLottoResults); | ||
|
||
return new LottoResult(results); | ||
} | ||
|
||
private static Map<LottoRank, Integer> groupBy(final Lottos autoLottos, final WinningLotto winningLotto) { | ||
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. 변수명 autoLottos 가 아닌 다른 걸 사용하는게 적절해보입니다! |
||
return autoLottos.lottos().stream() | ||
.collect(groupingBy(winningLotto::calculateRank, summingInt(value -> 1))); | ||
} | ||
|
||
private static Map<LottoRank, Integer> mergeResults(Map<LottoRank, Integer> manualResults, Map<LottoRank, Integer> autoResults) { | ||
Map<LottoRank, Integer> combinedResults = new EnumMap<>(LottoRank.class); | ||
|
||
for (LottoRank rank : LottoRank.values()) { | ||
combinedResults.put(rank, manualResults.getOrDefault(rank, 0) + autoResults.getOrDefault(rank, 0)); | ||
} | ||
|
||
return combinedResults; | ||
} | ||
|
||
public int CountBy(final LottoRank lottoRank) { | ||
return this.rankResults.getOrDefault(lottoRank, 0); | ||
} | ||
|
||
public double calculateProfitRate() { | ||
final long totalReward = calculateTotalReward(); | ||
final int purchaseAmount = calculatePurchaseAmount(); | ||
|
||
return totalReward / (double) purchaseAmount; | ||
} | ||
|
||
private long calculateTotalReward() { | ||
return this.rankResults.entrySet().stream() | ||
.map(rankResult -> rankResult.getKey().reward() * rankResult.getValue()) | ||
.reduce(0L, Long::sum); | ||
} | ||
|
||
private int calculatePurchaseAmount() { | ||
final int numberOfPurchases = this.rankResults.values().stream() | ||
.mapToInt(i -> i) | ||
.sum(); | ||
return numberOfPurchases * LottoVendor.LOTTO_PRICE; | ||
} | ||
} |
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.
짱구님 안녕하세요!
혹시 모든 inputView 메소드를 static으로 만드셨는데 어떤 의도이신지 궁금합니다.
또 만약 console이 아니라 다른 방식으로 입력을 받게 된다면 어떻게 구현하실 생각이신가요??