-
Notifications
You must be signed in to change notification settings - Fork 464
[2단계 - 블랙잭(베팅)] 후디(조동현) 미션 제출합니다. #369
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
Conversation
…서 실행하도록 구조 개선 - 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 메소드를 구현함
| 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) { |
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.
Participant 와 Player 는 별개의 클래스이고, Participant 가 가지고 있는 두개의 필드 (playerName, hand)는 private 으로 Player 측에서 접근하지 못하므로 Player 의 인스턴스 변수는 하나라고 생각했습니다.
| static { | ||
| // TODO: 2 Depth 인가?... | ||
| cache = Stream.of(CardSymbol.values()) | ||
| .flatMap(symbol -> Stream.of(CardRank.values()) | ||
| .map(rank -> new Card(rank, symbol))) | ||
| .collect(Collectors.toSet()); | ||
| } |
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.
이 코드를 for 문으로 작성할 경우 깊이가 2가 되어버려, Stream 을 2중으로 사용했는데요, 결국 조삼모사가 아닌가 싶어요! 앨런의 의견이 궁금합니다!
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중으로 나눠도 괜찮은 것 같아요.
|
|
||
| public List<Card> getInitialCards() { | ||
| return cards.subList(0, INITIAL_CARDS_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.
이번 미션의 경우 시작과 없음 등을 의미하는 0 이라는 숫자는 별도로 상수로 분리하지 않았습니다. 상수로 분리하지 않은 0 의 경우 해당 코드 자체가 길지 않고, 메소드명으로 충분이 0 이 어떤 역할을 하는지 유추할 수 있을 것 이라고 생각했는데요, 앨런의 의견이 궁금합니다!
hongbin-dev
left a comment
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단계 미션 고생하셨어요~ 구조가 많이 바뀌었군요 😮
몇 가지 코멘트 남겼으니 확인해주세요
| static { | ||
| // TODO: 2 Depth 인가?... | ||
| cache = Stream.of(CardSymbol.values()) | ||
| .flatMap(symbol -> Stream.of(CardRank.values()) | ||
| .map(rank -> new Card(rank, symbol))) | ||
| .collect(Collectors.toSet()); | ||
| } |
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중으로 나눠도 괜찮은 것 같아요.
| return initialScore.equals(Score.valueOf(Score.MAXIMUM_SCORE)); | ||
| } | ||
|
|
||
| protected ResultType compareWith(Participant other) { |
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.
| protected ResultType compareWith(Participant other) { | |
| protected ResultType compareWith(Dealer other) { |
other에 Player가 들어가도 무관한가요?
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.
딜러가 플레이어와 비교할 경우가 있을 것이라고 생각해서 이렇게 구현했는데, 생각해보니 Dealer 로 타입을 제한해도 될 것 같아요. 수정했습니다 :)
| if (other.isBlackjack()) { | ||
| return ResultType.DRAW; | ||
| } | ||
| if (other.isBusted()) { | ||
| return ResultType.WIN_WITH_BLACKJACK; | ||
| } | ||
| return ResultType.WIN_WITH_BLACKJACK; |
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.
| 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; |
이렇게 간소화할 수 있지 않을까요?
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.
앗, 그렇군요 수정했습니다 😓
| if (isBlackjack()) { | ||
| return getResultTypeIfBlackjack(other); | ||
| } | ||
|
|
||
| if (isBusted()) { | ||
| return getResultTypeIfBusted(other); | ||
| } | ||
|
|
||
| return getResultTypeIfOrdinary(other); |
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.
상태별로 나눠놓으니 가독성이 좋군요👍
| if (other.isBlackjack()) { | ||
| return ResultType.LOSE; | ||
| } | ||
| if (other.isBusted()) { | ||
| return ResultType.DRAW; | ||
| } | ||
| return ResultType.LOSE; |
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.
| if (other.isBlackjack()) { | |
| return ResultType.LOSE; | |
| } | |
| if (other.isBusted()) { | |
| return ResultType.DRAW; | |
| } | |
| return ResultType.LOSE; | |
| return ResultType.LOSE; |
플레이어가 버스트면 항상 패배입니다
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 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); | ||
| } |
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.
하나씩 추가하는형태(add() 메서드)가 아니라, 한번에 입력받아서 생성할 수는 없을까요?
그래야 불변을 유지하기 좋을 것 같네요
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.
플레이어 이름과 베팅 금액을 한쌍으로 여러개를 전달받아야, 한번에 플레이어 여러명을 생성하고 Players 를 생성할 수 있습니다. 고민을 많이 해봤는데 플레이어를 생성하기 위한 PlayerCreateDto 를 컨트롤러에서 생성해서 BlackjackGame 에 전달하는 형태로 개선했습니다!
| Money totalProfit = Money.from(0); | ||
| for (Player player : value) { | ||
| totalProfit = totalProfit.add(player.calculateProfit(dealer)); | ||
| } | ||
|
|
||
| return totalProfit; |
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.
이쪽은 스트림으로 분리할 수 있을 것 같아요. 분리하면 totalProfit 같은 가변변수를 줄일 수 있을 것 같네요.
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.
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; |
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.
금액을 사용하는거니 Decimal 타입을 사용해볼까요?
https://tecoble.techcourse.co.kr/post/2021-04-26-BigInteger_BigDecimal/ 이쪽을 참고해보면 좋을 것 같아요
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.
처음 들어본 클래스네요! 금액과 같이 정확한 계산이 필요한 경우 BigDecimal 을 사용해야 하는군요. 지식 공유 감사드립니다 😄
- compareWith 로직을 Participant 에서 Player 로 변경 - 테스트 코드 변경
- Controller 에 잘못 입력된 베팅금액에 대한 재입력 로직 추가 - getValue 를 getIntValue 로 변경
|
|
||
| public int getIntValue() { | ||
| return value.intValue(); | ||
| } | ||
|
|
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.
value 의 타입을 int 에서 BigDecimal 로 변경하였으므로 명시적으로 메소드명에 'int' 라는 단어를 사용하였습니다.
hongbin-dev
left a comment
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단계 미션 진행하느라 고생많으셨어요! 코드가 많이 깔끔해졌네요.
이만 머지하겠습니다 👏
| public int getIntValue() { | ||
| return value.intValue(); |
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.
프로퍼티에 int가 명시되어있는데, 자료형을 메서드이름에 남길 필요가 있을까요?
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 Player generatePlayer(String playerName, int betMoney) { | ||
| return Player.of(playerName, generateInitialHand(), Money.from(betMoney)); |
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 void addPlayer(Player player) { | ||
| players.add(player); | ||
| } |
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.
앗, 미처 확인하지 못했습니다. 지울게요 😓
| BlackjackGame blackjackGame = initializeGame(); | ||
| printInitialHand(blackjackGame); | ||
| giveExtraCardsToAllPlayer(blackjackGame); | ||
| giveExtraCardsToDealer(blackjackGame); | ||
| printFinalHandAndScore(blackjackGame); | ||
| printBetResult(blackjackGame); |
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.
giveExtraCardsToAllPlayer(blackjackGame)
위 같은 메서드를 봤을 때, 코드 사용자 입장에서 파라미터로 넘어간 blackjackGame의 상태가 바뀔거라고 예상하지못하는데요. 이런 부분을 개선해보셔도 좋을 것 같네요.
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.
저도 그 부분이 불편했었는데, 완벽히 blackjackGame 을 불변으로 만들거나 메서드명을 개선해야할 것 같습니다!..
안녕하세요, 앨런! 이번 피드백도 잘 부탁드려요!
1단계 코멘트에 대한 답변과 추가 질문은 이전 PR에 남겨두었습니다! (#273)
이전 두 미션보다 이번 미션이 훨씬 오래 걸렸습니다. 도메인 설계를 몇번을 엎은지를 모르겠네요. 🥲
금요일 강의 때 상태 패턴을 사용한 설계에 대해서 배우게 되었는데, 적용해보고 싶었으나 일단 지금 해온 설계를 마무리 해보고싶어서 아쉽게도 상태 패턴을 적용해보지는 못했습니다.
인스턴스 멤버를 2개로 제한하는 요구사항덕분에 도메인 설계에서 참 오래 걸렸던 것 같아요.
추가적인 질문이나 이야기해보고 싶은 내용은 코멘트로 작성할게요 🙇 즐거운 주말 보내시길!
아쉬운 점
Players,ResultCount등을 불변 객체로 만들면 더 좋았을텐데, 일정의 문제로 가변 객체로 둔 것이 아쉽습니다.