- 저장소: https://github.com/yoondgu/java-blackjack/tree/step2
- 코드리뷰 진행 PR: https://github.com/woowacourse/java-blackjack/pull/525
기능 요구사항
블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다.
- 플레이어는 게임을 시작할 때 배팅 금액을 정해야 한다.
- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다.
- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. 단, 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다.
- 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다.
- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리해 베팅 금액을 받는다.
구현하며 고민한 내용
추가된 요구사항은 복잡하지 않았지만, 1단계의 연장선상에서 리팩터링하며 고민을 많이 했던 것 같다.
컨트롤러의 Dealer, Player 참조를 허용
- 1단계에서는 변경 가능한 객체인 Dealer와 Player를 컨트롤러 단까지 꺼내고 싶지 않아서 이름을 기준으로 BlackJackGame 클래스와 메시지를 주고받는 방식으로 진행했다.
- 그러나 컨트롤러가 변경 가능한 도메인을 아는 것 <-> 모르는 상태에서 발생하는 코드 중복, 비효율적 로직 을 비교해보았을 때 전자가 훨씬 낫다고 판단했다.
- 새로운 요구사항으로 인해 베팅 금액이라는, 초기 세팅이 필요한 상태가 추가되었는데, 이를 컨트롤러에서 바로 생성자로 해당 객체들을 만든 뒤 BlackJackGame 클래스에 주입해줌으로써 setter 사용을 지양할 수 있었다.
수익 계산 데이터 타입
- "1.5배"를 해주는 계산이 존재하므로, 수익 정보는 소수점을 가진 수로 표현되어야 한다.
하지만 소수점 계산은 오차 발생의 문제가 있다. - 따라서 처음에는 수익 계산 시 아예 소수점을 버리는 정책을 스스로 정해서, 정수 계산을 하도록 했다.
하지만 수익 배분인 만큼 소수점까지도 정확하게 계산하는 것이 더 적절하다고 생각해,
정밀한 소수점을 다루는 BigDecimal 타입을 적용해보았다.
코드리뷰 피드백:
도이 말대로 프로젝트에서 정한 정책에 따라 달라지겠지만 제 의견을 말해보자면... 화폐마다 스탠다드하게 쓰이는 단위를 따라가면 되지 않을까 싶네요. 제가 현재 진행중인 프로젝트의 경우 통화 단위가 소수 10자리 수까지 쓰이고 있거든요ㅋㅋ 뭐든 간에 정책을 확실히 정해놓으면 됩니다. 이런 세세한 부분까지 생각해보는 자세 좋습니다. 👍👍
코드리뷰에서 배운 것들
- 커밋이 잘 나누어져 있어 읽기 편했다는 피드백을 받았다!
- 리팩터링 목록을 작성하고, 각 항목 별로 커밋을 하는 게 좋은 방법이었던 것 같다.
한 번씩만 쓰이는 문자열도 모두 상수 처리를 하는 것이 좋을까?
모두 상수 처리 했을 때의 장점과 단점을 정리해보았다.
장점: 코드 라인 수가 아주 많아졌을 때, 사용한 문자열이 어느 메서드를 통해 출력됐는지 찾기 용이하다.
단점: 딱 한 번씩 쓰이고 그 자체가 의미를 담고 있는 문자열이라면 출력하는 위치에서 바로 확인하는 게 더 코드를 읽기 쉽다.
이러한 장단점을 두고, 취향 차이인 영역으로 넘겨야 할지 모르겠어서 리뷰어님의 의견을 들어보았다.
리뷰어님은 "상수를 한 눈에 모아 확인할 수 있기 때문에, 한번만 쓰이더라도 상수화를 하는 게 맞다고 생각한다"고 하셨다.
코드리뷰 피드백:
저는 한번씩 쓰이더라도 상수화를 하는 게 맞다고 생각해요. 어떤 목적의 문자열인지 변수명으로 확인할 수 있다는 장점도 있지만 상수를 한 눈에 모아 확인할 수 있거든요.
또한, 현재 에러 메세지를 illegalArgumentException에서 문자열을 직접 주입해서 넣어주고 있는데, hasMessageContaining에서도 이 문자열을 검사하고 있죠. 만약 에러 메세지를 변경할 일이 생기면 상수화를 해둔 경우 한군데만 고치면 되지만 직접 주입하면 두 군데, 혹은 그 이상을 바꿔줘야 할 수도 있습니다. 그러니 상수화를 해두면 유지보수가 편리해지겠죠?
에러 메시지가 변경될 경우까지 고려하니 내가 생각한 장점 이상으로 상수 처리를 하고 이를 하나의 클래스에서 관리했을 때의 장점이 크다는 걸 깨달았다.
미션 피드백 강의에서 배운 것들
- 상태 패턴
- 블랙잭 게임은 카드를 받은 뒤 변경되는 상태의 종류가 여러 가지가 있고, 상태에 따라 다르게 행동해야 한다는 특징이 있다. 이러한 특징에 대해 디자인 패턴 중 하나인 상태 패턴을 적용해 구현할 수 있다.
- 게임 규칙을 객체로 추상화하고 다형성을 이용함으로써, 많은 조건문을 줄일 수 있다.
- 상태 패턴에 대해서는 마침 전날 테코톡에서 듣고 처음 알게 되었는데, 네오의 강의를 보며 감탄했던 기억이 .. 🥹
- 게임이니까 당연히 "Player"를 먼저 생각하고 구현했었는데 히트/스테이/버스트 상태를 가지는 객체라니! "객체 지향적으로 생각"하는 것이 무엇인지 아직은 공부할 부분이 많구나 느꼈다.
- 블랙잭 게임은 카드를 받은 뒤 변경되는 상태의 종류가 여러 가지가 있고, 상태에 따라 다르게 행동해야 한다는 특징이 있다. 이러한 특징에 대해 디자인 패턴 중 하나인 상태 패턴을 적용해 구현할 수 있다.
반응형
'우아한테크코스 > 미션 기록' 카테고리의 다른 글
[레벨1] 체스 3, 4단계 - 승패 및 점수, DB 적용 (0) | 2023.04.14 |
---|---|
[레벨1] 체스 1, 2단계 - 체스판 초기화, 말 이동 (0) | 2023.04.14 |
[레벨1] 블랙잭 1단계 - 블랙잭 게임 실행 (0) | 2023.04.08 |
[레벨1] 사다리 타기 2단계 - 사다리 게임 실행 (0) | 2023.04.07 |
[레벨1] 사다리 타기 1단계 - 사다리 생성 (0) | 2023.04.07 |