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

[블랙잭 1단계] 포르 미션 제출합니다. #113

Open
wants to merge 44 commits into
base: jiyuneel
Choose a base branch
from

Conversation

jiyuneel
Copy link

@jiyuneel jiyuneel commented Mar 7, 2025

안녕하세요, 제이든!
우테코 7기 안드로이드 크루 포르입니다 :)
블랙잭 미션 리뷰 잘 부탁드립니다!! 🙇🏻‍♀️

셀프 체크리스트

  • 프로그램이 정상적으로 작동하는가?
  • 모든 테스트가 통과하는가?
  • 이전에 받은 피드백을 모두 반영하였는가?
  • 코딩 스타일 가이드를 준수하였는가?
    • IDE 코드 자동 정렬을 적용하였는가?
    • 린트 검사를 통과하였는가?
  • 프로그래밍 요구 사항을 준수하였는가?
  • README.md에 기능 목록을 정리하고 명확히 기술하였는가?

어떤 부분에 집중하여 리뷰해야 할까요?

객체 지향 설계

딜러와 플레이어의 공통 로직을 Participant 추상 클래스에 구현하고, DealerPlayer가 상속받도록 구현했습니다.
또한, 각 참가자가 가지고 있는 카드 List<Card>Hand 클래스로 구현하였습니다.

객체들의 역할을 생각하며 설계하고자 노력했는데, 적절한 역할을 하고 있는지 리뷰 부탁드립니다!

jiyuneel added 30 commits March 4, 2025 12:11
@KwonDae KwonDae self-requested a review March 7, 2025 08:41
Copy link

@KwonDae KwonDae left a comment

Choose a reason for hiding this comment

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

블랙잭 미션 고생하셨습니다.
각 객체간의 역할에 대해 많은 고민이 있었음을 느낄 수 있었어요!
객체간의 역할과 구조를 중점적으로 코멘트를 남겨드렸으니 확인 부탁드리며 어려운 부분은 언제든 편하게 DM 주세요!

Comment on lines 37 to 40
repeat(INITIAL_CARD_COUNT) {
dealer.addCard(Deck.pick())
players.dealCards()
}
Copy link

Choose a reason for hiding this comment

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

Controller에서 '몇번' 반복한다는걸 제어하는것보다
객체 스스로가 판단하는건 어떨까요?

현실세계에서도 게임 시작시 게임판이 참가자에게 두장을 주지 않습니다.
규정상 딜러가 덱에서 뽑아주지만, 딜러 또한 참가자이기에
참가자 스스로가 덱에서 가져오는것으로 생각할 수 있어요

Controller는 참가자에게 "덱에서 카드를 뽑아라" 라고 메세지만 던져보는건 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

controller를 게임의 중재자 또는 카지노라고 생각했습니다.

참가자가 처음에 뽑아야 하는 카드의 수(2장)는 게임의 룰에 해당하며,
이를 controller가 관리하는 것이 가장 바람직하다고 판단했습니다..!

참가자에게 카드를 뽑으라는 메시지를 두 번씩 던져서 참가자는 카드 뽑기의 주체가 되지만,
몇 번 뽑아야 하는지는 게임의 흐름을 조율하는 controller가 결정하는 것이 적절하다고 생각했는데 어떻게 생각하시나요..?!

companion object {
val cards: ArrayDeque<Card> = ArrayDeque(createShuffledDeck())

fun pick(): Card = cards.removeLast()
Copy link

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.

덱이 비어있는 경우에 예외를 던질지 고민했는데,
카드가 없는 것도 정상적인 게임 상황이라고 판단하고 게임을 결과를 출력하도록 했습니다!

덱이 비어있으면 null을 반환하고, controller에서 null을 확인하여 프로그램을 종료하고 결과를 출력합니다. (11aa7ab)

fun pick(): Card? = cards.removeLastOrNull()
private fun Participant.drawCardOrStop(deck: Deck): Card? {
    val card = deck.pick() ?: return null
    drawCard(card)
    return card
}

Comment on lines 70 to 71
val playerResult = players.calculateResult(dealer)
val dealerResult = getDealerResult(playerResult)
Copy link

Choose a reason for hiding this comment

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

Players는 Players 객체가 계산을 하고,
딜러는 Controller에서 계산을 해주고 있습니다.

Participant라는 추상클래스를 통해 Controller는 해당 객체가 Player, Dealer인지 관심없이
계산하도록 메세지만 던져볼 수 있지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

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

결과를 구하는 것은 Participant의 공통 역할이라고 판단했고,
DealerPlayer의 승패 판단 기준이 달라 각각의 클래스에서 구현하도록 변경했습니다! (b2d13a6)

abstract fun getResult(other: Participant): Result

다만 이 방식으로 구현하면서 몇 가지 의문이 들었는데, 제이든의 생각이 궁금합니다..!

  1. 플레이어는 승 or 패의 결과가 나오는 반면, 딜러는 모든 플레이어와 비교하여 n승 n패 형식의 결과가 나옵니다.
    그렇기에 controller에서 딜러의 결과를 계산하는 로직은 아직 남아있습니다.

  2. 변경되기 전의 코드는 모든 플레이어의 결과를 계산한 후, 그 결과를 바탕으로 딜러의 결과를 계산했습니다.
    플레이어의 결과에 따라서 딜러의 결과가 결정된다고 생각해서 이렇게 구현했었습니다.
    다른 참가자와 비교하여 결과를 계산하도록 변경하면서 동일한 비교를 두 번 하게 된 것 같습니다.
    예를 들어, 플레이어1과 딜러의 결과를 계산할 때 플레이어1의 입장에서도 비교하고 딜러의 입장에서도 비교하게 되는데,
    이러한 중복된 비교가 불가피한지 궁금합니다.

@jiyuneel jiyuneel force-pushed the step1 branch 2 times, most recently from 5fc4ffb to 6958d19 Compare March 8, 2025 19:52
@jiyuneel
Copy link
Author

jiyuneel commented Mar 8, 2025

안녕하세요, 제이든 😊
남겨주신 코멘트 모두 확인하면서 리팩터링을 진행했습니다. 덕분에 여러 부분에서 개선할 수 있었습니다!

꽤 많은 변화가 있었는데, 주요 변화된 내용은 다음과 같습니다.

  • 딜러와 플레이어의 공통 역할을 Participant에 추가했습니다.
  • ShuffledCardGenerator 객체를 통해 셔플된 카드 리스트를 생성하여 Deck을 초기화합니다.
  • 점수를 포장하는 Score 객체를 추가하였고, 이 객체에서 bust 여부를 판단합니다.

리팩터링을 진행하면서 고민했던 내용들을 코멘트로 남겼습니다.
확인 부탁드립니다! 🙏

Copy link

@KwonDae KwonDae left a comment

Choose a reason for hiding this comment

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

피드백 잘 반영해주셨어요!
주로 남겨주신 질문들로 코멘트를 남겨드렸으며
진행하시면서 어려운 부분은 편하게 DM 부탁드립니다 🙂

Comment on lines +50 to +57
players.players.forEach { player ->
while (player.canHit() && inputView.readPlayerHit(player)) {
player.drawCardOrStop(deck) ?: return
outputView.printPlayerCards(player)
}
if (!player.canHit()) {
outputView.printBust(player)
}
Copy link

Choose a reason for hiding this comment

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

#113 (comment)

입력받는 로직을 domain으로 옮겨야만 해결이 가능할까요?
hint. kotlin에는 고차함수라는게 존재합니다.

@@ -33,57 +34,71 @@ class BlackjackController(
private fun dealInitialCards(
dealer: Dealer,
players: Players,
deck: Deck,
) {
repeat(INITIAL_CARD_COUNT) {
Copy link

Choose a reason for hiding this comment

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

#113 (comment)

처음에 뽑아야 하는 카드의 수(2장)이 게임의 룰 이라면,
게임의 룰은 도메인 로직일까요 Controller의 로직일까요?

현재 게임의 룰 중 하나인 21점이라는 기준은 domain에 위치하고 있는데
처음에 뽑는 2장은 Controller에 위치하고 있습니다.
둘은 다른 취급이 되는 룰인걸까요? 🧐

BlackJackGame 이라는 도메인 모델이 존재한다면
보다 명확해질 수 있을까요?


fun addCard(card: Card) {
abstract class Participant(
Copy link

Choose a reason for hiding this comment

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

  • abstract class
  • open class
  • interface

현재 abstract class를 사용하신 이유는 어떤걸까요?
이 세가지는 어떤 차이점이 있을까요?

Comment on lines +81 to +92
val dealerResult =
players.players
.map { dealer.getResult(it.getScore()) }
.groupingBy { it }
.eachCount()
outputView.printDealerResult(dealer, dealerResult)
players.players.forEach {
outputView.printPlayerResult(
it.name,
it.getResult(dealer.getScore()),
)
}
Copy link

Choose a reason for hiding this comment

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

#113 (comment)

2 depth로 들어가는 players.players 으로도 일급컬렉션의 용도가 생기는 부분 같아요..!
만약 참가자들(딜러포함)을 관리하는 객체가 존재한다면, 해당 객체 내에서
현재 우려하시는 중복된 비교를 해결할 수 있겠네요..!
이부분은 시도하다가 어려우시면 편하게 DM주세요!

Comment on lines +95 to +99
private fun Participant.drawCardOrStop(deck: Deck): Card? {
val card = deck.pick() ?: return null
drawCard(card)
return card
}
Copy link

Choose a reason for hiding this comment

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

Participant 확장함수가 Controller 내부에 있는게 자연스러울까요? 🧐


fun isBust(): Boolean = hand.isBust()
abstract fun getResult(otherScore: Score): Result
Copy link

Choose a reason for hiding this comment

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

공통 로직 추상화 잘해주셨어요 👍

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