Skip to content

Conversation

@Gobukgol
Copy link

@Gobukgol Gobukgol commented May 26, 2020

image

클래스 설명
LottoNumberCache 로또 번호들을 캐싱하는 LottoNumber 내부 정적 클래스
LottoNumber 로또 번호
NumberGenerator LottoNumberGenerator가 사용할 정수 리스트를 만들어주는 인터페이스, 인자로 정수 리스트의 최솟값과 최댓값을 받음
ShuffledNumberGenerator NumberGenerator를 구현하는 클래스로 생성된 리스트를 셔플까지해서 반환
LottoNumberGenerator 자동으로 인자로 받은 갯수만큼 LottoNumber를 반환하거나, 인자롤 받은 정수 리스트를 LottoNumber로 변환하여 반환.
LottoTicket LottoNumber를 감싸는 일급컬렉션 정정 팩토리 메소드로 생성
LottoTickets LottoTicket의 일급 컬렉션, LottoWinningTicket을 인자로 받아 가지고있는 Ticket 추첨해서 LottoResult로 반환
LottoMachine 로또 티켓을 발급
ManualNumbersDto 수동으로 입력한 정수 리스트를 가지고있음.
LottoWinningTicket 지난주 당첨번호와 보너스 번호를 가지는 클래스
WinningSheet 로또 상금 정보를 가지는 Enum, 맞은 갯수와 보너스 일치 여부로 맞는 상금 정보를 반환
LottoResult 로또 추첨 결과를 가지고 있는 클래스, 로또 당첨 결과를 Map으로 관리
LottoMoney 구매금액으로 자동과 수동 티켓수를 계산하고 수익률 계산

피드백 반영하고 최종 요구사항까지 구현하였습니다.
피드백 반영하면서 NumberGenerator 인터페이스의 이름을 고민하다가 실패하였습니다.

Comment on lines 4 to 6
private static final int LOTTO_PRICE = 1000;
private final int purchaseAmount;
private final int numberOfTicket;
Copy link
Member

Choose a reason for hiding this comment

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

돈이 로또가격과 티켓의 개수를 가지고있는게 어색해보여
이건 LottoMachine 쪽에 있는게 나은거같아


OutputView.printLottoTickets(lottoTickets);

List<Integer> winningNumbers = InputView.inputLastWinningNumbers();
Copy link
Member

Choose a reason for hiding this comment

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

WinningNumbers는 일급컬렉션으로 묶는게 좋아보여
그럼 LottoNumberGenerator에 있는 public static LottoNumber findByNumber(int number) 메소드가 없어도 될거같아.

Comment on lines 5 to 11
public enum WinningSheet {
FAIL(0, 0),
FOURTH(3, 5000),
THIRD(4, 50000),
SECOND(5, 1500000),
FIRST(6, 2000000000);

Copy link
Member

Choose a reason for hiding this comment

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

로또 보너스 번호가 없는거같아!


OutputView.printNumberOfTicket(lottoMoney);

List<LottoTicket> lottoTickets = LottoMachine.purchaseLottoTicket(lottoMoney);
Copy link
Member

Choose a reason for hiding this comment

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

List은 일급컬렉션으로 묶는게 좋아보여!

Comment on lines 14 to 18
static {
lottoNumbers = IntStream.rangeClosed(LOTTO_NUMBER_UNDER_BOUND, LOTTO_NUMBER_UPPER_BOUND)
.mapToObj(LottoNumber::new)
.collect(Collectors.toList());
}
Copy link
Member

Choose a reason for hiding this comment

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

싱글턴!!
내 개인적인 생각인데 Interger 클래스 까보면 내부에 static class 로 IntergerCache가 있단말이야?
그래서 LottoNumber도 LottoCache같은 거를 내부에 static class로 들고 있고
로또 넘버를 생성하는 거를 부르면 LottoCache에서 미리 생성된 싱글턴들을 반환해 주는건 어떨까??
LottoNumber의 생성자가 열려있어서 싱글턴하게 사용되지 않을수 있을거 같은 생각이 들어서!
LottoNumber의 생성자는 private으로 닫아두고 해당 private 생성자를 이용해서 static class에 cache를 생성해두고
of 같은 정적 팩토리 메서드로 캐싱된 값을 가져올수 있게 하는거지!

Comment on lines 14 to 23
public static List<LottoTicket> purchaseLottoTicket(LottoMoney lottoMoney) {
List<LottoTicket> lottoTickets = new ArrayList<>();

for (int i = 0; i < lottoMoney.getNumberOfTicket(); i++) {
List<LottoNumber> lottoNumbers = LottoNumberGenerator.generateLottoNumbers();
lottoTickets.add(new LottoTicket(lottoNumbers));
}

return lottoTickets;
}
Copy link
Member

Choose a reason for hiding this comment

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

LottoTicket이 노출될 필요가 있나? 라는 생각이 들었어!
LottoNumberGenrator가 굳이 List<LottoNumber>를 줘야하나? 라는 생각에서
LottoTicket을 반환해 줘도 문제 없지 않을까? 라는 생각이 들어

Comment on lines 23 to 24
public static List<LottoNumber> generateLottoNumbers() {
Collections.shuffle(lottoNumbers);
Copy link
Member

Choose a reason for hiding this comment

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

Collections.shuffle(lottoNumbers); 라는 부분은 우리가 제어할수 없는 부분이잖아?
제어할 수 없는 부분에 대한 의존성은 분리해 놓는게 좋지 않을까?

Comment on lines 24 to 36
private Map<WinningSheet, Long> makeWinningStatistics(List<Integer> drawResults) {
Map<WinningSheet, Long> winningStatistics = new LinkedHashMap<>();

for (WinningSheet winningSheet : WinningSheet.values()) {
long count = drawResults.stream()
.filter(drawResult -> winningSheet.getMatchCount() == drawResult)
.count();

winningStatistics.put(winningSheet, count);
}

return winningStatistics;
}
Copy link
Member

Choose a reason for hiding this comment

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

이 부분은 winningSheet에게 책임을 더 넘겨보는건 어떨까?

long count = drawResults.stream()
                    .filter(drawResult -> winningSheet.getMatchCount() == drawResult)
                    .count();

특히 이 부분..!!

.reduce(Integer::sum)
.orElse(0);

double rateOfReturn = profit / lottoMoney.getPurchaseAmount();
Copy link
Member

Choose a reason for hiding this comment

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

이 부분도 LottoMoney가 해줄수 있지 않을까?

import java.util.Set;
import java.util.stream.Collectors;

public class DrawingMachine {
Copy link
Member

Choose a reason for hiding this comment

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

우승 번호의 일급컬렉션 역할이라 위 이름이 그다지 어울리지 않는것 같아!

private void validateDuplicateWinningNumbers(List<Integer> winningNumbers) {
Set<Integer> nonDuplicateNumbers = new HashSet<>(winningNumbers);

if (nonDuplicateNumbers.size() != WINNING_NUMBER_SIZE) {
Copy link
Member

Choose a reason for hiding this comment

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

nonDuplicateNumbers.size() 가 7이어도 이 메서드는 실패하는 건데 중복은 아닌거잖아?
validateWinningNumbersSize 메서드가 먼저 호출되서 지금은 검증이 되겠지만
엄연히 validateDuplicateWinningNumbers 메서드만 호출했을때는 잘못된 로직아닐까?

import java.util.List;
import java.util.Map;

public class LottoResult {
Copy link
Member

Choose a reason for hiding this comment

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

이 객체에서 객체간의 의존성을 더 잘게 쪼개고 책임을 분리해보는게 좋을것 같아

Copy link
Member

@pci2676 pci2676 left a comment

Choose a reason for hiding this comment

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

오~~~ 전체적으로 깔끔하게 구현된 것 같아

Comment on lines 8 to 11
public LottoMoney(int purchaseAmount) {
validatePurchaseAmount(purchaseAmount);
this.numberOfAutoTicket = purchaseAmount / LOTTO_PRICE;
}
Copy link
Member

Choose a reason for hiding this comment

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

사용되지 않는 생성자네!

Comment on lines 19 to 26
public static LottoTicket ofFixed(List<Integer> numbers) {
validateSize(numbers);
return new LottoTicket(LottoNumberGenerator.generateFixedNumber(numbers));
}

public static LottoTicket ofAuto() {
return new LottoTicket(LottoNumberGenerator.generateRandomNumber(LOTTO_NUMBERS_SIZE));
}
Copy link
Member

Choose a reason for hiding this comment

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

LottoNumberGenerator의 의존성이 LottoTicket 내부에 숨겨져있어서 테스트하기가 힘들것 같아
fixed 티켓은 테스트할수 있지만 auto 티켓은 이 상태로는 테스트하기가 힘들것 같아보여

Comment on lines 20 to 26
public LottoTicket getWinningTicket() {
return winningTicket;
}

public LottoNumber getBonusNumber() {
return bonusNumber;
}
Copy link
Member

Choose a reason for hiding this comment

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

사용되지 않는 메서드넹

Comment on lines +28 to +30
if (isThirdAndMatchBonus(findSheet, matchBonus)) {
return SECOND;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (isThirdAndMatchBonus(findSheet, matchBonus)) {
return SECOND;
}
if (findSheet.isSecond(matchBonus)) {
return SECOND;
}

이건 어때?

Comment on lines 20 to 24
public double calculateRateOfReturn(int profit) {
int purchaseAmount = (numberOfAutoTicket + numberOfManualTicket) * LOTTO_PRICE;

return profit / purchaseAmount;
}
Copy link
Member

Choose a reason for hiding this comment

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

int 결과 계산이 double이 되어버렸어

import java.util.List;
import java.util.stream.Collectors;

public class LottoBill {
Copy link
Member

Choose a reason for hiding this comment

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

난 갠적으로 LottoTickets가 더 이름이 나은거같아보여

this.lottoTickets = lottoTickets;
}

public LottoResult drawAllLotto(LottoWinningTicket lottoWinningTicket) {
Copy link
Member

Choose a reason for hiding this comment

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

draw의 뜻이 끌어당기다, 그리다 이런뜻으로 알고있는데 다른 뜻은 모르겠어서
어떤 일을 하는지 이름만 보고는 잘 모를거같아

@Gobukgol Gobukgol changed the title 민형 로또(자동) 구현 민형 로또(수동) 구현 Jun 11, 2020
import java.util.List;

public interface NumberGenerator {
List<Integer> generate(int min, int max);
Copy link
Member

Choose a reason for hiding this comment

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

음 나라면

Suggested change
List<Integer> generate(int min, int max);
List<Integer> generateRangeOf(int min, int max);

라고 했을거같아

Comment on lines +23 to +31
public static LottoTicket ofFixed(List<Integer> numbers) {
validateSize(numbers);
return new LottoTicket(LottoNumberGenerator.generateFixedNumber(numbers));
}

public static LottoTicket ofAuto(NumberGenerator numberGenerator) {
return new LottoTicket(LottoNumberGenerator.generateRandomNumber(LOTTO_NUMBERS_SIZE, numberGenerator));
}

Copy link
Member

Choose a reason for hiding this comment

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

티켓 자체가 수동or자동 티켓을 발급해주는게 난조금 이상하게 보여
티켓이 티켓을 생성하는 책임을 가지고있다는 느낌이랄까?
흠.. 솔직히 잘 모르겠다 나는 LottoTicket을 만들어주는 애는 다른 클래스에 책임을 넘겼어.

Choose a reason for hiding this comment

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

음 이건 정적 펙토리 메서드입니다.
객체의 생성은 해당 객체의 생성자로만 이루어지는데, 생성자의 이름변경을 못하니 클래스메서드로 생성함수의 이름으로 명확하게 표현한걸로 보이네요,
이런 구현은 좋아보입니다.

Comment on lines +53 to +55
Set<Integer> duplicateNumbers = lottoNumbers.stream()
.map(LottoNumber::getNumber)
.collect(Collectors.toSet());
Copy link
Member

Choose a reason for hiding this comment

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

결과를 Set으로 안받구 그냥 integer로 받아도 좋을듯!

Suggested change
Set<Integer> duplicateNumbers = lottoNumbers.stream()
.map(LottoNumber::getNumber)
.collect(Collectors.toSet());
int distinctNumberSize = lottoNumbers.stream()
.map(LottoNumber::getNumber)
.collect(Collectors.toSet())
.size()


public int findMatchCount(LottoTicket winningTicket) {
return (int) IntStream.range(0, LOTTO_NUMBERS_SIZE)
.filter(index -> winningTicket.findByIndex(index).equals(this.findByIndex(index)))
Copy link
Member

Choose a reason for hiding this comment

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

디미터 법칙을 위반하고 있는것 같아!

Comment on lines +3 to +6
public class LottoMoney {
private static final int LOTTO_PRICE = 1000;
private int numberOfAutoTicket;
private int numberOfManualTicket;
Copy link
Member

Choose a reason for hiding this comment

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

흠 Money는 딱 돈에 대한 값만을 들고 있을것 같은 VO의 네이밍인데 산 갯수 두개를 들고 있는게 뭔가 어색해보여 이름을 바꿔줘도 될것같아.
아니면 객체를 나눠서 돈은 돈대로 갯수는 갯수대로 따로 관리해도 좋을것 같아
그리고 VO니까 final을 붙여줘도 좋을것 같아

}
}

public int findMatchCount(LottoTicket winningTicket) {
Copy link

@chldbtjd2272 chldbtjd2272 Jun 12, 2020

Choose a reason for hiding this comment

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

for문을 index에 의존해서 돌리는 방법이 아니라
애초에 LottNumber와 index를 감싸는 객체를 만들어 클래스를 만들어서 비교하면 더 깔끔하게 로직을 짤 수 있을거같아.

class PickedLottoNumber{
 private int order;
 private LottoNumber lottoNumber;
  .....
}

public static void main(String[] args) {
int purchaseAmount = InputView.inputPurchaseAmount();

int numberOfManualTicket = InputView.inputNumberOfManualTicket();

Choose a reason for hiding this comment

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

InputView에서 LottoMoney객체를 한번에 받아 가져오는게 좋을듯, 기본 타입을 굳이 다 노출시킬필요도 없고 도메인 객체를 input에서 받아도 상관없어보여

LottoMoney lottoMoney=InputView.inputLottoInfo();


int bonusNumber = InputView.inputBonusNumber();

LottoWinningTicket lottoWinningTicket = new LottoWinningTicket(winningNumbers, bonusNumber);

Choose a reason for hiding this comment

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

이것도 위와 마찬가지로 View에서 바로 받아와도 문제없어

Comment on lines +23 to +31
public static LottoTicket ofFixed(List<Integer> numbers) {
validateSize(numbers);
return new LottoTicket(LottoNumberGenerator.generateFixedNumber(numbers));
}

public static LottoTicket ofAuto(NumberGenerator numberGenerator) {
return new LottoTicket(LottoNumberGenerator.generateRandomNumber(LOTTO_NUMBERS_SIZE, numberGenerator));
}

Choose a reason for hiding this comment

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

음 이건 정적 펙토리 메서드입니다.
객체의 생성은 해당 객체의 생성자로만 이루어지는데, 생성자의 이름변경을 못하니 클래스메서드로 생성함수의 이름으로 명확하게 표현한걸로 보이네요,
이런 구현은 좋아보입니다.

import java.util.List;

public class LottoWinningTicket {
private final LottoTicket winningTicket;

Choose a reason for hiding this comment

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

LottoTicket과 winningTicket은 같은 역할을 하는 객체일까?
LottoTicket의 공통적으로 필요한 부분만 추출해 하나의 클래스로 추출하고,
각각 필요한 기능은 각각의 클래스가 가지는 포함관계로 풀어보는것도 좋을거같아

}

private Map<WinningSheet, Integer> makeStatistics(List<WinningSheet> winningSheets) {
Map<WinningSheet, Integer> statistics = new EnumMap<>(WinningSheet.class);

Choose a reason for hiding this comment

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

collect의 groupingBy를 쓰는게 더 깔끔할거같아

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants