Skip to content
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단계 - 블랙잭 베팅] 몰리(김지민) 미션 제출합니다. #728

Merged
merged 74 commits into from
Mar 18, 2024

Conversation

jminkkk
Copy link

@jminkkk jminkkk commented Mar 15, 2024

안녕하세요 파랑🌊
2단계를 진행하면서, 도메인에 대한 이해가 잘되지 않아 어려움이 있었던 것 같아요😭
중간에 리드미의 요구사항 분석을 많~이 잘못했었었다는 것을 느끼고 갈아엎고 열심히 다시 설계를 했습니다..ㅎㅎ

플레이어와 딜러의 관계

파랑의 피드백을 받고 플레이어와 딜러의 관계에 대해 다시 생각해보았어요. 제가 생각해본 방법과 의견은 아래와 같았어요.

  1. 딜러가 플레이어를 상속
    • 게임 안에서 딜러와 플레이어가 각각 다른 역할로 사용되고 있기 때문에 적절하지 않다.
  2. 딜러에 플레이어를 합성
    • 마찬가지로 역할이 다르기 때문에 딜러가 플레이어를 가지고 있다는 게 어색한 것 같다.
  3. 딜러와 플레이어가 Playable 인터페이스를 구현
    • 제가 생각하기에 인터페이스는 타입보다는 어떤 특징을 나타내게 할 때 좋을 것 같은데, 다른 역할이기 때문에 타입으로 정의를 하는 것이 조금 더 자연스러울 것 같다.
  4. 딜러와 플레이어가 Gamer 추상 클래스를 상속
  • 딜러와 플레이어가 Gamer의 한 종류라고 볼 수 있기 때문에 가장 적절한 것 같다.

결론으로, Gamer 추상 클래스를 통해 게임에 참여하는 모든 게이머가 가져야할 공통적인 역할을 묶고, 딜러와 플레이어가 게이머를 상속받게 하여 다른 역할에 대한 부분은 오버라이딩을 하는 식으로 진행하였습니다.

2단계도 잘 부탁드립니다 파랑!

jminkkk added 30 commits March 13, 2024 11:14
@jminkkk jminkkk changed the base branch from main to jminkkk March 15, 2024 08:42
Copy link

@summerlunaa summerlunaa 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 10 to 13
BUST((score -> score > BLACKJACK_SCORE), (cardSize -> cardSize >= BLACKJACK_CARD_SIZE)),
BLACKJACK((score -> score == BLACKJACK_SCORE), (cardSize -> cardSize == BLACKJACK_CARD_SIZE)),
NOT_BLACKJACK_BUT_21((score -> score == BLACKJACK_SCORE), (cardSize -> cardSize > BLACKJACK_CARD_SIZE)),
UNDER_21((score -> score < BLACKJACK_SCORE), (cardSize -> cardSize >= BLACKJACK_CARD_SIZE));

Choose a reason for hiding this comment

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

HandStatus를 Enum을 통해 관리하도록 했네요. 좋은 방법인 것 같아요 👍

Comment on lines 81 to 83
public boolean isUnder21() {
return getHandStatus().equals(HandStatus.UNDER_21);
}

Choose a reason for hiding this comment

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

사실상 여기서 블랙잭 기준이 21에서 다른 숫자로 변할 가능성은 극히 낮지만.. 상수를 메서드명이나 변수명에 넣으면 어떤 단점이 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

블랙잭 게임에 대해 이해도가 낮은 경우 21이라는 숫자에 대해 이해하기가 어려울 것 같아요 🥲
값이 변경된다면, 관련 메서드나 변수를 다 수정해야 해서 유지보수에도 좋지 않을 것 같네요!
변경하겠습니다 ㅎㅎ

import blackjack.model.deck.Card;
import java.util.List;

public abstract class Gamer {

Choose a reason for hiding this comment

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

결론으로, Gamer 추상 클래스를 통해 게임에 참여하는 모든 게이머가 가져야할 공통적인 역할을 묶고, 딜러와 플레이어가 게이머를 상속받게 하여 다른 역할에 대한 부분은 오버라이딩을 하는 식으로 진행하였습니다.

좋습니다 👍

Comment on lines -21 to -24
@Override
public void receiveCard(final Card card) {
this.hand = hand.addCard(card);
}

Choose a reason for hiding this comment

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

중복 코드가 많이 줄었네요 💯

Comment on lines 4 to 7
PLUS_150_PERCENT(1.5),
PLUS_100_PERCENT(1),
ZERO(0),
MINUS_100_PERCENT(-1)

Choose a reason for hiding this comment

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

여기도 숫자 값이 네이밍에 들어가있네요~

Comment on lines 22 to 25
for (Player player : playerGameResult.keySet()) {
Money profitMoney = calculateProfitMoney(player, playerGameResult.get(player));
playerProfitMoneys.put(player, profitMoney);
}

Choose a reason for hiding this comment

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

Suggested change
for (Player player : playerGameResult.keySet()) {
Money profitMoney = calculateProfitMoney(player, playerGameResult.get(player));
playerProfitMoneys.put(player, profitMoney);
}
for (Entry<Player, ResultCommand> entry : playerGameResult.entrySet()) {
Money profitMoney = calculateProfitMoney(entry.getKey(), entry.getValue());
playerProfitMoneys.put(entry.getKey(), profitMoney);
}

entry를 사용하면 map에서 get하는 로직을 없앨 수 있겠네욥.

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 +9 to +10
public class CardsFormatter {
public static String format(final List<Card> cards) {

Choose a reason for hiding this comment

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

몰리는 클래스를 어떤 기준으로 분리하고 있나요~? 각 객체별 Formatter는 어떤 이유로 만들게 되었는지 궁금합니다!

Copy link
Author

Choose a reason for hiding this comment

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

format하는 부분 때문에 OutputView가 너무 지저분해지는 것 같아 분리를 하게 되었습니다..!
Dto나 Vo별 분리를 하게 되면 delimiter나 포맷 형식이 변경되는 경우, 해당 부분에 대한 Formatter만 수정하면 될 것 같아 편할수도 있을 것 같다고 생각하기도 했구요..!

Choose a reason for hiding this comment

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

분리하신 이유는 알겠습니다~ 문자열을 포맷팅하는 부분 때문에 OutputView가 지저분해진다는 점에도 공감하구요.
다만 클래스를 분리하기 전, 클래스를 분리하여 관리하는 것도 비용이 든다는 점을 항상 염두에 두면 좋을 것 같습니다.
블랙잭 게임 정도의 요구사항에도 Formatter가 5개나 생성되었는데요, 여기서 도메인과 View가 더 복잡해지면 수십개의 Formatter 클래스가 생성될 것 같아요. 이런 상황에서 몰리는 Formatter 클래스들을 어떻게 관리할 것인지 궁금합니다 :-)

Copy link
Author

Choose a reason for hiding this comment

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

여기서 도메인과 View가 더 복잡해지면 수십개의 Formatter 클래스가 생성될 것 같아요. 이런 상황에서 몰리는 Formatter 클래스들을 어떻게 관리할 것인지 궁금합니다 :-)

당장에 드는 생각은... 도메인과 View가 더 복잡해지면 Formatter를 없애고 OutputView 자체를 분리해볼수도 있겠다는 생각이 듭니다..!
블랙잭에서는, 게임 진행에 대한 출력, 결과에 대한 출력 등등 처럼요..!! 좋은 아이디어인지는 확신이 들지는 않지만요🥲

파랑 말처럼 클래스를 어떻게 관리해야 할지에 대해서도 고려를 해야할 것 같아요...! 상황이나 규모에 맞게 적절하게 분리를 하는 것이, 어렵게 느껴지지만 중요한 것 같습니다...!

@@ -21,29 +25,42 @@ public class BlackJackGame {

Choose a reason for hiding this comment

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

ConsoleReader가 util에 있네요. util에는 어떤 클래스가 들어가나요?

Copy link
Author

Choose a reason for hiding this comment

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

전역적으로 사용되는 파일이 들어가야 할 거 같아요 🥲
reader는 view 에서만 사용되기 때문에 view 패키지 안에 존재하는 것이 맞을 것 같네요!
감사합니다 파랑! 🌊

validatePlayerSize(players);
this.players = players;
}

public static Participants of(final List<String> rawNames, final List<Hand> cards) {
public static Players of(final List<String> rawNames, final List<Hand> cards) {

Choose a reason for hiding this comment

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

Deck을 받으면 rawNames와 cards의 size가 같아야 한다는 관계에 신경쓰지 않고도 안전한 코드를 만들 수 있을 것 같네요!

Copy link
Author

Choose a reason for hiding this comment

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

편한 테스트를 위해 안전함을 조금 걸리지만 양보했었는데, 안전한 코드가 확실히 더 중요한 것 같아요.😂
좋은 피드백 감사합니다🔥

final Map<Player, Money> playerAndBettingMoney = moneyStaff.calculateProfitMoneys(playerResults);
final Money dealerProfit = moneyStaff.calculateDealerProfitAmount(
playerAndBettingMoney.values().stream().toList());
OutputView.printDealerProfit(dealer.getName(), dealerProfit);

Choose a reason for hiding this comment

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

dealer도 NameProfit을 사용할 수 있겠네요

Copy link
Author

Choose a reason for hiding this comment

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

넵 그렇게 하면 따로 printDealerProfit를 선언하지 않아도 될 것 같네요 감사합니다!!

@jminkkk
Copy link
Author

jminkkk commented Mar 17, 2024

좋은 피드백 감사합니다 파랑!🐬
이번 리뷰도 잘 부탁드립니다 🙏🙏

Copy link

@summerlunaa summerlunaa left a comment

Choose a reason for hiding this comment

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

몰리 안녕하세요~

리뷰를 꼼꼼히 잘 반영해주셨네요 👍

지금도 충분히 좋지만 리뷰 조금 더 남겨드렸으니 확인부탁드릴게요!

Comment on lines 25 to 27
return IntStream.range(0, rawNames.size())
.mapToObj(index -> new Player(new Name(rawNames.get(index)), cards.get(index)))
.collect(Collectors.collectingAndThen(Collectors.toList(), Participants::new));
}

private static void validateNamesInitialCardsSize(final List<String> rawNames, final List<Hand> cards) {
if (rawNames.size() != cards.size()) {
throw new IllegalArgumentException("플레이어 인원과 초기 카드 목록의 사이즈가 다릅니다.");
}
.mapToObj(index -> new Player(new Name(rawNames.get(index)), deck.distributeInitialCard()))
.collect(Collectors.collectingAndThen(Collectors.toList(), Players::new));

Choose a reason for hiding this comment

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

List<Hand> cards가 사라졌으니 IntStream.range는 사용하지 않아도 되겠네요. 가능하면 리스트에 index로 접근하는 방식은 피하는 것이 좋습니다.

Choose a reason for hiding this comment

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

Card 클래스에 대한 변경사항이 없어 여기 작성합니다.
Card의 종류는 총 몇가지 인가요? 만들어질 수 있는 Card의 수가 한정되어 있다면 반복적으로 불필요한 인스턴스 생성을 막기 위한 방법은 어떤 것이 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

카드의 종류가 고정되어 있기 때문에 미리 생성해두어도 좋을 것 같아요!
파랑 덕분에 캐싱에 대해 학습해볼 수 있었습니다 ㅎㅎ

추가로, 현재는 Deck에서 카드 목록을 생성하고 Shuffle한 후 Deck을 생성하여 반환하는데,
미리 카드 목록을 생성하는 것은 Cards에 두고 Deck은 Shuffle한 후 Deck 생성만 하게 두는 것이 좋을 것 같다는 생각이 들었습니다...!

좋은 피드백 감사합니다!

Comment on lines +9 to +10
public class CardsFormatter {
public static String format(final List<Card> cards) {

Choose a reason for hiding this comment

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

분리하신 이유는 알겠습니다~ 문자열을 포맷팅하는 부분 때문에 OutputView가 지저분해진다는 점에도 공감하구요.
다만 클래스를 분리하기 전, 클래스를 분리하여 관리하는 것도 비용이 든다는 점을 항상 염두에 두면 좋을 것 같습니다.
블랙잭 게임 정도의 요구사항에도 Formatter가 5개나 생성되었는데요, 여기서 도메인과 View가 더 복잡해지면 수십개의 Formatter 클래스가 생성될 것 같아요. 이런 상황에서 몰리는 Formatter 클래스들을 어떻게 관리할 것인지 궁금합니다 :-)

Copy link
Author

@jminkkk jminkkk left a comment

Choose a reason for hiding this comment

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

리뷰를 반영하면서 생각해보지 못했던 부분들에 대해 고민해볼 수 있었던 것 같아요!😁
이번에도 좋은 피드백 감사합니다 파랑🐬

Copy link
Author

Choose a reason for hiding this comment

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

카드의 종류가 고정되어 있기 때문에 미리 생성해두어도 좋을 것 같아요!
파랑 덕분에 캐싱에 대해 학습해볼 수 있었습니다 ㅎㅎ

추가로, 현재는 Deck에서 카드 목록을 생성하고 Shuffle한 후 Deck을 생성하여 반환하는데,
미리 카드 목록을 생성하는 것은 Cards에 두고 Deck은 Shuffle한 후 Deck 생성만 하게 두는 것이 좋을 것 같다는 생각이 들었습니다...!

좋은 피드백 감사합니다!

Comment on lines +9 to +10
public class CardsFormatter {
public static String format(final List<Card> cards) {
Copy link
Author

Choose a reason for hiding this comment

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

여기서 도메인과 View가 더 복잡해지면 수십개의 Formatter 클래스가 생성될 것 같아요. 이런 상황에서 몰리는 Formatter 클래스들을 어떻게 관리할 것인지 궁금합니다 :-)

당장에 드는 생각은... 도메인과 View가 더 복잡해지면 Formatter를 없애고 OutputView 자체를 분리해볼수도 있겠다는 생각이 듭니다..!
블랙잭에서는, 게임 진행에 대한 출력, 결과에 대한 출력 등등 처럼요..!! 좋은 아이디어인지는 확신이 들지는 않지만요🥲

파랑 말처럼 클래스를 어떻게 관리해야 할지에 대해서도 고려를 해야할 것 같아요...! 상황이나 규모에 맞게 적절하게 분리를 하는 것이, 어렵게 느껴지지만 중요한 것 같습니다...!

Copy link

@summerlunaa summerlunaa left a comment

Choose a reason for hiding this comment

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

몰리 안녕하세요~

리뷰 반영도 빠르게 해주셨네요 💯 깔끔하게 잘 완성해주셨습니다ㅎㅎ

블랙잭 미션은 여기서 머지하겠습니다. 수고하셨습니다 👏👏 👍

@summerlunaa summerlunaa merged commit 3612a20 into woowacourse:jminkkk Mar 18, 2024
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