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단계 - 블랙잭 베팅] 가콩(최가빈) 미션 제출합니다 #891

Open
wants to merge 13 commits into
base: gabean13
Choose a base branch
from

Conversation

gabean13
Copy link

@gabean13 gabean13 commented Mar 15, 2025

안녕하세요. 가콩입니다 😁
이번 리뷰도 잘 부탁드립니다 감사합니다

체크 리스트

  • 미션의 필수 요구사항을 모두 구현했나요?
  • Gradle test를 실행했을 때, 모든 테스트가 정상적으로 통과했나요?
  • 애플리케이션이 정상적으로 실행되나요?
  • 프롤로그에 셀프 체크를 작성했나요?
    https://prolog.techcourse.co.kr/studylogs/4040

객체지향 생활체조 요구사항을 얼마나 잘 충족했다고 생각하시나요?

1~5점 중에서 선택해주세요.

  • 1 (전혀 충족하지 못함)
  • 2
  • 3 (보통)
  • 4
  • 5 (완벽하게 충족)

선택한 점수의 이유를 적어주세요.

3개 이상의 인스턴스 변수를 가진 클래스를 만들었습니다. (Player)

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

1. 딜러와 플레이어 / 상속 vs 조합

이 부분은 1단계 이후에 크루들과 토론을 가장 많이 했던 부분입니다.
기존에는 dealer와 player가 Participant를 상속받고, 아래에 서로 다른 메서드와 필드를 추가로 구현했습니다.

이후 블랙잭 피드백1에서
상속은 반드시 하위 클래스가 상위 클래스의 '진짜' 하위 타입인 상황에서만 쓰여야 한다.
상속이 적절한 경우란 언제일까? 클래스의 행동을 확장(extend)하는 것이 아니라 정제(refine)할 때다. 확장이란 새로운 행동을 덧붙여 기존의 행동을 부분적으로 보완하는 것을 의미하고 정제란 부분적으로 불완전한 행동을 완전하게 만드는 것을 의미한다.
이라는 말을 봤고, 여기에 공감하여 코드를 수정하였습니다.
공감한 이유로는

  • 기존의 Participant를 활용한 다형성을 전혀 활용하지 못하고 있었음 (카드를 받을 때만 사용)
  • 현재 딜러와 참가자가 한가지 부모를 상속받을 만큼 유사한가 -> NO
  • is-a가 아니라 has-a에 가까움

그래서 저는 조합과 상속을 다시 고민하였습니다.
하지만 이 당시에 조합을 사용하면 결국 player와 dealer내부에 중복되는 코드가 생길 것이라고 판단하였고 이게 단점이라고 생각해서 결국 저는 상속을 하기 위해 딜러를 재 정의했습니다.
딜러는 참가자다.라는 말이 맞도록 dealer에게서 Deck 필드를 제거했습니다.
또한 player의 name을 제거하고 Participant에 name필드를 추가하였습니다.
두 클래스모두 override만 하고 추가적인 public 메서드나 내부 필드를 가지지 않도록 리팩토링하였습니다. 2ce0373, 6c84c4a

그렇게 하고 step2를 시작하였습니다.
배팅 기능을 구현하면서 dealer와 player가 제가 아무리 정의를 다시 내려도 둘은 본질적으로 너무 다르다는 생각이 들었습니다. 점점 상속을 위해서 억지로 현실과 동떨어지는 세상을 프로그래밍하고 있다는 느낌을 받아서 저는 조합을 사용하기로 했습니다.

  • dealer와 player모두 패(hand)를 가지고 있다. 라는 말은 충분히 현실에서도 객체지향 세계에서도 납득이 가능한 사실이라고 생각합니다.
  • 코드가 겹치는 것은 그냥 어쩔 수 없는 부분이라고 생각하기로 했습니다. 상속을 끌고가며 딜러에게 name필드와 betAmount (배팅금액)이라는 필드를 제공하는 것이 더 큰 단점이라고 판단했습니다.

그래서 조합을 사용하게 코드를 다시 리팩토링하였습니다. 6d2c964
이런 고민을 하면서 TDD와 커밋이 조금 엉망으로 구현된 것 같습니다.ㅠㅠ 죄송합니다.
그리고 제 생각에 대해서 어떻게 생각하시는지 궁금합니다. 리뷰어님은 이런 상황이라면 조합 vs 상속 어떤 선택을 하셨을지 궁금합니다.

2. Betting 구현할 때 플레이어가 배팅 금액을 가지고 있기 vs 외부에서 betting 관리하기

저는 플레이어가 배팅 금액을 가지고 있게 구현을 하였습니다.
그 이유로는

  • 배팅 금액을 플레이어가 가지고 있는게 현실과 유사하다고 생각했습니다.
  • 플레이어가 게임에 참가하기 위한 필수적인 조건으로 이름 & 배팅 금액 이 필요하다고 생각했기 때문에, 플레이어 생성 시 이를 체크하고 그렇지 않은 참가자는 생성하지 않도록 하고 싶었습니다.

하지만 외부에서 배팅을 관리하는 것(Map<Player, BetAmount> 필드를 가지는 클래스)에 대해서 가장 큰 장점으로는 확장성과 책임 분리를 지킬 수 있다는 것입니다. 저는 플레이어의 무결함을 더 지키고 싶어서 첫번째 방식을 사용했는데, 리뷰어님의 생각은 어떤지 궁금합니다.

3. MatchResult / BetAmount / Profit

과한 책임분리인가 싶은 느낌도 듭니다.
현재는

  • MatchResult enum : 블랙잭,승, 패, 무 판단 + 블랙잭인 경우 1.5배, 승인 경우 1배 등 profitRate에 대한 필드를 가짐
  • BetAmount class : 플레이어의 배팅 금액을 가진 클래스, 플레이어의 필드에 존재하며 배팅금액에 대한 검증 수행
  • Profit class : 정적 생성자로 MatchResult, BetAmount 를 인자로 받아서 Profit을 넘기는 클래스

입니다. Profit클래스의 책임이 너무 적은 것 같다고 생각이 들기도 합니다. 또한 MatchResult에서 ProfitRate를 가지고 있으면서 수익률 계산은 또 Profit에서 하는게 책임이 분산되어있다는 느낌이 들기도 합니다. 차라리 Profit을 없애고 matchresult에서 수익률을 계산하고 그냥 int를 반환하는 메서드를 만들까 하는 고민도 했습니다.
하지만 객체의 이름이 주는 명확함이 있다고 생각하여 MatchResult에서 profit을 반환하지 않고 Profit의 정적생성자로 넘겼는데 혹시 너무 과한 책임분리인지 여쭙고 싶습니다.ㅠㅠ

gabean13 added 13 commits March 12, 2025 14:52
- canHit() 히트가 가능한 여부를 조사하도록 변경
- hit 관련 함수형 인터페이스 삭제, 컨트롤러에서 처리하도록 변경
- 변경된 코드에 따른 테스트 코드 수정
- 플레이어가 블랙잭으로 이긴 경우 WIN -> BLACKJACK 반환하도록 수정
- 배팅 금액을 가집니다.
- 1000원 단위여야하고, 1,000 ~ 1,000,000 원 까지 가능합니다.
- 수익률을 가집니다.
- 승패 결과에 따른 수익률을 계산합니다.
- 키 : 플레이어 값 : 배틍 금액을 가집니다.
- 결과에 따른 수익률을 반환합니다.
- Participant 추상 클래스 삭제
- Hand 클래스 추가, 조합으로 구조 변경
- 이에 따른 테스트 코드 변경
- Player 생성할 때, 이름과 수익률을 DTO로 받아와서 저장하도록 변경
- BetAmount를 가지고, 수익률 계산 시 Profit 반환
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.

1 participant