Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
873a9f4
refactor: 로또 숫자들을 생성한다는 명확한 의미로 매서드 이름 변환
Gyuchool Feb 27, 2022
1210aad
refactor: Money class VO객체로 전환
Gyuchool Feb 27, 2022
d823a1f
refactor: LottoNumber에 캐싱 적용
Gyuchool Feb 27, 2022
9ea144d
Lotto의 맴버 변수 자려구조 Set으로 변경 및 랜덤 생성 매서드 인터페이스 적용
Gyuchool Feb 28, 2022
b9dfe71
refactor: winningLotto에서 당첨 로또 확인하도록 수정 및 Statistic 생성과 동시에 초기화
Gyuchool Feb 28, 2022
8116b94
docs: 기능 요구 사항 추가
Gyuchool Feb 28, 2022
15432f7
feat: 수동으로 구매할 로또 수 입력
Gyuchool Mar 1, 2022
a23c181
feat: 로또 수 입력시, 자연수가 아니면 예외 발생
Gyuchool Mar 1, 2022
7497f85
feat: 검증 로직 적용
Gyuchool Mar 1, 2022
a79cbb9
feat: 수동으로 구매할 로또 번호 입력
Gyuchool Mar 1, 2022
2bc1eec
feat: 수동 로또와 자동 로또 합쳐서 결과 계산
Gyuchool Mar 1, 2022
d41a985
refactor: 자동로또와 수동로또 클래스 생성
Gyuchool Mar 1, 2022
069301c
test: 수동로또와 자동로또가 합쳐서 하나의 로또로 되는지 테스트
Gyuchool Mar 1, 2022
62b2a54
test: 로또 번호 정상인 경우 테스트
Gyuchool Mar 1, 2022
b4c3ec5
refactor: @ParameterizedTest로 로또 등수 테스트
Gyuchool Mar 1, 2022
f17eaa4
refactor: 테스트 이름이 명확하도록 수정
Gyuchool Mar 1, 2022
291d619
docs: lotto 상속을 통해 예외처리 사항 업데이트
Gyuchool Mar 1, 2022
fa9501a
refactor: 상속으로 구현한 Lottos 해제
Gyuchool Mar 3, 2022
cee97de
refactor: numbers 반환하는 매서드 이름 getNumbers로 수정
Gyuchool Mar 3, 2022
449a848
refactor: 1부터 45사이의 숫자가 아니면 예외 발생 추가
Gyuchool Mar 3, 2022
f66270d
refactor: 로또 개수 계산하는 매서드에서 자동 로또 개수를 인자로 받도록 변경
Gyuchool Mar 3, 2022
71d6380
refactor: money객체에서 자동, 수동 로또 개수 검증하기
Gyuchool Mar 3, 2022
85e822b
refactor: 에러 메세지 상수화
Gyuchool Mar 3, 2022
7366195
refactor: 로또 번호들을 받음으로써 유의미한 매서드로 변환
Gyuchool Mar 3, 2022
b69e4e9
refactor: 중복된 상수 제거
Gyuchool Mar 3, 2022
4d67be0
refactor: 매서드 의미가 명확하도록 네이밍 변경
Gyuchool Mar 3, 2022
94361e3
refactor: money객체에서 자동 로또 개수 검증하도록 책임 이전
Gyuchool Mar 4, 2022
cc117ce
refactor: 수동 로또 생성기 인터페이스 추가
Gyuchool Mar 4, 2022
83a01c7
refactor: view에서 객체 생성 로직 제거
Gyuchool Mar 4, 2022
6a8eefa
refactor: Money에서 로또 개수 상태 제거
Gyuchool Mar 6, 2022
9875d20
refactor: controller 패키지 정리
Gyuchool Mar 6, 2022
4764515
refactor: 상수 정책에 맞게 이동
Gyuchool Mar 6, 2022
6680172
refactor: controller에서 view 의존성 제거
Gyuchool Mar 6, 2022
35ab131
refactor: List<LottoGenerator>를 이용한 구조 개선
Gyuchool Mar 6, 2022
43d82c9
refactor: 방어적 복사로 수정
Gyuchool Mar 6, 2022
dd4362c
refactor: 정적팩토리 네이밍 변경
Gyuchool Mar 6, 2022
a38645c
test: 수동 로또 테스트 추가
Gyuchool Mar 6, 2022
0287e78
refactor: AutoLottoGenerator에서 로또 번호 생성 책임 이전
Gyuchool Mar 6, 2022
fb575e0
refactor: view에서 도메인 의존성 제거
Gyuchool Mar 6, 2022
70a69f2
refactor: stream 사용 및 변수명 수정
Gyuchool Mar 6, 2022
9cbc911
refactor: convention에 맞게 수정
Gyuchool Mar 6, 2022
46417a0
refactor: 중복된 로직 제거
Gyuchool Mar 6, 2022
6faef49
refactor: controller에서 Lottos생성하도록 수정
Gyuchool Mar 6, 2022
4987375
refactor: controller에서 Lottos 생성하도록 수정
Gyuchool Mar 6, 2022
f37b823
refactor: dto에서 collectingAndThen 매서드 적용
Gyuchool Mar 7, 2022
7519d44
refactor: 상수명 네이밍 수정
Gyuchool Mar 7, 2022
c89bff0
refactor: 로또 구매할때, LottosDto를 반환하도록 수정
Gyuchool Mar 7, 2022
4574bf5
refactor: statisticDto내부에 도메인 제거
Gyuchool Mar 7, 2022
f538071
refactor: collectingAndThen사용으로 한번에 dto반환하도록 수정
Gyuchool Mar 7, 2022
ac1cb12
refactor: LottoService 생성을 통한 구조 개선
Gyuchool Mar 7, 2022
c9a174b
refactor: Money 도메인 의존성 제거
Gyuchool Mar 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 로또(자동) 🤑
# 로또(자동) + (수동) 🤑

## 기능요구사항 😎

Expand All @@ -9,6 +9,11 @@
- [x] 지난주 당첨 번호 입력
- [x] 보너스 볼 입력
- [x] 6자리인 당첨 번호 입력
- [x] 수동으로 구매할 로또 수 입력
- [x] 자연수가 아니면, 예외 발생
- [x] 수동으로 구매할 로또 번호 입력
- [x] 번호 개수가 6개가 아니면, 예외 발생
- [x] 중복된 번호가 있으면, 예외 발생

### 로또

Copy link

Choose a reason for hiding this comment

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

코멘트가 안달려 여기다가 답변해둘게요.

Q. #444 (comment)
기론이 생각하신 부분이 맞고, 더 나아가서 view 패키지를 완전히 독립적인 다른 애플리케이션(프론트엔드)라고 생각하고 의존성을 끊어내보면 더 많이 고민하고 연습이 되지 않을까 싶은 의도가 있었습니다!

Q. #444 (comment)
LottoNumber를 의존하고 있고 LottoNumber가 캐싱하고 있기 때문에 메서드로 제공하여 중복을 제거하면 좋겠다는 의도로 드린 코멘트입니다:)

Expand All @@ -18,6 +23,7 @@

### 당첨 로또 확인

- [x] 수동 로또와 자동 로또 합쳐서 계산
- [x] 로또에서 일치하는 번호 개수 확인
- [x] 당첨 번호에 중복된 번호가 있을 시 예외 발생
- [x] 당첨 번호 통계 기능
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
import controller.LottoController;
import controller.dto.LottosDto;
import controller.dto.StatisticDto;
import domain.Money;
import service.LottoService;
import view.InputView;
import view.OutputView;

import java.util.List;

public class Application {
public static void main(String[] args) {
LottoMachine lottoMachine = new LottoMachine();
lottoMachine.start();
LottoController lottoController = new LottoController(new LottoService());
int inputMoney = InputView.askInputMoney();
int manualLottoCount = InputView.askManualLottoCount();
List<String[]> manualLottoNumbers = InputView.askManualLottoNumbers(manualLottoCount);
LottosDto lottosDto = lottoController.purchase(inputMoney, manualLottoCount, manualLottoNumbers);
OutputView.printCountOfLotto(lottosDto.getSize(), manualLottoCount);

OutputView.printLottos(lottosDto);
String[] inputWinningNumber = InputView.askInputWinningNumber();
int inputBonusBall = InputView.askInputBonusBall();

StatisticDto statisticDto = lottoController.winningResult(inputWinningNumber, inputBonusBall,
manualLottoNumbers, inputMoney);
Copy link

Choose a reason for hiding this comment

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

수동 로또 번호만 입력하고 있어요 😭

OutputView.printStatistics(statisticDto);
OutputView.printProfitRate(statisticDto.getProfitRate());

}
}
33 changes: 0 additions & 33 deletions src/main/java/LottoMachine.java

This file was deleted.

39 changes: 39 additions & 0 deletions src/main/java/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package controller;

import controller.dto.LottosDto;
import controller.dto.StatisticDto;
import domain.*;
import service.LottoService;

import java.util.List;

public class LottoController {

private final LottoService lottoService;

public LottoController(LottoService lottoService) {
this.lottoService = lottoService;
}

public LottosDto purchase(int inputMoney, int manualLottoCount, List<String[]> manualLottoNumbers) {
Copy link

Choose a reason for hiding this comment

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

로또 구입할 때 구입 금액과 선택한 로또 번호만 주면 구입할 수 있지 않나요?

Money money = lottoService.createMoney(inputMoney);
int autoLottoCount = lottoService.getAutoLottoCount(money, manualLottoCount);
Lottos lottos = lottoService.generateLottos(manualLottoNumbers, autoLottoCount);
Comment on lines +19 to +21
Copy link

Choose a reason for hiding this comment

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

서비스 로직이 응집도가 낮은 것 같아요.

로또 구입 기능을 제공하는 서비스가 되어야 하지 않을까요?
lottoService.purchase(inputMoney, manualLottoNumbers);

Copy link
Author

@Gyuchool Gyuchool Mar 8, 2022

Choose a reason for hiding this comment

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

그렇네요 서비스에서 모든 것을 처리하고 결과만 반환하니 더욱 깔끔해진 것 같습니다!

여기서 서비스 로직이 응집도가 낮다는 것은 어떤 이유인지 알 수 있을까요?
제가 공부했던 바로는 클래스 내에서 필드로 선언된 맴버 변수가 클래스 내부의 각 매서드에서 모두 사용될수록 응집도가 높은 것으로 알고 있었습니다.
그런데 service는 상태를 가지고 있으면 멀티 스레드환경에서 동시성 이슈가 생길수 있으므로 갖고 있지 않도록 했고, 응집도도 높은 상태가 되는 줄 알았어서 질문 드립니다!

  • 혹시 구매를 한다는 (크게보면)하나의 비즈니스 로직이 service에서 각각 흩어져서 호출하기 떄문에 응집도가 낮다고 보면 될까요?


return LottosDto.from(lottos, lottos.size());
}

public StatisticDto winningResult(String[] inputWinningNumber,
int inputBonusBall,
List<String[]> manualLottoNumbers,
Copy link

Choose a reason for hiding this comment

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

당첨 결과를 확인하는 모든 로또를 입력으로 받아야 하는 거 아닌가요!?

Suggested change
List<String[]> manualLottoNumbers,
List<String[]> lottos,

Copy link
Author

Choose a reason for hiding this comment

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

아예 로또들 Dto를 받도록 수정했습니다! List<String[]>으로 하려면 만들어진 Lottos를 다시 돌면서 List<String[]> 에 맞는 형으로 바꿔야 할것 같아서 Dto로 넘겼습니다.

그런데 이렇게하니 LottosDto가 많이 무거워진 것 같아서 어떻게 해야 할지 고민입니다..

int inputMoney) {
WinningLotto winningLotto = lottoService.generateWinningLotto(
new ManualLottoGenerator(inputWinningNumber),
inputBonusBall);

Lottos lottos = lottoService.generateLottos(manualLottoNumbers, 0);
Statistic winningStatistics = lottos.getWinningStatistics(winningLotto);
Money money = lottoService.createMoney(inputMoney);
return StatisticDto.from(winningStatistics, winningStatistics.getProfitRate(money));
Comment on lines +30 to +37
Copy link

Choose a reason for hiding this comment

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

이 부분도 마찬가지로 서비스 로직의 응집도가 낮은 것 같습니다!
컨트롤러에서 비즈니스 로직을 구성하는 형태가 되었어요.

}
}
27 changes: 27 additions & 0 deletions src/main/java/controller/dto/LottoDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package controller.dto;

import domain.Lotto;
import domain.LottoNumber;

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

public class LottoDto {
private final Set<Integer> numbers;

public LottoDto(Set<Integer> numbers) {
this.numbers = new LinkedHashSet<>(numbers);
}

public static LottoDto from(Lotto lotto) {
Set<LottoNumber> lottoNumbers = lotto.getNumbers();
Copy link

Choose a reason for hiding this comment

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

변수로 담을 필요 없지 않을까요?

return lottoNumbers.stream()
.map(LottoNumber::getLottoNumber)
.collect(Collectors.collectingAndThen(Collectors.toUnmodifiableSet(), LottoDto::new));
Copy link

Choose a reason for hiding this comment

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

unmodifiableSet으로 변환할 필요가 있을까요?

}

public Set<Integer> getNumbers() {
return numbers;
}
}
35 changes: 35 additions & 0 deletions src/main/java/controller/dto/LottosDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package controller.dto;

import domain.Lottos;

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

public class LottosDto {
private final List<LottoDto> lottoDtos;
private final int size;

public LottosDto(List<LottoDto> lottoDtos, int size) {
this.lottoDtos = new ArrayList<>(lottoDtos);
this.size = size;
}

public List<LottoDto> getLottoDtos() {
return lottoDtos;
}

public int getSize() {
return size;
}

public static LottosDto from(Lottos lottos, int size) {
return lottos.getLottos()
.stream()
.map(LottoDto::from)
.collect(Collectors.collectingAndThen(
Collectors.toUnmodifiableList(),
lottoDtos -> new LottosDto(lottoDtos, size)
));
}
}
37 changes: 37 additions & 0 deletions src/main/java/controller/dto/RankDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package controller.dto;

import domain.Rank;

public class RankDto {
private final int count;
private final int winningPrice;
private final boolean hasBonusBall;

public RankDto(int count, int winningPrice, boolean hasBonusBall) {
this.count = count;
this.winningPrice = winningPrice;
this.hasBonusBall = hasBonusBall;
}


public static RankDto from(Rank rank) {
return new RankDto(
rank.getCount(),
rank.getWinningPrice(),
rank.hasBonusBall()
);
}

public int getCount() {
return count;
}

public int getWinningPrice() {
return winningPrice;
}

public boolean hasBonusBall() {
return hasBonusBall;
}

}
37 changes: 37 additions & 0 deletions src/main/java/controller/dto/StatisticDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package controller.dto;

import domain.Statistic;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class StatisticDto {
private final Map<RankDto, Integer> statisticDto;
private final double profitRate;

public StatisticDto(Map<RankDto, Integer> statisticDto, double profitRate) {
this.statisticDto = new LinkedHashMap<>(statisticDto);
this.profitRate = profitRate;
}

public static StatisticDto from(Statistic statistics, double profitRate) {
return statistics.getStatistics().entrySet()
.stream()
.collect(Collectors.collectingAndThen(Collectors.toMap(
statistic -> RankDto.from(statistic.getKey()),
Map.Entry::getValue,
(key, value) -> key,
LinkedHashMap::new
), statisticDto1 -> new StatisticDto(statisticDto1, profitRate))
);
}

public Map<RankDto, Integer> getStatisticDto() {
return new LinkedHashMap<>(statisticDto);
}

public double getProfitRate() {
return profitRate;
}
}
19 changes: 19 additions & 0 deletions src/main/java/domain/AutoLottoGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain;

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

import static domain.Lotto.LOTTO_LENGTH;

public class AutoLottoGenerator implements LottoGenerator {

@Override
public Lotto generateLotto() {
List<LottoNumber> numbers = LottoNumber.values();
Collections.shuffle(numbers);
return new Lotto(numbers.stream()
.limit(LOTTO_LENGTH)
.collect(Collectors.toSet()));
}
}
57 changes: 12 additions & 45 deletions src/main/java/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,65 +1,32 @@
package domain;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


public class Lotto {
private static final String LOTTO_DUPLICATED_MESSAGE = "[ERROR] 로또 번호에 중복된 숫자가 들어가면 안됩니다.";
private static final String LOTTO_SIZE_MESSAGE = "[ERROR] 로또의 숫자는 6개여야 합니다.";
private static final int LOTTO_START = 0;
private static final int LOTTO_END = 6;
private static final int LOTTO_SIZE = 6;
public static final int LOTTO_LENGTH = 6;
private static final String LOTTO_SIZE_MESSAGE = "[ERROR] 로또의 숫자는 중복 없이 6개여야 합니다.";

private final List<LottoNumber> numbers;
private final Set<LottoNumber> numbers;

public Lotto(List<LottoNumber> numbers) {
public Lotto(Set<LottoNumber> numbers) {
validateSize(numbers);
validateDuplicate(numbers);
Collections.sort(numbers);
this.numbers = new ArrayList<>(numbers);
this.numbers = new HashSet<>(numbers);
}

private void validateSize(List<LottoNumber> lotto) {
if (lotto.size() != LOTTO_SIZE) {
private void validateSize(Set<LottoNumber> lotto) {
if (lotto.size() != LOTTO_LENGTH) {
throw new IllegalArgumentException(LOTTO_SIZE_MESSAGE);
}
}

private void validateDuplicate(List<LottoNumber> numbers) {
if (isDuplicated(numbers)) {
throw new IllegalArgumentException(LOTTO_DUPLICATED_MESSAGE);
}
}

private boolean isDuplicated(List<LottoNumber> numbers) {
return numbers.size() != numbers.stream()
.distinct()
.count();
}

public List<LottoNumber> getLotto() {
return new ArrayList<>(numbers);
}

public static Lotto generateLottoNumber(int minNumber, int maxNumber) {
List<LottoNumber> lottoRange = IntStream.rangeClosed(minNumber, maxNumber)
.mapToObj(LottoNumber::generateLottoNumber)
.collect(Collectors.toList());
Collections.shuffle(lottoRange);
List<LottoNumber> numbers = lottoRange.subList(LOTTO_START, LOTTO_END);
return new Lotto(numbers);
}

public Rank match(WinningLotto winningNumber) {
int matchCount = getMatchCount(winningNumber);
boolean hasBonusBall = winningNumber.isBonusBallMatch(this);
return Rank.valueOf(matchCount, hasBonusBall);
public Set<LottoNumber> getNumbers() {
return new HashSet<>(numbers);
}

private int getMatchCount(WinningLotto winningNumber) {
public int getMatchCount(Lotto lotto) {
return (int) numbers.stream()
.filter(winningNumber::contains)
.filter(lotto::contains)
.count();
}

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/domain/LottoGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package domain;

public interface LottoGenerator {
Lotto generateLotto();
}
Loading