Skip to content

[블랙잭 게임 1단계] 리뷰 요청 드립니다. #1

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

Open
wants to merge 55 commits into
base: chunghyeon-kim
Choose a base branch
from

Conversation

chunghyeon-kimm
Copy link

학습로그


1. StringUtils
  • StringUtils
    • StringUtils 클래스에 String을 다루는 여러가지 편리한 메서드가 정의되어 있다

2. Collectors
  • Collectors
    • Stream 최종연산 collect에서 사용하는 Collectors 클래스의 여러가지 메서드

3. Parameterized Test
  • Parameterized Test
    • @ParameterizedTest로 선언
    • @valuesource, @EnumSource, @MethodSource 등을 이용해서 인자를 주입한다
    • 테스트코드의 중복을 제거할 수 있다

chunghyeon-kimm and others added 30 commits July 25, 2021 12:55
Copy link

@KIMSIYOUNG KIMSIYOUNG left a comment

Choose a reason for hiding this comment

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

안녕하세요 충현님.

처음 블랙잭 구현할 때 룰도 너무 어렵고, 구현도 너무 어려웠던 기억이 있는데 잘 구현해주셨네요!

코멘트 몇 가지 남겼으니 확인 부탁드립니다🙏

ps. 블랙잭에 원래 배팅 금액 입력하고 수익률 구하는거도 있었지 않나요? 요번 미션에 없었나용

import java.util.List;

public class BlackJackController {
private final List<Player> players;

Choose a reason for hiding this comment

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

Controller를 Entry-point로써 사용하고 있는데, 현재와 같이 해당 값들을 이를 멤버변수로 가지게 되면 해당 게임을 여러명이 동시에 요청하는 경우 데이터 부정합이 발생할 수 있어요. 멤버변수를 지역변수로 가져가는 것이 좋아 보여요🙂

Copy link
Author

Choose a reason for hiding this comment

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

여러명이 동시에 요청하는 경우를 고려하지 않았네요. 서비스 개발자로서 항상 생각해야 할 부분인 것 같습니다.
view를 제외한 클래스변수들을 로컬변수로 수정했습니다.

}

public void run() {
drawTowCards();

Choose a reason for hiding this comment

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

메소드 분리가 잘 되어 있어서, 가독성이 좋네요🙂

}

private void drawCardToPlayer(Player player) {
DrawCardResponseDTO drawCardResponse = inputView.getPlayersResponse(player);

Choose a reason for hiding this comment

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

DTO도 자신이 가지고 있는 데이터에 대해서, 어떤 행위/판단을 할 수 있다고 생각해요. 입력받은 값이 Y/N인지 판단하는 로직은 DTO로 가도 된다고 생각합니다.

다만 해당 로직이 여러 곳에서 사용되거나, 이를 하나의 도메인 행위로 정의한다면 DTO -> Domain으로 전환한 다음 도메인에서 판단하는 것도 좋을 것 같아요. 현재는 해당 부분에 대한 도메인 로직이 Controller에 나와 있기에 원하시는 형태로 포장해주시면 좋을 것 같습니다🙂

추가로 네이밍은 팀바팀이고 사바사지만, 개인적으로 프로그램에게 넘어온 Input을 담는 DTO는 Request/우리 프로그램에서 반환해주는DTO는 Response로 사용하고 있어요. response로 작성해주신 맥락은 알겠으나, Client-Server가 항상 유동적으로 달라질 수 있다면 DTO가 많아졌을 때 헷갈릴 수 있다고 생각해요🙂 (추가로 DTO는 > Dto로 적고 있긴합니다 - 취향)

Copy link
Author

Choose a reason for hiding this comment

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

Dto 이름을 DrawCardRequestDto로 변경하고 판단로직을 Dto로 이동했습니다.
Dto가 가진 데이터만을 이용한 간단한 로직은 Dto에서 처리해도 된다는 것을 알게 되었습니다.

Dto 이름을 받아온 것인지, 반환하는 것인지에 따라 통일하면 일관성이 있어 헷갈릴 일이 없어서 좋은 방법인 것 같습니다.

.collect(Collectors.toList());
}

public static String validateYesOrNo(String response) {

Choose a reason for hiding this comment

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

Utils성 클래스는 일반적으로 정말 유틸리티한 것들만 기능을 담는 것이 일반적이에요. 현재 유틸로직이 Y/N이라는 어떤 룰에 대해서 너무 많이 알고 있는 상태인 것 같습니다

도메인 혹은 DTO의 로직으로 변경하는 것이 좋아보이고, Util에서 사용하신다면 검증 대상이 되는 Y/N도 파라미터로 받는다면 조금 더 범용적으로 사용할 수 있는 메소드가 될 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

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

로직을 DrawCardRequestDto로 이동했습니다.

private final List<Card> deck;

public Deck() {
this.deck = new LinkedList<>(DeckFactory.createDeck());

Choose a reason for hiding this comment

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

LinkedList로 선언하신 이유는 카드를 한장씩 삭제할 때 ArrayList가 비효율적이라서 그렇게 하신걸까요? 👏

Copy link
Author

Choose a reason for hiding this comment

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

넵 그렇습니다.

DEALER_HIGHER(((player, dealer) -> player.getCardsSum() < dealer.getCardsSum()), WinningResult.LOSE),

// 무승부 결정
TIES(((player, dealer) -> player.getCardsSum() == dealer.getCardsSum()), WinningResult.TIE);

Choose a reason for hiding this comment

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

요 로직들이 순서에 따라 영향을 받을 것 같아요. 원래는 플레이어가 버스트인 경우 항상 패배로 간주되는데 무승부 Enum을 맨 위로 올리면, 비긴거로 간주되는 형식으로여! 그렇다면 기존 Enum의 순서가 중요하다는 것을 명시적으로 드러내거나, 다른 방법을 사용해줘야 다른 사람이 실수할 여지를 줄일 수 있을 것 같아요ㅎㅎ(여긴 가장 중요한 로직이니까 더더욱이요!)

Copy link
Author

Choose a reason for hiding this comment

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

verificationOrder 라는 int 필드를 추가하고 RuleComparator클래스를 생성해서 비교하기 전에 enum을 정렬하도록 했습니다.

Rule enum 자체에서 comparable 인터페이스를 구현하고 compareTo 메서드를 오버라이딩 하려고 시도해봤는데
Enum은 상수를 선언한 순서대로 정렬되고, compareTo 오버라이딩이 불가능하다는 것을 알게 되었습니다.

https://stackoverflow.com/questions/519788/why-is-compareto-on-an-enum-final-in-java
참고했습니다.

PLAYER_BUST(((player, dealer) -> player.isBust()), WinningResult.LOSE),
DEALER_BUST(((player, dealer) -> dealer.isBust()), WinningResult.WIN),

// 카드 값의 합이 21을 넘지 않으면서, 값이 더 큰쪽이 승패결정(이미 여기서 결정, BlackJack 체크 필요없음)

Choose a reason for hiding this comment

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

친절한 주석 좋네요. 해당 메소드는 participant에게 값을 비교하는 형태로 리팩토링할 수 있을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

이 부분 이해를 못했습니다. 조금만 더 설명해주실 수 있을까요?

this.winningResult = winningResult;
}

public Boolean compare(Player player, Dealer dealer) {

Choose a reason for hiding this comment

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

해당 메소드는 비교하는 메소드가 아니라, 어떤 Rule인지를 찾는거에 가깝네요. 요것도 메소드 이름 수정해주시면 좋을 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

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

findMatchingRule로 수정했습니다.

this.result = result;
}

public WinningResult reverse() {

Choose a reason for hiding this comment

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

요거 제가 전에 할 때, 안 됐던건데 WinningResult가 WinningResult 멤버변수를 가지고, 반대로 선언하면 안되더라구요. 왜 안되는지 바로 이해가시면 Enum을 잘 이해하고 계신 것 같습니다ㅋㅋㅋ

WIN("승", LOST) // 요런식으로여

Copy link
Author

Choose a reason for hiding this comment

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

WIN을 정의하기 위해 LOST가 필요하고, 다시 LOST를 정의하기 위해 WIN이 필요하고... 이런식으로 무한반복되기 때문에 안 되는 것일까요 ? ㅎㅎ

제가 Enum에 아직 익숙하지 않아서 간단하게 설명해주시면 감사하겠습니다.

Choose a reason for hiding this comment

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

네넹ㅎㅎㅎ이해하신게 맞습니다. 윈을 로드하는 시점에 로스트가 정의되어있지 않기 때문에 발생하는 에러입니다ㅎㅎ

class GameResultTest {

@ParameterizedTest
@EnumSource(WinningResult.class)

Choose a reason for hiding this comment

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

EnumSource를 활용해주신 것은 좋은데, 요거보단 개인적으로 ArgumentProvider/CsvSource등을 통해, 카드들도 같이 명시해주는게 좋아보이는데, 어떠신가용?

Copy link
Author

Choose a reason for hiding this comment

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

Parameterized Test를 실행하는 다양한 방법들이 있다는 것을 알게 되었습니다.
말씀해주신 방법을 찾아봤는데 아직 어떻게 쓰는것인지 잘 모르겠습니다. 좀 더 찾아보고 구현해보겠습니다.

@chunghyeon-kimm
Copy link
Author

배팅 금액 관련된 부분은 2단계에서 구현하도록 되어있습니다. ㅎㅎ

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.

3 participants