Skip to content

Conversation

@devHudi
Copy link

@devHudi devHudi commented Mar 19, 2022

안녕하세요, 앨런! 이번 피드백도 잘 부탁드려요!
1단계 코멘트에 대한 답변과 추가 질문은 이전 PR에 남겨두었습니다! (#273)

이전 두 미션보다 이번 미션이 훨씬 오래 걸렸습니다. 도메인 설계를 몇번을 엎은지를 모르겠네요. 🥲
금요일 강의 때 상태 패턴을 사용한 설계에 대해서 배우게 되었는데, 적용해보고 싶었으나 일단 지금 해온 설계를 마무리 해보고싶어서 아쉽게도 상태 패턴을 적용해보지는 못했습니다.

인스턴스 멤버를 2개로 제한하는 요구사항덕분에 도메인 설계에서 참 오래 걸렸던 것 같아요.
추가적인 질문이나 이야기해보고 싶은 내용은 코멘트로 작성할게요 🙇 즐거운 주말 보내시길!


아쉬운 점

  1. 최대한 모든 도메인을 불변 객체로 만들고싶었으나, Players, ResultCount 등을 불변 객체로 만들면 더 좋았을텐데, 일정의 문제로 가변 객체로 둔 것이 아쉽습니다.

devHudi added 19 commits March 19, 2022 18:53
…서 실행하도록 구조 개선

- BlackjackController의 public 메소드 이름 개선
- Hand.getScore 를 사용하는 코드를 Score.calculateSumFrom 으로 변경
- 테스트코드 수정
- isGreaterThan, isLessThan 과 같은 메소드를 없애고, isBusted, isOverDealerHitThreshold 와 같은 메소드를 별개로 구현함
…atchResult 로 위임

- Dto 였던 DealerMatchDto 를 DealerMatchResult 라는 도메인으로 변경
- Dto 였던 PlayerMatchDto 를 PlayerMatchResult 라는 도메인으로 변경
- 테스트 코드 작성
- Score 클래스가 카드 리스트를 전달받아 합계를 계산할 수 있도록 개선
- Hand 클래스가 첫 두장의 카드를 리스트로 반환하는 기능 구현
- 테스트코드 작성
- Money 도메인 구현
- BetAndProfit 도메인 구현
- Money 도메인에 add, subtract 메소드 구현
- BlackjackController 및 InputView, OutputView 기능 구현
- 승패결과를 나타내는 클래스 제거
- 기타 사용되지 않는 클래스 제거
- PlayerBetResult 에서 ProfitResult 로 이름 변경
- DealerProfitResult 제거
- Player 클래스에 betMoney 필드 추가
- Player 생성자 및 of 메소드에 betMoney 파라미터 추가
- ResultType 에 WIN_WITH_BLACKJACK 상수 추가
- ResultType 에 승부 결과 별 수익 계산 로직 추가
- Players 일급 컬렉션 클래스 구현
- Dealer 와 Players 를 가지고 있는 ParticipantGroup 클래스 구현
- BlackjackGame 이 Dealer 와 List<Player> 대신 ParticipantGroup 을 갖도록 개선
- ParticipantDto 를 추상클래스로 변경
- DealerDto 와 PlayerDto 를 ParticipantDto 를 상속하여 구현
- getCardNames 를 ParticipantDto 에서 Hand 로 이동
- ProfitResult 클래스 제거
- BetAndProfit 클래스 제거
- 변경된 도메인에 맞게 BlackjackController 와 OutputView 로직 수정
- 테스트 코드 수정
- Score 가 다른 Score 에 비해 큰지, 작은지를 검사하는 isGreaterThan, isLessThan 메소드를 구현함
Comment on lines 9 to 15
public class Player extends Participant {

private static final String INVALID_NAME_LENGTH_EXCEPTION_MESSAGE = "이름은 1글자 이상이어야합니다.";

private Player(final String name, final Hand hand) {
private final Money betMoney;

private Player(final String name, final Hand hand, final Money betMoney) {
Copy link
Author

Choose a reason for hiding this comment

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

ParticipantPlayer 는 별개의 클래스이고, Participant 가 가지고 있는 두개의 필드 (playerName, hand)는 private 으로 Player 측에서 접근하지 못하므로 Player 의 인스턴스 변수는 하나라고 생각했습니다.

Comment on lines +43 to +49
static {
// TODO: 2 Depth 인가?...
cache = Stream.of(CardSymbol.values())
.flatMap(symbol -> Stream.of(CardRank.values())
.map(rank -> new Card(rank, symbol)))
.collect(Collectors.toSet());
}
Copy link
Author

Choose a reason for hiding this comment

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

이 코드를 for 문으로 작성할 경우 깊이가 2가 되어버려, Stream 을 2중으로 사용했는데요, 결국 조삼모사가 아닌가 싶어요! 앨런의 의견이 궁금합니다!

Choose a reason for hiding this comment

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

넵, 이 부분은 2중으로 나눠도 괜찮은 것 같아요.

Comment on lines 21 to +25

public List<Card> getInitialCards() {
return cards.subList(0, INITIAL_CARDS_COUNT);
}

Copy link
Author

@devHudi devHudi Mar 19, 2022

Choose a reason for hiding this comment

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

이번 미션의 경우 시작과 없음 등을 의미하는 0 이라는 숫자는 별도로 상수로 분리하지 않았습니다. 상수로 분리하지 않은 0 의 경우 해당 코드 자체가 길지 않고, 메소드명으로 충분이 0 이 어떤 역할을 하는지 유추할 수 있을 것 이라고 생각했는데요, 앨런의 의견이 궁금합니다!

Copy link

@hongbin-dev hongbin-dev left a comment

Choose a reason for hiding this comment

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

안녕하세요 후디!
2단계 미션 고생하셨어요~ 구조가 많이 바뀌었군요 😮
몇 가지 코멘트 남겼으니 확인해주세요

Comment on lines +43 to +49
static {
// TODO: 2 Depth 인가?...
cache = Stream.of(CardSymbol.values())
.flatMap(symbol -> Stream.of(CardRank.values())
.map(rank -> new Card(rank, symbol)))
.collect(Collectors.toSet());
}

Choose a reason for hiding this comment

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

넵, 이 부분은 2중으로 나눠도 괜찮은 것 같아요.

return initialScore.equals(Score.valueOf(Score.MAXIMUM_SCORE));
}

protected ResultType compareWith(Participant other) {

Choose a reason for hiding this comment

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

Suggested change
protected ResultType compareWith(Participant other) {
protected ResultType compareWith(Dealer other) {

other에 Player가 들어가도 무관한가요?

Copy link
Author

Choose a reason for hiding this comment

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

딜러가 플레이어와 비교할 경우가 있을 것이라고 생각해서 이렇게 구현했는데, 생각해보니 Dealer 로 타입을 제한해도 될 것 같아요. 수정했습니다 :)

Comment on lines 60 to 66
if (other.isBlackjack()) {
return ResultType.DRAW;
}
if (other.isBusted()) {
return ResultType.WIN_WITH_BLACKJACK;
}
return ResultType.WIN_WITH_BLACKJACK;

Choose a reason for hiding this comment

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

Suggested change
if (other.isBlackjack()) {
return ResultType.DRAW;
}
if (other.isBusted()) {
return ResultType.WIN_WITH_BLACKJACK;
}
return ResultType.WIN_WITH_BLACKJACK;
if (other.isBlackjack()) {
return ResultType.DRAW;
}
return ResultType.WIN_WITH_BLACKJACK;

이렇게 간소화할 수 있지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

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

앗, 그렇군요 수정했습니다 😓

Comment on lines 48 to 56
if (isBlackjack()) {
return getResultTypeIfBlackjack(other);
}

if (isBusted()) {
return getResultTypeIfBusted(other);
}

return getResultTypeIfOrdinary(other);

Choose a reason for hiding this comment

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

상태별로 나눠놓으니 가독성이 좋군요👍

Comment on lines 70 to 76
if (other.isBlackjack()) {
return ResultType.LOSE;
}
if (other.isBusted()) {
return ResultType.DRAW;
}
return ResultType.LOSE;

Choose a reason for hiding this comment

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

Suggested change
if (other.isBlackjack()) {
return ResultType.LOSE;
}
if (other.isBusted()) {
return ResultType.DRAW;
}
return ResultType.LOSE;
return ResultType.LOSE;

플레이어가 버스트면 항상 패배입니다

Copy link
Author

Choose a reason for hiding this comment

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

블랙잭 규칙을 잘 못 이해하고 있었네요 ㅎㅎ; 로직을 변경했습니다!

Comment on lines 7 to 15
public class Players {

private static final String NOT_FOUND_PLAYER_EXCEPTION_MESSAGE = "플레이어를 찾을 수 없습니다.";
private final List<Player> value = new ArrayList<>();

public void add(Player player) {
validateNotDuplicateName(player.getName());
value.add(player);
}

Choose a reason for hiding this comment

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

하나씩 추가하는형태(add() 메서드)가 아니라, 한번에 입력받아서 생성할 수는 없을까요?
그래야 불변을 유지하기 좋을 것 같네요

Copy link
Author

Choose a reason for hiding this comment

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

플레이어 이름과 베팅 금액을 한쌍으로 여러개를 전달받아야, 한번에 플레이어 여러명을 생성하고 Players 를 생성할 수 있습니다. 고민을 많이 해봤는데 플레이어를 생성하기 위한 PlayerCreateDto 를 컨트롤러에서 생성해서 BlackjackGame 에 전달하는 형태로 개선했습니다!

Comment on lines 37 to 42
Money totalProfit = Money.from(0);
for (Player player : value) {
totalProfit = totalProfit.add(player.calculateProfit(dealer));
}

return totalProfit;

Choose a reason for hiding this comment

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

이쪽은 스트림으로 분리할 수 있을 것 같아요. 분리하면 totalProfit 같은 가변변수를 줄일 수 있을 것 같네요.

Copy link
Author

Choose a reason for hiding this comment

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

return value.stream()
        .map(player -> player.calculateProfit(dealer))
        .reduce(Money.from(0), Money::add);

위와 같이 수정하였습니다 ! ㅎㅎ

public class Money {
private static final double BLACKJACK_PROFIT_RATE = 1.5;

private final int value;

Choose a reason for hiding this comment

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

금액을 사용하는거니 Decimal 타입을 사용해볼까요?

https://tecoble.techcourse.co.kr/post/2021-04-26-BigInteger_BigDecimal/ 이쪽을 참고해보면 좋을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

처음 들어본 클래스네요! 금액과 같이 정확한 계산이 필요한 경우 BigDecimal 을 사용해야 하는군요. 지식 공유 감사드립니다 😄

devHudi added 3 commits March 20, 2022 02:40
- compareWith 로직을 Participant 에서 Player 로 변경
- 테스트 코드 변경
- Controller 에 잘못 입력된 베팅금액에 대한 재입력 로직 추가
- getValue 를 getIntValue 로 변경
Comment on lines +49 to +53

public int getIntValue() {
return value.intValue();
}

Copy link
Author

Choose a reason for hiding this comment

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

value 의 타입을 int 에서 BigDecimal 로 변경하였으므로 명시적으로 메소드명에 'int' 라는 단어를 사용하였습니다.

Copy link

@hongbin-dev hongbin-dev left a comment

Choose a reason for hiding this comment

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

안녕하세요 후디!
2단계 미션 진행하느라 고생많으셨어요! 코드가 많이 깔끔해졌네요.
이만 머지하겠습니다 👏

Comment on lines +50 to +51
public int getIntValue() {
return value.intValue();

Choose a reason for hiding this comment

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

프로퍼티에 int가 명시되어있는데, 자료형을 메서드이름에 남길 필요가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

아 프로퍼티에도 명시되어 있고, 반환타입에도 명시가 되어 있군요!.. 수정해야할 것 같습니다!

Comment on lines +35 to +36
public Player generatePlayer(String playerName, int betMoney) {
return Player.of(playerName, generateInitialHand(), Money.from(betMoney));

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 +21
public void addPlayer(Player player) {
players.add(player);
}

Choose a reason for hiding this comment

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

사용하지 않는 메서드는 지우는게 좋겠네요 🙃

Copy link
Author

@devHudi devHudi Mar 22, 2022

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 +24
BlackjackGame blackjackGame = initializeGame();
printInitialHand(blackjackGame);
giveExtraCardsToAllPlayer(blackjackGame);
giveExtraCardsToDealer(blackjackGame);
printFinalHandAndScore(blackjackGame);
printBetResult(blackjackGame);

Choose a reason for hiding this comment

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

giveExtraCardsToAllPlayer(blackjackGame)

위 같은 메서드를 봤을 때, 코드 사용자 입장에서 파라미터로 넘어간 blackjackGame의 상태가 바뀔거라고 예상하지못하는데요. 이런 부분을 개선해보셔도 좋을 것 같네요.

Copy link
Author

Choose a reason for hiding this comment

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

저도 그 부분이 불편했었는데, 완벽히 blackjackGame 을 불변으로 만들거나 메서드명을 개선해야할 것 같습니다!..

@hongbin-dev hongbin-dev merged commit af0ace7 into woowacourse:devhudi Mar 21, 2022
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.

2 participants