-
Notifications
You must be signed in to change notification settings - Fork 330
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
[2단계 - 로또(수동)] 연로그(권시연) 미션 제출합니다. #454
Changes from all commits
6d80ecb
820a506
f7db213
cf4410f
137b7c2
3b9694f
829c37d
c71c797
b7a65d2
ac8befc
9f08b0c
964e57a
e45fd9e
7796450
dcd4bcd
de191ad
3d94fe2
2701bcc
abb7362
ecaba3c
cb8cfb6
4646a00
affa474
293fe25
581f834
64660e7
6951fa8
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,9 +1,12 @@ | ||
package lotto.controller; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import lotto.domain.LottoAmount; | ||
import lotto.domain.LottoNumber; | ||
import lotto.domain.LottoNumbers; | ||
import lotto.domain.LottoTicket; | ||
import lotto.domain.ManualLottoCount; | ||
import lotto.domain.WinningNumbers; | ||
import lotto.domain.WinningResult; | ||
import lotto.view.InputView; | ||
|
@@ -13,7 +16,8 @@ public class LottoController { | |
public void start() { | ||
LottoAmount amount = inputAmount(); | ||
|
||
LottoTicket lottoTicket = buyTickets(amount); | ||
ManualLottoCount manualLottoCount = inputManualLottoCount(amount); | ||
LottoTicket lottoTicket = buyTicket(amount, manualLottoCount); | ||
|
||
WinningNumbers winningNumbers = createWinningNumbers(); | ||
|
||
|
@@ -31,37 +35,68 @@ private LottoAmount inputAmount() { | |
} | ||
} | ||
|
||
private LottoTicket buyTickets(LottoAmount amount) { | ||
int ticketCount = amount.calculateLottoCount(); | ||
OutputView.printTicketCount(ticketCount); | ||
private ManualLottoCount inputManualLottoCount(LottoAmount amount) { | ||
try { | ||
return new ManualLottoCount(InputView.inputTryCount(), amount.calculateLottoCount()); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e); | ||
return inputManualLottoCount(amount); | ||
} | ||
} | ||
|
||
LottoTicket lottoTicket = new LottoTicket(ticketCount); | ||
OutputView.printTicket(lottoTicket); | ||
private LottoTicket buyTicket(LottoAmount amount, ManualLottoCount manualLottoCount) { | ||
int manualTicketCount = manualLottoCount.getValue(); | ||
int autoTicketCount = amount.calculateLottoCount() - manualTicketCount; | ||
|
||
LottoTicket lottoTicket = LottoTicket.createAutoLottoTicket(autoTicketCount); | ||
|
||
if (manualTicketCount != 0) { | ||
lottoTicket.addLottoTicket(buyManualTicket(manualLottoCount)); | ||
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.
getter/setter 같은 네이밍은 관용어구에 가깝지만, 다른 네이밍은 좋은 코드를 많이 보다보면 감이 느는 것 같아요 ㅎㅎ |
||
} | ||
|
||
printTickets(manualTicketCount, autoTicketCount, lottoTicket); | ||
return lottoTicket; | ||
} | ||
|
||
private LottoTicket buyManualTicket(ManualLottoCount manualLottoCount) { | ||
List<LottoNumbers> manualTickets = new ArrayList<>(); | ||
int tryCount = manualLottoCount.getValue(); | ||
|
||
for (int i = tryCount; i > 0; i--) { | ||
OutputView.printInputManualTicketSentence(i); | ||
manualTickets.add(inputLottoNumbers()); | ||
} | ||
return LottoTicket.createManualLottoTicket(manualTickets); | ||
} | ||
|
||
private void printTickets(int manualTryCount, int autoTryCount, LottoTicket lottoTicket) { | ||
OutputView.printTicketCount(manualTryCount, autoTryCount); | ||
OutputView.printTicket(lottoTicket); | ||
} | ||
|
||
private WinningNumbers createWinningNumbers() { | ||
LottoNumbers inputLottoNumbers = getInputLottoNumbers(); | ||
LottoNumber bonusNumber = getBonusNumber(); | ||
OutputView.printInputWinningTicketSentence(); | ||
LottoNumbers inputLottoNumbers = inputLottoNumbers(); | ||
LottoNumber bonusNumber = inputBonusNumber(); | ||
|
||
return getWinningNumbers(inputLottoNumbers, bonusNumber); | ||
} | ||
|
||
private LottoNumbers getInputLottoNumbers() { | ||
private LottoNumbers inputLottoNumbers() { | ||
try { | ||
return new LottoNumbers(InputView.inputWinningNumbers()); | ||
return new LottoNumbers(InputView.inputLottoNumbers()); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e); | ||
return getInputLottoNumbers(); | ||
return inputLottoNumbers(); | ||
} | ||
} | ||
|
||
private LottoNumber getBonusNumber() { | ||
private LottoNumber inputBonusNumber() { | ||
try { | ||
return LottoNumber.of(InputView.inputBonusBall()); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e); | ||
return getBonusNumber(); | ||
return inputBonusNumber(); | ||
} | ||
} | ||
|
||
|
@@ -70,7 +105,7 @@ private WinningNumbers getWinningNumbers(LottoNumbers lottoNumbers, LottoNumber | |
return new WinningNumbers(lottoNumbers, bonusNumber); | ||
} catch (IllegalArgumentException e) { | ||
OutputView.printException(e); | ||
return getWinningNumbers(lottoNumbers, getBonusNumber()); | ||
return getWinningNumbers(lottoNumbers, inputBonusNumber()); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,25 @@ | ||
package lotto.domain; | ||
|
||
import java.util.stream.IntStream; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class LottoNumber { | ||
private static final int MIN = 1; | ||
private static final int MAX = 45; | ||
protected static final int MIN = 1; | ||
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. LottoNumbers에서도 해당 상수가 필요해 접근 제어자 변경 ➕ default와 protected 중 고민했으나 나중에 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으로 열긴 하는데요. (불변 상수기 때문에 접근제어를 fit하게 해봤자 큰 의미 없다고 생각해서) 답은 없으니 각 제어자 목적에 맞게 고민하고 판단하셔서 결정하시면 됩니다 :) |
||
protected static final int MAX = 45; | ||
|
||
private static final String NUMBER_RANGE_ERROR = "로또 숫자는 " + MIN + " 이상 " + MAX + " 이하의 숫자만 가능합니다."; | ||
private static final String NUMBER_RANGE_ERROR = String.format("로또 숫자는 %d 이상 %d 이하의 숫자만 가능합니다.", MIN, MAX); | ||
|
||
private static final LottoNumber[] cacheLottoNumber = new LottoNumber[MAX + 1]; | ||
private static final Map<Integer, LottoNumber> cacheLottoNumber = new HashMap<>(); | ||
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에 저장 |
||
|
||
private final int number; | ||
private final int value; | ||
|
||
static { | ||
IntStream.range(MIN, MAX + 1) | ||
.forEach(i -> cacheLottoNumber[i] = new LottoNumber(i)); | ||
} | ||
|
||
private LottoNumber(int number) { | ||
this.number = number; | ||
private LottoNumber(int value) { | ||
this.value = value; | ||
} | ||
|
||
public static LottoNumber of(int number) { | ||
validateNumber(number); | ||
return cacheLottoNumber[number]; | ||
return generateLottoNumber(number); | ||
} | ||
|
||
private static void validateNumber(int number) { | ||
|
@@ -32,14 +28,42 @@ private static void validateNumber(int number) { | |
} | ||
} | ||
|
||
public int toInt() { | ||
return number; | ||
private static LottoNumber generateLottoNumber(int number) { | ||
LottoNumber lottoNumber = cacheLottoNumber.get(number); | ||
if (lottoNumber == null) { | ||
lottoNumber = new LottoNumber(number); | ||
cacheLottoNumber.put(number, lottoNumber); | ||
} | ||
return lottoNumber; | ||
} | ||
|
||
public int getValue() { | ||
return value; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof LottoNumber)) { | ||
return false; | ||
} | ||
|
||
LottoNumber that = (LottoNumber) o; | ||
|
||
return value == that.value; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return value; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "LottoNumber{" + | ||
"number=" + number + | ||
"number=" + value + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,23 +4,19 @@ | |
import static java.util.stream.Collectors.toList; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class LottoNumbers { | ||
private static final int LOTTO_COUNT = 6; | ||
private static final int MIN_NUMBER = 1; | ||
private static final int MAX_NUMBER = 45; | ||
|
||
private static final String DELIMITER = ","; | ||
private static final String DUPLICATE_ERROR = "로또 개수는 중복이 불가능합니다."; | ||
private static final String COUNT_ERROR = "로또 개수는 " + LOTTO_COUNT + "개로 제한됩니다."; | ||
private static final String COUNT_ERROR = String.format("로또 개수는 %d개로 제한됩니다.", LOTTO_COUNT); | ||
|
||
private static final List<LottoNumber> candidateLottoNumbers = new ArrayList<>(); | ||
|
||
static { | ||
for (int i = MIN_NUMBER; i <= MAX_NUMBER; i++) { | ||
for (int i = LottoNumber.MIN; i <= LottoNumber.MAX; i++) { | ||
candidateLottoNumbers.add(LottoNumber.of(i)); | ||
} | ||
} | ||
|
@@ -31,45 +27,44 @@ public LottoNumbers() { | |
this.lottoNumbers = generateRandomLottoNumbers(); | ||
} | ||
|
||
public LottoNumbers(String input) { | ||
String[] stringArr = input.split(DELIMITER); | ||
validateLottoNumbers(stringArr); | ||
this.lottoNumbers = convertStringArrToIntegerList(stringArr); | ||
public LottoNumbers(List<Integer> 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. 최소한의 검증을 거치고 생성자에 파라미터 보내줄 수 있도록 View와 Domain의 로직 분리 |
||
validateLottoNumbers(numbers); | ||
this.lottoNumbers = convertToLottoNumberList(numbers); | ||
} | ||
|
||
private List<LottoNumber> generateRandomLottoNumbers() { | ||
Collections.shuffle(candidateLottoNumbers); | ||
return List.copyOf(candidateLottoNumbers.subList(0, LOTTO_COUNT)); | ||
} | ||
|
||
private void validateLottoNumbers(String[] stringArr) { | ||
validateLottoCount(stringArr); | ||
validateDuplicateCount(stringArr); | ||
private void validateLottoNumbers(List<Integer> numbers) { | ||
validateLottoCount(numbers); | ||
validateDuplicateCount(numbers); | ||
} | ||
|
||
private void validateLottoCount(String[] array) { | ||
if (array.length != LOTTO_COUNT) { | ||
private void validateLottoCount(List<Integer> numbers) { | ||
if (numbers.size() != LOTTO_COUNT) { | ||
throw new IllegalArgumentException(COUNT_ERROR); | ||
} | ||
} | ||
|
||
private void validateDuplicateCount(String[] arr) { | ||
int distinctCount = calDistinctCountFromArray(arr); | ||
private void validateDuplicateCount(List<Integer> numbers) { | ||
int distinctCount = calDistinctCountFromArray(numbers); | ||
|
||
if (arr.length != distinctCount) { | ||
if (numbers.size() != distinctCount) { | ||
throw new IllegalArgumentException(DUPLICATE_ERROR); | ||
} | ||
} | ||
|
||
private int calDistinctCountFromArray(String[] arr) { | ||
return (int) Arrays.stream(arr) | ||
private int calDistinctCountFromArray(List<Integer> numbers) { | ||
return (int) numbers.stream() | ||
.distinct() | ||
.count(); | ||
} | ||
|
||
private List<LottoNumber> convertStringArrToIntegerList(String[] array) { | ||
return Arrays.stream(array) | ||
.map(string -> LottoNumber.of(Integer.parseInt(string))) | ||
private List<LottoNumber> convertToLottoNumberList(List<Integer> numbers) { | ||
return numbers.stream() | ||
.map(LottoNumber::of) | ||
.collect(collectingAndThen(toList(), Collections::unmodifiableList)); | ||
} | ||
|
||
|
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.
수동 로또 개수를 검증하는 로직을 분리하고 싶어서 ManualLottoCount라는 vo를 생성했습니다
다만 ManualLottoCount에 저장한 값이 계속 필요해서 사용할때마다 getValue()를 호출하게 되네요
이게 적절한 VO의 생성이 맞나?라는 의문이 계속 들어요😂
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.
값을 사용해야 하는 시점에서는 어쩔 수 없죠 ㅎㅎ
그게 아니라면 public 메서드를 열어서 내부에서 처리하게끔 해야하는데, 아래 count 계산 로직의 경우 책임 분리의 관점에서 애매한 부분이 있을수도 있겠네요 :)