-
Notifications
You must be signed in to change notification settings - Fork 252
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단계 - 로또(수동)] 돌범(조재성) 미션 제출합니다. #451
Conversation
- 컨트롤러 내부에서 new LottoNumber() 사용을 LottoService로 감춤 - lastWinLotto, bonusNumber를 컨트롤러로 get해서 run(계산) - service의 인스턴스 변수로서 계산 전까지 초기화해서 가지고 있도록 수정 - 예외시 각각을 재입력 받기 위해 - lastWinLotto 먼저 초기화 -> 마지막 입력 bonusNumber는 파라미터화
- boolean field를 제거하고 filter 하나를 추가하여 SECOND, THIRD를 감별 - NONE 객체 추가
- 만약 반복 횟수가 0이라면, IntStream.collect(Collectors.toList())는 [] 빈 List를 반환해준다.
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.
안녕하세요 돌범!
2단계 구현하시면서 고민을 많이 하신거같아요. 고생많으셨어요.
코멘트 몇가지 남겨두었는데요, 현재 도메인으로 묶어볼 수 있는 로직들을 좀 더 고민해보시면 좋을 것 같아요.
하시다가 어려운점 있으시면 말씀주세요!
src/test/java/domain/MoneyTest.java
Outdated
@DisplayName("구입금액에 맞는 로또 발급 갯수 반환을 확인한다.") | ||
@Test | ||
void calculateCounts() { |
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.
여기 클래스 컴파일이 안되는데 확인부탁드려요!
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.
git conflict 해결 과정에서 코드 중복이 생긴체로 제출되었네요! 잘 확인하겠습니다!
src/main/java/domain/Lotto.java
Outdated
public List<LottoNumber> get() { | ||
return this.lotto; |
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.
dto반환부를 잘 수정해주셨네요 👍
메서드명이 조금더 명시적이면 좋을것같아요!
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.
오호.. 이 부분은 제가 단일객체(get개별속성())이후의 Dto에 대해서는ex> list, 일급컬렉션
.get()만 붙여서 통채로 전달하는 것을 자신만의 규칙으로 정했었습니다. Lotto.getLotto() 명칭이 어색해서요!
get()도 적절하지 않은 것 같아 getLotto()로 수정하겠습니다! 혹시 괜찮은 메서드명 추천이 가능할까요 제이!
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.
메서드명 훨씬 나아졌어요!
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class LottoFactory { |
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.
의문점 Q1. 로또 발급시 service에 추가된 LottoFactory를 제대로 활용하고 있는지 궁금합니다. lottoFactory라는 클래스가 (1)Lotto만을 발급하는 정적팩토리메서드만 모아놔야하는지 (2) service를 대체해야하는게 아니라 내부에서 사용되는데 혹시 대체하면서 controller에서 사용되어야하는지 등... 궁금합니다! (3) 저의 경우, Lotto만을 발급해주는 Lotto클래스의 정적팩토리메서드 역할뿐만 아니라, LottoGroup으로 수동/자동Lotto발급후 List로 반환하는 것까지 정의해서 Lotto이후의 모든 것들을 만들어내는데 괜찮은지
- 역할을 정의하기 나름인데요, 로또발급만을 담당하게 해주신다면 지금 구조도 괜찮습니다.
- 이거는 제가 질문을 잘 이해를 못했는데요, 추가설명을 부탁드려요. 추정하여 남겨놓자면, 현재 service 가 어떤 역할을 하시는걸로 생각하셨나요? 일례로 아래와 같이 작성을 해주셨는데요,
public void issueLotto(final List<List<String>> issuedManualLottoInput) {
final List<Lotto> issuedManualLotto = LottoFactory.generateManualLottoGroup(issuedManualLottoInput);
this.issuedLotto = addAutoLotto(issuedManualLotto);
}
private List<Lotto> addAutoLotto(final List<Lotto> issuedManualLotto) {
final int autoCount = money.calculateCounts() - issuedManualLotto.size();
validate(autoCount);
if (autoCount > AUTO_LOTTO_COUNT_LOWER_BOUND) {
final List<Lotto> issuedAutoLotto = LottoFactory.generateAutoLottoGroup(autoCount);
return concatLotto(issuedManualLotto, issuedAutoLotto);
}
return issuedManualLotto;
}
로또 팩토리가 로또를 발급하는역할을 한다고 해주셨는데 현재 로또서비스란곳에서도 로또번호를 발급하고있어요. 둘의 역할이 모호한거같아요. 추정컨데 서비스를 사용하는것이 아닌 컨트롤러에서 factory를 사용하는게 어떤가요?라는 질문인거같아요. 저는 불필요한 서비스는 없애도 될것같아요. 만들어야 하는 나름의 이유가 있으시면 코멘트 부탁드려요!
- 마찬가지로 리팩토링을 하다보면 수정될 수 있을 것 같은데요, 역할을 조금더 나눠보면 좋을 것 같아요.
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.
LottoService쪽에 추가로 코멘트 남겨두겠습니다!
return Collections.unmodifiableList(issuedAutoLotto); | ||
} | ||
|
||
private static List<Lotto> AccumulateAutoLottoWithCount(final LottoGenerator lottoGenerator, Count count) { |
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.
메서드명 확인부탁드려요 :)
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.
메서드명에 소문자로 시작하도록 cameCase로 수정하였습니다! 감사합니다 제이!
public LottoService(final int money) { | ||
this.money = new Money(money); | ||
} |
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.
돈을 생성자인자로 받으신 이유가있을까요?
아마 만들어주신 팩토리와 서비스라는것을 개선하다보면 없어질 수도 있을 것 같아요!
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.
저는 controller에서 도메인을 생성하거나 사용하면 안되는 줄 알고
conroller에서 받는 족족 -> service로 빨리 넘겨주다보니 -> service에서 없어도 될 상태값 이 늘어났었습니다.ㅠㅠ
controller에서도 도메인을 받아 생성하고, service로부터 응답값으로 받아도 된다고 생각을 바꾼 상태입니다.
이 부분 피드백 해주시면 감사하겠습니다.!
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 String getProfitOrNotMessage(final ResultDto resultDto) { | ||
final double profitRate = money.calculateProfit(resultDto.getPrizeProfit()); |
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.
수익률을 계산하는것이 Money의 역할인지 다시 고민해보시면 좋을 것 같아요!
수익률을 계산하려면 구매한로또들, 당첨로또번호가 필요할텐데 이 둘을 받는 클래스를 만들어보시면 역할분리를 하실수있을거에요.
} | ||
return bonusNumber; | ||
} catch (IllegalArgumentException e) { | ||
return lottoService.calculateResult(inputView.getBonusNumber()); |
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.
이거는 참고만 해보셔요!
메서드를 정의할때 보통 인풋과 아웃풋을 명확히 하는게 좋습니다.
결과계산 이라는 행위를 할때 인풋은 뭘까요?
- 내가산 로또, 당첨번호
가 필요하지 않을까요?
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.
제이 말씀에 의하면, controller가 내가 산 로또, 당첨번호
를 알고 있어야 -> service에게 이걸로 처리해주라고 부탁할 수 있습니다.
저는 controller가 아닌 service에게 도메인을 알고 시키는 것으로 코드를 작성해왔습니다. service만 도메인을 알고, 계속 필요하면 그것을 상태로 관리하게 하였습니다.
이부분에 대한 고민을 금일 상담전까지 고민해봐야겠네요 ㅠ!
그렇다면 제가 생각한 방법은
- controller가 도메인이 아닌 원시값을 가지고 계산에 들어간다. (이렇게 하면 도메인 의존이 아니게 된다)
- controller가 VO같은 것이 아니라면 Dto(혹은result클래스) 를 가지고 service에게 시킨다.
- 만일, 윙의 문제처럼 controller가 service에게 시킬 때, 내가산 로또, 당첨번호의 dto가 아닌 도메인이 필요하다면.. 가지고 있어도 되나..?
3번이 제일 고민입니다!
-> 일단 수정은 controller에서 도메인 편하게 사용하자. view에만 전달하지말자. 이번에는 result넘기는 것만 허용 정도로 정리해서 코드를 수정했습니다!
src/main/java/dto/ResultDto.java
Outdated
public EnumMap<RankPrize, Integer> getResultDto() { | ||
return resultDto; | ||
} |
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.
Result라는 도메인으로 바뀌게되면 dto반환부도 사라져야할텐데요,
일단은 dto는 신경쓰지말고 도메인도출, 역할분리 만 해주셔도 정말 충분할거같아요 👍
지금은 view에 도메인그대로 전달되도 좋습니다.
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.
이 코멘트를 나중에 보고 고민하다가 시도한 내용은 아래와 같습니다!
- geResultDto 및 Dto관련 내용삭제
- outputView에서 dto를 받아 내부에서 통계관련 string처리 -> Result클래스에서 메소드에서 통계관련 string처리후 controller -> output까지 단일 String으로 결과 반환
outputView.printWinStatistics(result.getStatistics());
string처리는 view에서 해결되어야할 것 같은데 Result라는 결과(도메인)을 controller랑 view에 전달은 허용되었는데, string처리도 Result내부에서 하는게 맞는지 궁금합니다!
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.
String처리를 분리하는게 좋습니다. 이 부분은 코멘트로 다시남겨둘게요!
private void issueLotto() { | ||
final int manualCount = getManualCount(); | ||
if (manualCount > MANUAL_LOTTO_IS_NONE_NUMBER) { | ||
lottoService.issueLotto(getManualLottoWith(manualCount)); | ||
outputView.printLotto(lottoService.getIssuedLotto(), manualCount); | ||
return; | ||
} | ||
lottoService.issueLotto(new ArrayList<>()); | ||
outputView.printLotto(lottoService.getIssuedLotto(), manualCount); | ||
} |
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.
의문점 Q4. controller에서 inputview로 받는 수동 로또가 0개로 없는 경우, LottoService에 빈리스트를 넘겨주도록 분기하였는데 이게 맞는지 궁금합니다.
구조를 개선하는게 좋아보여요! 로또를 발급할때 수동개수 등 정보를 전달해주면 굳이 밖에서 분기를 해주지 않아도 발급된로또 getIssuedLotto()를 구할 수 있지 않을까요?
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.
수동 개수를 전달해서 내부에서 처리하려고 했으나, 내부 try/catch 때문에 헤맸으나 early return + Collections.emptyList()
를 이용해서 해결하였습니다 감사합니다 제이!
private final Money money; | ||
private Lotto lastWinLotto; | ||
private List<Lotto> issuedLotto; |
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.
불필요한 인스턴스변수인지 고민해보시면 좋을 것 같아요.
도메인 도출측면에서 생각해보면 lastWinLotto와 보너스번호를 묶어보면 당첨로또 라는 개념이 나올 수 있구요,
지난번에도 계속 이야기했던 부분이 역할분리인데요, 만들어주신 서비스가 비대해지는것을 막으려면 서비스에 도메인로직이 있는지 점검이 필요해요.
추가적으로 말씀드리면 추후 레벨에서 서비스를 굉장히 많이 다루실거에요. 지금은 서비스를 생각하지마시고 도메인로직으로 묶어볼수있는게 있는지 고민하시면 추후 미션들에 있어서 도움이 굉장히 많이 될거에요 👍
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.
서비스가 비대해지는것을 막으려면 서비스에 도메인로직이 있는지 점검이 필요해요.
service에서 도메인에게 일을 시킬 때, 제가 인스턴스로 관리하는 도메인 및 중간 결과물들을
- 서비스가 비대할때는 도메인 추출을 생각
- 당첨로또 같은 새로운 도메인으로 추출 ->도메인 로직으로서 처리
하도록 빼야한다는 말씀이군요!
고민해서 적용해보도록 하겠습니다!
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.
제가 컨트롤러에서는 도메인 사용안해야한다의 생각을 포니까..
서비스 상태 관리가 필요 없어졌습니다!
추가로 WinLotto
의 클래스를 생성하여 관련 보너스볼 검증 로직 및 일부 로직이 도메인으로 넘어갔습니다.
말씀해주신 사항은 항상 되뇌이도록 잘 메모해야겠습니다 감사합니다 제이!
result라는 결과값 도메인을 controller에 반환가능 + 결과관련 로직을 담을 수 있게 허용하여 - service가 상태 관리하던 money -> controller에서 원시값으로 받아 여러번 사용 - Money에서 이익금을 받아 이익률 계산 로직을 Result에서 한번에 처리하도록 수정 - service에서 하던 이익률 계산을 -> 도메인 Result에게 위임
- controller에서 도메인 사용을 허용함 - LottoFactory는 Lotto만 생성하고 그 이후 로직은 service에게 책임 이전
2단계 수정사항 이라고 말씀드렸던 아래 부분
내부 early return으로 바깥쪽 분기 제거
service가 받은 후 여러번 사용되서 service에서 상태(인스턴스변수) 관리되었던 Money를 상태변수에서 제거 -> 대신 Controller에서 도메인사용 허용
LottoFactory는 로또 1장 발급만하는 정적펙토리메서드를 모은 것 -> LottoService에서 발급된 로또 모으게 하기
service에서 최대한 시킨 결과물을 한꺼번에 다 뽑게 -> 못뽑으면 controller에서 중간결과물(도메인)을 받아, 추가로 service에게 시키기
필요에 의한 도메인 생성 WinLotto
Count를 그대로 쓰되, 수동Count 생성시 검증은 정적팩토리메서드에서 따로??
public Count(final int count) {
this.count = count;
}
public static Count createWithTotal(final int manualCount, final int totalCount) {
validate(manualCount, totalCount);
return new Count(manualCount);
}
private static void validate(final int manualCount, final int totalCount) {
if (manualCount > totalCount) {
throw new IllegalArgumentException(MANUAL_COUNT_CANNOT_EXCEED_TOTAL_MESSAGE);
}
} generator를 전략적으로 나눠받아야하는데, 어차피 2개가 다 쓰여서 외부입력을 안하고 service내부에서 LottoFactory에 바로 생성해서 전달하여 사용중인데 이게 맞나??// service
public List<Lotto> issueManualLottoGroup(final List<List<String>> issuedManualLottoInput) {
final List<Lotto> issuedManualLotto = new ArrayList<>();
for (List<String> manualLottoInput : issuedManualLottoInput) {
issuedManualLotto.add(LottoFactory.generateLotto(new ManualLottoGenerator(manualLottoInput)));
}
return Collections.unmodifiableList(issuedManualLotto);
}
public static List<Lotto> generateAutoLottoGroup(final int count) {
final List<Lotto> issuedAutoLotto = accumulateAutoLottoWithCount(new AutoLottoGenerator(), new Count(count));
return Collections.unmodifiableList(issuedAutoLotto);
} |
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.
안녕하세요 돌범!
2단계 피드백 반영하시느라 고생많으셨습니다.
관련해서 몇가지 추가로 요청드려요. 같이 이야기한 내용 토대로 한 두 부분 더 개선해보시면 도움이 많이 될 거에요 👍
시간은 충분히 남았으니 차분히 고민하는 시간으로 활용하셔도 좋을 것 같아요.
src/main/java/domain/Result.java
Outdated
private static final String SECOND_RANK_CORRECT_MESSAGE = "개 일치, 보너스 볼 일치("; | ||
private static final String RANK_PRICE_MESSAGE = "원)- "; |
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.
말씀드린것처럼 도메인에서 뷰와 관련된 처리를 한번 분리해보면 좋을 것 같아요!
결과
라는 측면을 해보고싶다고하셨던 TDD로 다시 만들어보시는건 어떨까요?
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.
해당 로직을 view로 가져갔습니다 감사합니다 제이!
|
||
public LottoController() { | ||
lottoService = new LottoService(getMoney()); | ||
public LottoController(final InputView inputView, final OutputView 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.
컨트롤러도 도메인로직이 있을지 다시한번 둘러보시고 한눈에 읽히기 좋게 흐름을 만들어보면 좋을 것 같아요!
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.
컨트롤러 부분에 input에 따른 -> output으로 드러나도록 메서드 호출 위치나 메서드 추출을 변경해보았습니다.
나름 한다고 했는데 불편하실 수 도 있을 것 같습니다! ㅋㅋ^^
제이 감사합니다! 제이를 만난 것은 행운이에요
프리코스에서부터 달고 왔던 궁금증이 다 풀렸습니다!!!!!!!!!
- InputView#getManualLotto의 returnType에서 List의 List 구조 제거 - Controller에서 service를 사용하는 로직의 흐름이 자연스럽게 input/output 구조를 변경함.
제이! 오늘 쪽찝게 피드백 덕분에 프리코스 때부터 갈증 났던 부분이 많이 해소 되었습니다.ㅠ 감사합니다!!! 적게 일하시고 많이 버시는 개발자 되십시오!! |
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.
안녕하세요 돌범!
재차 요청드렸는데 마지막까지 반영하시느라 고생많으셨습니다 👍
이번 미션이 다음 미션 진행하시는데 도움이 되었으면 합니다.
이만 머지하겠습니다. 다음 미션도 화이팅이에요~
System.out.println(stringBuilder); | ||
} | ||
|
||
private String getStatisticsWith(final Entry<RankPrize, Integer> rankCount) { |
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.
잘 옮겨주셨네요 👍
1단계에 비해 많은 것들을 수정하고, 피드백 반영해보았습니다.
수정사항
List<Lotto> issuedLotto
피드백 반영내역
toDto()등 도메인 내 dto의존성 삭제 요청 반영
역할나누기
LottoGenerator
에서 int List -> LottoNumber List -> Lotto로 반환하는 로직을 작성함.LottoFactory
는 Generator구현체를 받아 Lotto 발행 뿐만 아니라 List에 모으는 로직들을 추출해놓음.의문점 Q1. 로또 발급시 service에 추가된 LottoFactory를 제대로 활용하고 있는지 궁금합니다. lottoFactory라는 클래스가 (1)Lotto만을 발급하는 정적팩토리메서드만 모아놔야하는지 (2) service를 대체해야하는게 아니라 내부에서 사용되는데 혹시 대체하면서 controller에서 사용되어야하는지 등... 궁금합니다! (3) 저의 경우, Lotto만을 발급해주는 Lotto클래스의 정적팩토리메서드 역할뿐만 아니라, LottoGroup으로 수동/자동Lotto발급후 List로 반환하는 것까지 정의해서 Lotto이후의 모든 것들을 만들어내는데 괜찮은지
의문점 Q2. 피드백 해주신 내용이 결과값class에 정의해야하는지, 즉, 따로 클래스를 추출해서 해야하는지 현 상태대로 결과값class(ResultDto)에 로직을 추가하기만 함.
결과값을 담당하는 클래스
의문점 Q3. 결과값 반환할 때, Dto가 아닌 클래스를 생성해서 반환해도 되는지(Result로 생성 -> controller가 의존해도 되나??) 만약 클래스명을 Dto로 명시한다면 getter외에 추가로직이 들어가도 되는지. ResultDto에 수익률계산로직을 포함시켰는데 명칭과 로직이 적절한지
추가 의문점
의문점 Q4. controller에서 inputview로 받는 수동 로또가 0개로 없는 경우, LottoService에 빈리스트를 넘겨주도록 분기하였는데 이게 맞는지 궁금합니다.