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단계 - 블랙잭 게임 실행] 몰리(김지민) 미션 제출합니다. #613

Merged
merged 122 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 96 commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
c917986
docs(README.md): 요구사항 명세 작성
jminkkk Mar 5, 2024
87c85ee
feat(Player): 이름을 가지는 플레이어 객체 생성
jminkkk Mar 5, 2024
155f357
feat(Players): 플레이어의 이름들을 받아서 플레이어의 그룹을 생성하는 객체 생성
jminkkk Mar 5, 2024
c3988bb
feat(Players): 플레이어의 인원 수를 검증하는 로직 작성
jminkkk Mar 5, 2024
ba08356
refactor(Players): 검증 메서드 추출
jminkkk Mar 5, 2024
af5cb26
feat(Player): 이름 입력 시 null 또는 빈 값이 들어오면 예외를 던지는 기능 구현
jminkkk Mar 5, 2024
e4da2a3
refactor(Players): 필요 없는 static 제거
jminkkk Mar 5, 2024
be71c02
feat(InputView): 입력받은 이름을 파싱하여 리스트 형태로 반환하는 로직 작성
jminkkk Mar 5, 2024
06ca65b
feat(Deck): 카드들 상수 Enum 추가
jminkkk Mar 5, 2024
9e75cf4
feat(Deck): 모든 카드들을 List로 반환하는 기능 추가
jminkkk Mar 5, 2024
08636d4
feat(Player): 플레이어가 카드를 받는 기능 구현
jminkkk Mar 5, 2024
a1792aa
feat(Dealer): 딜러가 카드를 더 받을 수 있는지 확인하는 기능 구현
jminkkk Mar 5, 2024
d0c4ff8
feat(Player): 자신이 가진 카드의 합을 계산하는 기능 구현
jminkkk Mar 5, 2024
f718ce7
refactor(Dealer): 카드를 더 받을 수 있을지에 대한 메서드에서 calculateScore() 사용으로 변경
jminkkk Mar 5, 2024
1e65d32
feat(Player): 카드의 총 점수를 계산할 때 ace가 여러개인 경우 1로 값을 변경하는 기능 구현
jminkkk Mar 5, 2024
8d96417
refactor(Player): ace 카드의 개수를 세는 기능 메서드 분리
jminkkk Mar 5, 2024
7229877
feat(Player): Ace의 기본값으로 결과를 계산했을 때 21 초과 시 Ace 값 변경하는 기능 구현
jminkkk Mar 5, 2024
362b317
feat(Cards): List<Deck>에 대한 일급 컬렉션 Cards 구현
jminkkk Mar 5, 2024
f83bbd6
feat(Players): 생성자 인자에 List<Cards> 추가
jminkkk Mar 5, 2024
795d9ed
feat(Player): 생성자 인자에 Cards 추가
jminkkk Mar 5, 2024
31e992d
feat(Dealer): 생성자 인자에 Cards 추가
jminkkk Mar 5, 2024
cb9f423
fix(Cards): cards를 받아 생성하는 경우 List에 새로운 값이 추가 안되는 버그 해결
jminkkk Mar 5, 2024
92cd701
feat(Cards): Cards 인스턴스 생성시 카드가 두 장 미만인 경우 예외를 던지는 기능 구현
jminkkk Mar 5, 2024
33b1a9c
refactor(Cards): Cards 테스트 코드 메서드 이름 변경
jminkkk Mar 5, 2024
6272dac
feat(Cards): 가장 앞에 있는 카드를 반환하는 기능 구현
jminkkk Mar 5, 2024
9056d88
feat(Dealer): 딜러 카드 공개 시 한 장의 카드만 공개하는 기능 구현
jminkkk Mar 5, 2024
d461956
refactor(Player): cards에 대한 getter 메서드 이름 변경
jminkkk Mar 5, 2024
bb73759
feat(Result): 결과를 나타내는 enum 생성
jminkkk Mar 5, 2024
fbfd65f
feat(Card, Score, Shape): 카드 한 장을 생성하는 Card 객체 및 카드의 모양과 숫자를 나타내는 Sha…
jminkkk Mar 6, 2024
e9d59ef
refactor(Deck): 전체 Card 목록을 가고 있는 Deck을 생성해 사용하도록 개선
jminkkk Mar 6, 2024
d5a2cbd
test(Player): 플레이어 생성시 카드 2장을 지급받지 않으면 예외를 던지는 테스트 작성
jminkkk Mar 6, 2024
5df2df5
refactor(Score, Cards): Ace의 기본값을 1로 변경 및 카드의 합계를 구하는 로직 변경
jminkkk Mar 6, 2024
a60b3b1
feat(Cards): 현재 점수가 Bust 상태인지를 확인하는 코드 작성
jminkkk Mar 6, 2024
9148da7
feat(Player): 현재 추가로 카드를 받을 수 있는 상태인지 확인하는 기능 구현
jminkkk Mar 6, 2024
41813fd
refactor(Dealer): 현재 추가로 카드를 받을 수 있는 상태인지 확인하는 메서드 명을 오버라이딩으로 변경
jminkkk Mar 6, 2024
081235f
feat(Referee): 플레이어 결과가 딜러보다 큰 경우 승리를 반환하는 기능 구현
jminkkk Mar 6, 2024
370b223
feat(Cards): 카드의 개수를 반환하는 로직 작성
jminkkk Mar 6, 2024
f25271f
feat(Player): 카드의 개수를 반환하는 로직 작성
jminkkk Mar 6, 2024
f00858e
feat(Player): 플레이어 결과가 딜러의 결과와 동일하지만 카드 수는 적은 경우 플레이어가 승리하는 기능 구현
jminkkk Mar 6, 2024
c3be17a
feat(Cards): 2개의 카드로 21을 맞추는 경우 블랙잭임을 알려주는 기능 구현
jminkkk Mar 6, 2024
dee5f8a
feat(Player): 2개의 카드로 21을 맞추는 경우 블랙잭임을 알려주는 기능 구현
jminkkk Mar 6, 2024
dddfacd
feat(Referee): 플레이어 카드만 블랙잭인 경우 플레이어가 승리하는 기능 구현
jminkkk Mar 6, 2024
0ca64fe
test(Referee): 플레이어의 카드 수가 딜러의 카드 수보다 적은 경우 플레이어가 승리하 테스트 작성
jminkkk Mar 6, 2024
ebaeb63
feat(Referee): 딜러가 Bust이고 플레이어가 Bust가 아닌 경우 플레이어가 승리하는 기능 구현
jminkkk Mar 6, 2024
b40b606
refactor(Cards, Player, Dealer): hit과 bust를 확인하는 메서드 분리
jminkkk Mar 6, 2024
81f81d1
feat(Referee): 플레이어와 딜러가 모두 블랙잭인 경우 무승부 기능 구현
jminkkk Mar 6, 2024
7edde19
feat(Referee): 플레이어와 딜러의 결과, 카드 수가 모두 동일한 경우 무승부 기능 구현
jminkkk Mar 6, 2024
8deb5b6
test(Referee): 플레이어와 딜러 모두 21 초과인 경우 무승부 기능 구현
jminkkk Mar 6, 2024
aff7d7f
feat(Referee): 딜러의 결과를 반환하는 기능 구현
jminkkk Mar 6, 2024
1a911a8
test(Referee): 플레이어들의 결과를 반환하는 테스트 코드 작성
jminkkk Mar 6, 2024
d2d0e11
feat(Players): 플레이어들의 이름이 중복되는 경우 예외를 던지는 기능 추가
jminkkk Mar 6, 2024
0a05fef
style(Player, README.md): 필요 없는 공백 및 기능 목록 제거
jminkkk Mar 6, 2024
6b230af
feat(InputView): 추가 선택 입력 시 y 또는 n이 아닌 경우 예외를 던지는 기능 구현
jminkkk Mar 6, 2024
88b115d
feat(InputView): 추가 선택 입력 시 blank인 경우 예외를 던지는 기능 구현
jminkkk Mar 6, 2024
b13e39b
refactor(Players): DisplayName 추가
jminkkk Mar 6, 2024
07ac6eb
refactor(InputView): 인스턴스 생성 시도할 경우 예외를 던지도록 개선
jminkkk Mar 6, 2024
a491955
feat(Deck): 생성한 카드들을 shuffle 하여 반환하도록 수정
jminkkk Mar 6, 2024
e771288
feat(Deck): 카드 한 세트(52장)을 다 소진할 경우 예외를 발생하는 기능 구현
jminkkk Mar 6, 2024
78234a5
feat(ConsoleReader): 사용자 입력을 받아오는 ConsoleReader 구현
jminkkk Mar 6, 2024
9550b82
refactor(Rule, Referee): Referee에서 딜러와 플레이어와 비교하여 결과를 생성하는 기능을 Rule로 분리
jminkkk Mar 7, 2024
480f725
fix(Deck): 게임 생성 시 마다 Deck을 생성하여 실행하도록 로직 변경
jminkkk Mar 7, 2024
bea1d9b
test(Deck): 인원 수만큼 카드 목록을 생성하는지 확인하는 테스트 작성
jminkkk Mar 7, 2024
f947195
test(Deck):deck에서 가장 앞의 카드를 반환하는지 확인하는 테스트 코드 작성
jminkkk Mar 7, 2024
9de64e7
refactor(Player): 필요 없는 메소드 삭제
jminkkk Mar 7, 2024
37bae4e
refactor(Dealer): 딜러가 초기 카드를 공개할 때 List 형태로 반환하도록 변경
jminkkk Mar 7, 2024
8872685
feat(Card): Card의 Score와 Shape를 리턴하는 getter 추가
jminkkk Mar 7, 2024
3a73079
feat(Deck): 초기 카드를 나누어주는 메서드 분리
jminkkk Mar 7, 2024
d05b394
feat(BlackJackGame): 블랙잭 게임의 컨트롤러 생성
jminkkk Mar 7, 2024
50cda73
feat(Players): 플레이어들의 이름 목록을 반환하는 기능 구현
jminkkk Mar 7, 2024
518fb3c
feat(OutputView): 참가자들에게 초기 카드를 배분하는 코멘트 출력 기능 구현
jminkkk Mar 7, 2024
2c47b06
feat(OutputView): 딜러의 초기 카드를 출력하는 기능 추가
jminkkk Mar 7, 2024
cd033ea
feat(Players): 플레이어의 이름과 카드들을 모아서 반환하는 기능 구현
jminkkk Mar 7, 2024
3184b39
feat(BlackJackGame): 플레이어의 카드들을 출력하는 기능 추가
jminkkk Mar 7, 2024
38e2836
feat(ScoreDisplay, ShapeDisplay): Score, Shape의 view 출력 display를 만드는 …
jminkkk Mar 7, 2024
f5daf94
feat(BlackJackGame): 게임을 진행하는 기능 구현
jminkkk Mar 7, 2024
9ff8c1d
feat(BlackJackGame): 배분받은 카드를 출력하는 기능 개선
jminkkk Mar 7, 2024
9e84d39
feat(BlackJackGame, NameCardsAScore): 플레이어들의 이름과 카드, 스코어를 출력하는 기능 구현
jminkkk Mar 7, 2024
981aaad
feat(BlackJackGame): 플레이어의 이름과 최종 승패를 출력하는 기능 구현
jminkkk Mar 7, 2024
487c06b
feat(BlackJackGame): 딜러의 최종 승패를 출력하는 기능 구현
jminkkk Mar 7, 2024
cd42ce3
feat(Application): main 추가
jminkkk Mar 7, 2024
9aac0f1
feat(NameCardsScore): 파일 이름 오타 수정
jminkkk Mar 8, 2024
5949c9c
refactor(Players): 정적 팩토리 메서드의 이름을 of로 수정
jminkkk Mar 8, 2024
f1389a7
feat(OutputView): 생성자의 접근 제한자를 private로 설정
jminkkk Mar 8, 2024
c612623
refactor(Hand): 플레이어가 가지고 있는 카드 목록을 의미하는 Cards 클래스의 이름을 Hand로 변경
jminkkk Mar 8, 2024
16b5e83
refactor(src/): 패키지 구조화
jminkkk Mar 8, 2024
b4b5b1c
refactor(OutputView): 출력 형식에 맞게 수정
jminkkk Mar 8, 2024
aa6a173
fix(BlackJackGame): 카드 합계가 21인 경우 추가 입력을 받지 않도록 수정
jminkkk Mar 8, 2024
34f9546
refactor(Rule): 플레이어와 딜러가 동일한 점수일 때 카드의 개수로 비교하는 메서드 분리
jminkkk Mar 8, 2024
4fbdda7
refactor(BlackJackGame): 최종 카드들과 합, 승패를 출력하는 메서드 분리
jminkkk Mar 8, 2024
ee2730b
update(README.mnd): 구현한 요구사항 체크
jminkkk Mar 8, 2024
ff0fdf5
rename(Referee): findOpposite 메서드 이름 changeToOppositeResultCommand로 변경
jminkkk Mar 8, 2024
4897520
refactor(Hand): 상수 추출
jminkkk Mar 8, 2024
64628eb
refactor(Player): 메서드 순서 조정
jminkkk Mar 8, 2024
ca7b504
refactor(Referee): EnumMap 선언 시 선언 부분의 타입을 Map으로 수정
jminkkk Mar 8, 2024
d873ea1
refactor(test/): @BeforeEach로 중복 코드 제거 및 불필요한 import 제거
jminkkk Mar 8, 2024
2d5723d
refactor(Application): 예외 발생 시 예외 메시지를 출력하도록 처리
jminkkk Mar 8, 2024
a741ccd
refactor(test/display/): 인자에 해당하는 출력 문자를 반환하는 메서드에 get() 사용을 orElseTh…
jminkkk Mar 9, 2024
bf0fa4b
refactor(OutputView): 서식 지정자 출력을 위한 String.format 사용을 printf로 대체
jminkkk Mar 9, 2024
cc2ae77
refactor(Deck): 생성자 대신 정적 팩토리 메서드로 랜덤순서인 Deck을 생성하도록 수정
jminkkk Mar 9, 2024
f587f55
refactor(DealerTest): 경계값으로 테스트하도록 카드 값 변경
jminkkk Mar 9, 2024
5de9b62
refactor(BlackJackGame): run 메서드의 길이 축소 및 메서드 추출
jminkkk Mar 9, 2024
6cda451
docs(README.md): 요구사항에 21인 경우 턴을 넘긴다에 대한 명세 추가
jminkkk Mar 9, 2024
fa3c1d3
refactor(Name): name 원시값 포장
jminkkk Mar 10, 2024
0cbce41
refactor(ConsoleReader): 상태가 변경될 수 있는 scanner를 상수가 아닌 변수명으로 수정
jminkkk Mar 10, 2024
5c215ad
refactor(display/): 출력 문자를 매칭하기 위한 enum에서 표시값을 displayName으로 변수명 변경
jminkkk Mar 10, 2024
5513d52
refactor(DealerTest): 불필요한 import 제거 및 hand를 지역변수로 변경
jminkkk Mar 10, 2024
4255ba8
refacotor(Player): 사실상 getter인 메서드 네이밍 수정
jminkkk Mar 10, 2024
a868728
refactor(Players): 이름 중복 검사를 정적 팩토리 메서드 단에서 처리
jminkkk Mar 10, 2024
2bc11ba
feat(Players): 플레이어 인원과 초기 카드 목록의 사이즈가 다른 경우 예외 추가
jminkkk Mar 10, 2024
2895e6a
refactor(Players): stream을 사용하여 코드 변경
jminkkk Mar 10, 2024
fbb3b76
refactor(Player): equals, hashCode() 제거
jminkkk Mar 11, 2024
c8c4e31
refactor(Player, Hand): 21에 대한 상수 의미를 정확하게 사용하도록 책임 변경
jminkkk Mar 11, 2024
ff4890e
refactor(Players): Dealer가 아닌 Player들에 대한 일급 컬렉션을 이름 변경
jminkkk Mar 11, 2024
85e709f
refactor(CardTest): 목적이 분명하지 않는 테스트 제거
jminkkk Mar 11, 2024
abc48aa
test(Dealer): 딜러는 16 초과면 카드를 추가로 받을 수 없다에 대한 테스트 추가
jminkkk Mar 11, 2024
d136d50
refactor(BlackJackGame): 플레이어의 턴 체크의 조건을 Hit이 가능한가로 변경
jminkkk Mar 11, 2024
4898bc0
refactor(Participants): dto 변환 위치 변경
jminkkk Mar 11, 2024
62bccf8
refactor(DeckTest): distribute() 테스트 시 앞의 카드를 반환하는가가 아닌 카드를 반환하고 해당 카…
jminkkk Mar 11, 2024
05bc1a8
refactor(view/): view 및 Game에 원시값으로 전달하던 이름값을 Name 객체로 전달
jminkkk Mar 12, 2024
2a987dc
fix(Name): name 출력 시 getRawName()로 문자열 출력하도록
jminkkk Mar 12, 2024
0365aaf
refactor(Playable): Player 인터페이스 생성 및 Player와 Dealer가 구현하도록 수정
jminkkk Mar 12, 2024
e83abda
refactor(Playable): Playable에 구현체들의 중복 메서드 추가
jminkkk Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## 기능 요구 사항

- [x] 참여할 사람의 이름을 입력받는다.
- [x] 참여할 인원의 수는 최소 1명 최대 10명이다.(딜러 제외)

- [x] 게임을 시작하면 플레이어는 딜러에게 두 장의 카드를 지급 받는다.
- [x] 딜러는 2장의 카드를 받고 한 장의 카드만 공개한다.

- [x] 카드를 더 받을지 선택한다.
- [x] 21이 넘지 않은 플레이어는 추가로 카드를 더 받을지 선택할 수 있다.
- [x] 21이 초과된 경우 알리고 다음 플레이어의 턴으로 넘어간다.
- [x] 카드 추가 여부 확인 시마다 해당 플레이어의 카드 현황을 보여준다.
- [x] 카드를 더 받지 않으면 다음 플레이어에게 턴을 넘긴다.
- [x] 모든 플레이어가 선택을 마칠 때까지 위의 과정을 반복한다.

- [x] 딜러는 처음에 받은 2장의 합계가 16이하이면, 17이상이 넘을 때까지 카드를 추가로 받는다.

- [x] 딜러와 플레이어의 카드, 결과를 공개한다.
- [x] 딜러와 각 플레이어 별로 승패를 공개한다.

### 플레이어

- [x] 플레이어는 카드를 받을 수 있다.

### 플레이어의 승리 조건

#### 딜러가 21미만인 경우

- [x] 플레이어 결과가 딜러보다 큰 경우
- [x] 플레이어 결과가 딜성러의 결과와 동일하지만 카드 수는 적은 경우

#### 딜러가 21인 경우

- [x] 플레이어 카드만 블랙잭인 경우
- [x] 플레이어의 카드 수가 딜러의 카드 수보다 적은 경우

#### 딜러가 21 초과했을 경우

- [x] 플레이어 결과가 21이하인 경우

### 무승부 조건

- [x] 플레이어와 딜러 모두 블랙잭인 경우
- [x] 플레이어와 딜러의 결과, 카드 수가 모두 동일한 경우
- [x] 플레이어와 딜러 모두 21 초과인 경우

### 카드

- [x] 전체 카드의 수는 52장이다.
- [x] 카드의 종류와 개수는 아래 사진과 같다.
![image](https://github.com/jminkkk/java-blackjack/assets/102847513/5e4a056f-136d-429d-b9b5-6819c2684726)

- [x] Ace의 기본값은 1이다.
- [x] Ace를 여러 개 가진 경우 ACE는 1점으로 계산한다.
- [x] Ace를 제외한 나머지 카드의 합계가 10을 초과하면 ACE는 1점으로 계산한다.
- [x] Ace를 제외한 나머지 카드의 합계가 10 이하인 경우 ACE는 11점으로 계산한다.
- [x] King, Queen, Jack은 각각 10으로 계산한다.

---

## 예외처리

- [x] 이름 입력 시 blank가 들어온 경우
- [x] 이름의 수가 1개 미만인 경우
- [x] 이름의 수가 10개 초과인 경우성
- [x] 이름이 중복되는 경우

- [x] 추가 선택 입력 시 blank가 들어온 경우
- [x] 추가 선택 입력 시 y 또는 n이 아닌 경우
12 changes: 12 additions & 0 deletions src/main/java/blackjack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package blackjack;

public class Application {
public static void main(String[] args) {
try {
BlackJackGame game = new BlackJackGame();
game.run();
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
97 changes: 97 additions & 0 deletions src/main/java/blackjack/BlackJackGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package blackjack;

import blackjack.dto.NameCardsScore;
import blackjack.model.participant.Dealer;
import blackjack.model.deck.Deck;
import blackjack.model.participant.Player;
import blackjack.model.participant.Players;
import blackjack.model.result.Referee;
import blackjack.model.result.ResultCommand;
import blackjack.model.result.Rule;
import blackjack.util.ConsoleReader;
import blackjack.view.InputView;
import blackjack.view.OutputView;
import java.util.List;
import java.util.Map;

public class BlackJackGame {
private static final ConsoleReader CONSOLE_READER = new ConsoleReader();

public void run() {
final Deck deck = new Deck();

final List<String> names = InputView.readPlayerNames(CONSOLE_READER);
final Players players = Players.of(names, deck.distributeInitialCard(names.size()));

final Dealer dealer = new Dealer(deck.distributeInitialCard());
final Referee referee = new Referee(new Rule(dealer), players);

OutputView.printDistributionSubject(players.getNames());
printInitialCards(dealer, players);

playPlayersTurn(players.getPlayers(), deck);
playDealerTurn(dealer, deck);

printFinalResult(dealer, players, referee);
}

Choose a reason for hiding this comment

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

  • 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.
    • 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라.


private void printInitialCards(final Dealer dealer, final Players players) {
OutputView.printNameAndCards(dealer.getName(), dealer.openCard());
players.collectCardsOfEachPlayer()
.forEach(OutputView::printNameAndCards);
OutputView.println();
}

private void playPlayersTurn(final List<Player> players, final Deck deck) {
for (Player player : players) {
playPlayerTurn(player, deck);
}
}

private void playPlayerTurn(final Player player, final Deck deck) {
if (!player.isBust() && player.canHit()) {

Choose a reason for hiding this comment

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

player.canHit()이 true면 bust는 당연히 아니지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

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

player의 점수가 정확히 21인 경우 때문에 canHit를 검사해주었습니다.
21은 bust는 아니지만 더 이상 hit도 할 수 없는 상태이라고 생각했습니다!

Choose a reason for hiding this comment

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

넵넵 정확히 21인 경우도 canHit에서 걸러주고 있기 때문에 player.canHit()만 있어도 정상 동작하겠네요.

final boolean isHit = InputView.readHitOrNot(player.getName(), CONSOLE_READER);
distributeIfPlayerWant(isHit, player, deck);
}
}

private void distributeIfPlayerWant(final boolean isHit, final Player player, final Deck deck) {
if (isHit) {
distributeNewCard(player, deck);
OutputView.printNameAndCards(player.getName(), player.openCards());
playPlayerTurn(player, deck);
}
}

private void playDealerTurn(final Dealer dealer, final Deck deck) {
while (dealer.canHit()) {
OutputView.printDealerHit();
distributeNewCard(dealer, deck);
}
}

private void distributeNewCard(final Player player, final Deck deck) {
player.receiveCard(deck.distribute());
}

private void printFinalResult(final Dealer dealer, final Players players, final Referee referee) {
printFinalCardsAndScores(dealer, players);
printFinalResultCommand(referee);
}

private void printFinalCardsAndScores(final Dealer dealer, final Players players) {
OutputView.println();
NameCardsScore dealerNameCardsScore = new NameCardsScore(dealer.getName(), dealer.openCards(),
dealer.notifyScore());
List<NameCardsScore> playerNameCardsScore = players.collectFinalResults();
OutputView.printFinalCardsAndScore(dealerNameCardsScore);
OutputView.printFinalCardsAndScore(playerNameCardsScore);
}

private void printFinalResultCommand(final Referee referee) {
Map<ResultCommand, Integer> dealerResults = referee.judgeDealerResult();
OutputView.printDealerFinalResult(dealerResults);
Map<String, ResultCommand> playerResults = referee.judgePlayerResult();
OutputView.printFinalResult(playerResults);
}
}
7 changes: 7 additions & 0 deletions src/main/java/blackjack/dto/NameCardsScore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package blackjack.dto;

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

public record NameCardsScore(String name, List<Card> cards, int score) {
}
46 changes: 46 additions & 0 deletions src/main/java/blackjack/model/deck/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package blackjack.model.deck;

import java.util.Objects;

public class Card {
private final Shape shape;
private final Score score;

public Card(final Shape shape, final Score score) {
this.shape = shape;
this.score = score;
}

public boolean isAce() {
return score.isAce();
}

public int getScoreValue() {
return score.getValue();
}

public Score getScore() {
return score;
}

public Shape getShape() {
return shape;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Card card = (Card) o;
return shape == card.shape && score == card.score;
}

@Override
public int hashCode() {
return Objects.hash(shape, score);
}
}
59 changes: 59 additions & 0 deletions src/main/java/blackjack/model/deck/Deck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package blackjack.model.deck;

import blackjack.model.participant.Hand;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Deck {
private final Deque<Card> deck;

public Deck() {
this.deck = makeDeck();
}

Choose a reason for hiding this comment

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

deck의 기능에 맞는 자료구조를 사용하셨네요 💯

기본 생성자를 사용하는 대신 랜덤으로 초기화해서 객체를 생성한다는 의미를 담은 정적팩토리메서드를 사용해보는 것은 어떨까요?


private static Deque<Card> makeDeck() {
List<Card> originDeck = Arrays.stream(Shape.values())
.flatMap(Deck::matchScore)
.collect(Collectors.toList());
return new ArrayDeque<>(shuffleDeck(originDeck));
}

private static Deque<Card> shuffleDeck(final List<Card> originDeck) {
Collections.shuffle(originDeck);
return new ArrayDeque<>(originDeck);
}

private static Stream<Card> matchScore(Shape shape) {
return Arrays.stream(Score.values())
.map(score -> new Card(shape, score));
}

public Hand distributeInitialCard() {
return new Hand(List.of(distribute(), distribute()));
}

public List<Hand> distributeInitialCard(final int playerCount) {
return IntStream.range(0, playerCount)
.mapToObj(i -> distributeInitialCard())
.toList();
}

public Card distribute() {
try {
return deck.removeFirst();
} catch (NoSuchElementException e) {
throw new NoSuchElementException("카드가 부족합니다.");
}
}

public Deque<Card> getDeck() {
return deck;
}
Comment on lines +60 to +62

Choose a reason for hiding this comment

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

테스트에서만 사용하는 getter네요 👀

Copy link
Author

Choose a reason for hiding this comment

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

파랑! 이 부분과 관련해서 궁금한 점이 있어요!
그동안 TDD를 하면서, 한번씩 테스트에서만 사용되는 메서드들이 있었는데요 🥲

제가 생각하기에 정말 반드시 테스트가 필요한 부분이라고 생각되면 프로덕션 코드에서 사용되지 않더라도 남겨두고, 크게 중요하지 않는 부분이라고 생각되면 테스트를 제거해 버렸었습니다.

예시로, 위의 getDeck()를 남겨둔 이유는 getDeck()이 2가지를 테스트 하고 있는데,

  1. 배분 시에 가장 위에 카드부터 배분하는지
  2. 52개의 카드로 이루어져있는지

1번 같은 경우는 꼭 위의 카드가 배분되지 않더라도 프로그램 동작 자체에는 문제가 없기 때문에 반드시 테스트가 필요하지는 않지만,
2번은 하나의 덱은 사이즈가 52로 고정적이고 (요구사항이 변경된 경우 다른 카드집을 사용하게 되어 꼭 52는 아닐 수 있지만 고정적일 것이라고 생각했어요) 추가로 사이즈가 52개라는 것은 모든 shape과 score에 대해 1:1로 잘 매칭되었다 까지 예상할 수 있기 때문에 테스트가 반드시 필요한 부분이라고 생각했어요.

그래서 조금의 찝찝함을 감수하더라도 getDeck()을 남겨두게 되었는데요..!


말이 조금 길어졌지만 😂
결론적으로, 파랑에게 반드시 테스트가 필요한 부분에서 생겨진, 프로덕트에서는 사용하지 않는 메서드는 어떻게 해야하는지에 대해 물어보고 싶어요!

저는 3가지 방법을 생각해볼 수 있을 것 같아요...!

  1. 메서드를 제거
  2. 테스트를 제거
  3. 다른 프로덕션에 있는 메서드로 대체하여 테스트 (대체가 가능하다면)

Choose a reason for hiding this comment

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

중요한 로직에 대한 테스트이고 대체가 어렵다면 getter 정도는 남겨도 괜찮다고 생각하긴 합니다. 테스트만을 위한 코드를 최소화하되, 항상 맥락을 고려해야 하니까요.

}
32 changes: 32 additions & 0 deletions src/main/java/blackjack/model/deck/Score.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package blackjack.model.deck;

public enum Score {
ACE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10),
KING(10),
QUEEN(10),
JACK(10),
;

private final int value;

Score(final int value) {
this.value = value;
}

public boolean isAce() {
return this == ACE;
}

public int getValue() {
return value;
}
}
9 changes: 9 additions & 0 deletions src/main/java/blackjack/model/deck/Shape.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package blackjack.model.deck;

public enum Shape {
SPADE,
DIA,
CLOVER,
HEART,
;
}
21 changes: 21 additions & 0 deletions src/main/java/blackjack/model/participant/Dealer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package blackjack.model.participant;

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

public class Dealer extends Player {

Choose a reason for hiding this comment

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

Dealer와 Player, Players의 관계가 어떻게 되나요?
Dealer가 Player를 상속받는다는 것은 Dealer가 Player다 즉, Player라 하면 Dealer일 수도 있는 것인데 다른 코드들을 보니 Dealer와 Player를 마치 별개의 클래스처럼 사용하고 있네요. 어떻게 생각하시나요?

Copy link
Author

Choose a reason for hiding this comment

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

맞아요...!
처음 Dealer가 Player를 상속받도록 구현한 이유 중 하나가 Dealer 또한 특수한 형태의 Player라고 생각했기 때문이에요!
또한 Players는 Dealer가 제외된 Player에 대한 일급 컬렉션이에요...!

Players라는 이름만 보기에는 오해가 발생할 수 있는 것 같아요🥲
수정하도록 하겠습니다!

Choose a reason for hiding this comment

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

단순히 이름의 문제가 아닙니다. Dealer도 Player의 한 종류라면 Player가 쓰이는 모든 곳에는 Dealer도 똑같이 들어갈 수 있어야 합니다. 지금 그렇게 동작하고 있나요? 두 개가 별개의 클래스로 사용되어야 하며 Player가 쓰이는 모든 곳에 Dealer도 함께 쓰일 수 없다면 둘은 상속 관계가 되어서는 안 됩니다.

private static final int HITTABLE_THRESHOLD = 16;

public Dealer(final Hand hand) {
super("딜러", hand);
}

@Override
public boolean canHit() {
return hand.calculateScore() <= HITTABLE_THRESHOLD;
}

public List<Card> openCard() {
return List.of(hand.getFirstCard());
}
Comment on lines +52 to +54
Copy link
Author

Choose a reason for hiding this comment

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

파랑의 의견이 궁금한 부분이 있어요 🥹
이 부분을 구현하면서 페어와 의견이 조금 갈렸었는데요.

저는 Hand로부터 카드를 다 받아 숨길 부분을 제거하고 나머지를 공개하도록 한다. 공개할지 말지 조차 결정하는 것은 Dealer의 행동이다. 라고 생각했어요.
페어는 반대로 어차피 요구사항에 의해 한장만 공개를 할 것인데 애초에 한 장만 받아오는 것이 맞는 것 같다는 의견이였어요.

이 상황에 대해 파랑은 어떻게 생각하시는지 궁금합니다...!

Choose a reason for hiding this comment

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

어쨌거나 오픈했을 때 한 장만 보이게 한다는 요구사항만 충족한다면 어떤 방식으로 구현할지는 개발자의 몫이라고 생각합니다 😅 페어의 의견을 반드시 따를 필요는 없습니다. 본인이 좋다고 생각하는 방향으로 구현해보시죠~~

}
Loading