-
Notifications
You must be signed in to change notification settings - Fork 0
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
[로또] 박성우 제출합니다. #1
base: main
Are you sure you want to change the base?
Changes from 15 commits
a199472
7735f66
02d4d32
59107a8
bdeee94
f0f1e45
ebb73f7
f18032d
91f6c78
a308695
8307281
1610c8d
bea1554
1ab7d50
373e46f
4cf6226
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 |
---|---|---|
@@ -1,7 +1,55 @@ | ||
package lotto; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
import lotto.domain.Lotto; | ||
import lotto.domain.Money; | ||
import lotto.domain.WinNumbers; | ||
import lotto.domain.lottonumbercreator.AutoLottoNumberCreator; | ||
import lotto.domain.lottonumbercreator.LottoNumberCreator; | ||
import lotto.domain.lottorank.LottoRank; | ||
import lotto.domain.lottorank.LottoRanks; | ||
import lotto.view.InputView; | ||
|
||
public class Application { | ||
private static final LottoNumberCreator CREATOR = new AutoLottoNumberCreator(); | ||
|
||
public static void main(String[] args) { | ||
// TODO: 프로그램 구현 | ||
System.out.println("구입금액을 입력해 주세요."); | ||
|
||
final Money money = new Money(InputView.requestInteger()); | ||
final int lottoCount = money.getLottoCount(); | ||
System.out.println(String.format("\n%d개를 구매했습니다.", lottoCount)); | ||
|
||
final List<Lotto> lottos = getAutoLottos(lottoCount); | ||
lottos.forEach(System.out::println); | ||
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. List에 forEach를 쓸 수 있군요...!! |
||
|
||
System.out.println("\n당첨 번호를 입력해 주세요."); | ||
final Lotto winLotto = new Lotto(InputView.requestWinNumbers()); | ||
|
||
System.out.println("\n보너스 번호를 입력해 주세요."); | ||
int bonusBall = InputView.requestInteger(); | ||
Comment on lines
+28
to
+32
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. 이 부분도 모두 입력받는 로직 같은데 InputView가 아닌 Application에서 구현하신 이유가 있으실까요? 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. Application의 실행 흐름을 모두 Application class에 작성하였는데.. 딱히 이유는 없습니다 |
||
|
||
final WinNumbers winNumbers = new WinNumbers(winLotto, bonusBall); | ||
final LottoRanks lottoRanks = getLottoRanks(lottos, winNumbers); | ||
|
||
System.out.println("당첨 통계\n---"); | ||
System.out.println(lottoRanks); | ||
System.out.println(String.format("총 수익률은 %.1f%%입니다.", money.getRateOfProfit(lottoRanks.getProfit()))); | ||
} | ||
|
||
private static List<Lotto> getAutoLottos(int lottoCount) { | ||
return Stream.generate(() -> Lotto.from(CREATOR)) | ||
.limit(lottoCount) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private static LottoRanks getLottoRanks(List<Lotto> lottos, WinNumbers winNumbers) { | ||
final List<LottoRank> ranks = lottos.stream() | ||
.map(lotto -> LottoRank.calculateRank(winNumbers, lotto)) | ||
.collect(Collectors.toList()); | ||
|
||
return LottoRanks.from(ranks); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package lotto.domain; | ||
|
||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import lotto.domain.lottonumbercreator.LottoNumberCreator; | ||
|
||
public class Lotto { | ||
private static final int LOTTO_NUMBERS_SIZE = 6; | ||
private static final int MIN_LOTTO_NUMBER = 1; | ||
private static final int MAX_LOTTO_NUMBER = 45; | ||
|
||
private final List<Integer> numbers; | ||
|
||
public Lotto(List<Integer> numbers) { | ||
validateSize(numbers); | ||
validateDuplicated(numbers); | ||
validateNumbersRange(numbers); | ||
this.numbers = numbers; | ||
} | ||
|
||
public static Lotto from(LottoNumberCreator creator) { | ||
return new Lotto(creator.create(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER, LOTTO_NUMBERS_SIZE)); | ||
} | ||
|
||
private void validateSize(List<Integer> numbers) { | ||
if (numbers.size() != LOTTO_NUMBERS_SIZE) { | ||
throw new IllegalArgumentException(String.format("[ERROR] 로또 숫자의 개수는 %d일 수 없습니다.", numbers.size())); | ||
} | ||
} | ||
|
||
private void validateDuplicated(List<Integer> numbers) { | ||
if (numbers.size() != new HashSet<>(numbers).size()) { | ||
throw new IllegalArgumentException("[ERROR] 중복되는 숫자가 존재합니다."); | ||
} | ||
} | ||
|
||
private void validateNumbersRange(List<Integer> numbers) { | ||
if (!numbers.stream().allMatch(this::isInRange)) { | ||
throw new IllegalArgumentException( | ||
String.format("[ERROR] %d ~ %d 범위를 넘어가는 숫자가 존재합니다.", MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER) | ||
); | ||
} | ||
} | ||
|
||
private boolean isInRange(int bonusBall) { | ||
return bonusBall >= MIN_LOTTO_NUMBER && bonusBall <= MAX_LOTTO_NUMBER; | ||
} | ||
|
||
public boolean isPossible(int bonusBall) { | ||
final boolean isInRange = isInRange(bonusBall); | ||
final boolean isNotLottoNumber = !numbers.contains(bonusBall); | ||
|
||
return isInRange && isNotLottoNumber; | ||
} | ||
|
||
public int calculateSameLottoNumber(Lotto other) { | ||
return (int) other.numbers.stream() | ||
.filter(this::contain) | ||
.count(); | ||
} | ||
|
||
public boolean contain(Integer number) { | ||
return this.numbers.contains(number); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format("[%s]", | ||
this.numbers.stream() | ||
.sorted() | ||
.map(number -> Integer.toString(number)) | ||
.collect(Collectors.joining(", "))); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package lotto.domain; | ||
|
||
public class Money { | ||
private static final int MIN_VALUE = 1000; | ||
private final int value; | ||
|
||
public Money(int value) { | ||
validate(value); | ||
this.value = value; | ||
} | ||
|
||
private void validate(int value) { | ||
if (value < MIN_VALUE || value % MIN_VALUE != 0) { | ||
throw new IllegalArgumentException(String.format("[ERROR] 금액은 %d원일 수 없습니다.", value)); | ||
} | ||
} | ||
|
||
public int getLottoCount() { | ||
return this.value / MIN_VALUE; | ||
} | ||
|
||
public double getRateOfProfit(int profit) { | ||
return (double) profit * 100 / value; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package lotto.domain; | ||
|
||
public class WinNumbers { | ||
|
||
private final Lotto lotto; | ||
|
||
private final int bonusBall; | ||
|
||
public WinNumbers(Lotto lotto, int bonusBall) { | ||
validateBonusBall(lotto, bonusBall); | ||
this.lotto = lotto; | ||
this.bonusBall = bonusBall; | ||
} | ||
|
||
private void validateBonusBall(Lotto lotto, int bonusBall) { | ||
if (!lotto.isPossible(bonusBall)) { | ||
throw new IllegalArgumentException(String.format("[ERROR] %d는 보너스 볼이 될 수 없습니다.", bonusBall)); | ||
} | ||
} | ||
Comment on lines
+15
to
+19
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 int calculateSameLottoNumber(Lotto lotto) { | ||
return this.lotto.calculateSameLottoNumber(lotto); | ||
} | ||
|
||
public boolean matchBonus(Lotto lotto) { | ||
return lotto.contain(bonusBall); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package lotto.domain.lottonumbercreator; | ||
|
||
import camp.nextstep.edu.missionutils.Randoms; | ||
import java.util.List; | ||
|
||
public class AutoLottoNumberCreator implements LottoNumberCreator{ | ||
@Override | ||
public List<Integer> create(int minLottoNumber, int maxLottoNumber, int lottoNumbersSize) { | ||
return Randoms.pickUniqueNumbersInRange(minLottoNumber, maxLottoNumber, lottoNumbersSize); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package lotto.domain.lottonumbercreator; | ||
|
||
import java.util.List; | ||
|
||
public interface LottoNumberCreator { | ||
List<Integer> create(int minLottoNumber, int maxLottoNumber, int lottoNumbersSize); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package lotto.domain.lottorank; | ||
|
||
import java.util.Arrays; | ||
import lotto.domain.Lotto; | ||
import lotto.domain.WinNumbers; | ||
|
||
public enum LottoRank { | ||
FIRST(2_000_000_000, 6, false), | ||
SECOND(30_000_000, 5,true), | ||
THIRD(1_500_000, 5, false), | ||
FOURTH(50_000, 4, false), | ||
FIFTH(5_000, 3, false), | ||
OTHER(0, 0, false); | ||
|
||
|
||
private final int prize; | ||
private final int count; | ||
private final boolean bonus; | ||
|
||
LottoRank(int prize, int count, boolean bonus) { | ||
this.prize = prize; | ||
this.count = count; | ||
this.bonus = bonus; | ||
} | ||
|
||
public static LottoRank calculateRank(WinNumbers winNumbers, Lotto lotto) { | ||
return Arrays.stream(LottoRank.values()) | ||
.filter(rank -> rank.count == winNumbers.calculateSameLottoNumber(lotto)) | ||
.filter(rank -> !rank.bonus || winNumbers.matchBonus(lotto)) | ||
.findAny() | ||
.orElse(LottoRank.OTHER); | ||
} | ||
|
||
|
||
public int getPrize() { | ||
return prize; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format("%d개 일치%s (%,d원)", this.count, this.bonus ? ", 보너스 볼 일치" : "", this.prize); | ||
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,39 @@ | ||||||
package lotto.domain.lottorank; | ||||||
|
||||||
import java.util.Arrays; | ||||||
import java.util.EnumMap; | ||||||
import java.util.List; | ||||||
import java.util.Map; | ||||||
import java.util.stream.Collectors; | ||||||
|
||||||
public class LottoRanks { | ||||||
|
||||||
private final Map<LottoRank, Integer> ranks; | ||||||
|
||||||
private LottoRanks(Map<LottoRank, Integer> ranks) { | ||||||
this.ranks = ranks; | ||||||
} | ||||||
|
||||||
public static LottoRanks from(List<LottoRank> ranks) { | ||||||
EnumMap<LottoRank, Integer> lottoRankMap = new EnumMap<>(LottoRank.class); | ||||||
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. EnumMap이라는 게 있었군요!! 딱 제가 찾던 녀석입니다 ㅜㅜ |
||||||
Arrays.stream(LottoRank.values()).forEach(lottoRank -> lottoRankMap.put(lottoRank, 0)); | ||||||
|
||||||
ranks.forEach(rank -> lottoRankMap.computeIfPresent(rank, (key, value) -> value + 1)); | ||||||
return new LottoRanks(lottoRankMap); | ||||||
} | ||||||
|
||||||
public int getProfit() { | ||||||
return this.ranks.entrySet() | ||||||
.stream() | ||||||
.mapToInt(entry -> entry.getKey().getPrize() * entry.getValue()) | ||||||
.sum(); | ||||||
} | ||||||
|
||||||
@Override | ||||||
public String toString() { | ||||||
return ranks.entrySet() | ||||||
.stream() | ||||||
.map(entry -> String.format("%s - %d개", entry.getKey().toString(), entry.getValue())) | ||||||
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.
Suggested change
|
||||||
.collect(Collectors.joining("\n")); | ||||||
Comment on lines
+34
to
+37
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. LottoRank의 모든 값들에 대해 출력하고 있어, 당첨되지 않은 내역도 같이 출력되는 것 같습니다! |
||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package lotto.domain.rankvalidator; | ||
|
||
import lotto.domain.Lotto; | ||
import lotto.domain.WinNumbers; | ||
|
||
public interface RankValidator { | ||
|
||
boolean isRanked(WinNumbers winNumbers, Lotto lotto); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package lotto.view; | ||
|
||
import camp.nextstep.edu.missionutils.Console; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class InputView { | ||
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. 유틸성 클래스는 기본 생서자를 private로 선언해 객체 생성을 막아주세요! 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. 좋습니다. 이런 클래스를 잘 선언하지 않아서 기본 생성자를 추가하는게 어색하네요 |
||
private static final String DELIMITER = ","; | ||
|
||
private InputView() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
public static int requestInteger() { | ||
final String input = Console.readLine(); | ||
return parseInt(input); | ||
} | ||
Comment on lines
+15
to
+18
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. 맞는 얘기인 것 같아요! |
||
|
||
public static List<Integer> requestWinNumbers() { | ||
final String input = Console.readLine(); | ||
|
||
return Arrays.stream(input.split(DELIMITER)) | ||
.map(InputView::parseInt) | ||
.collect(Collectors.toList()); | ||
} | ||
Comment on lines
+20
to
+26
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. InputView 클래스에서 입력이 아닌 책임도 같이 갖고 있어서 어색한 것 같아요. 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. view 관련 로직은 어느게 맞는지 잘 모르겠어요.. |
||
|
||
private static int parseInt(String value) { | ||
try { | ||
return Integer.parseInt(value); | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException(String.format("[ERROR] 입력값 %s는 숫자가 아닙니다.", value)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomUniqueNumbersInRangeTest; | ||
import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
class ApplicationTest extends NsTest { | ||
private static final String ERROR_MESSAGE = "[ERROR]"; | ||
|
@@ -48,10 +49,9 @@ class ApplicationTest extends NsTest { | |
|
||
@Test | ||
void 예외_테스트() { | ||
assertSimpleTest(() -> { | ||
runException("1000j"); | ||
assertThat(output()).contains(ERROR_MESSAGE); | ||
}); | ||
assertSimpleTest(() -> assertThatThrownBy(() -> runException("1000j")) | ||
.isInstanceOf(IllegalArgumentException.class) | ||
); | ||
Comment on lines
+52
to
+54
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. 👍🏻 |
||
} | ||
|
||
@Override | ||
|
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는 선언하시고, OutputView는 선언하지 않으신 이유가 있으실까요?
애플리케이션 중간중간 출력하는 로직이 노출돼서 책임이 모호해지는 것 같습니다.
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.
OutputView를 만든다고해서 출력하는 로직이 크게 달라질 것 같지않아서.,,?
view에는 많이 신경 안썼습니다 ㅠㅠ