Skip to content
Open
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
60a21e8
docs: 프로젝트 소개글 및 구현 기능 목록 작성
Libienz Mar 5, 2024
2f32bca
feat: 카드의 모양 및 점수 이넘 생성
Libienz Mar 5, 2024
49be956
feat: 소유한 카드 합 계산 구현
Libienz Mar 5, 2024
768674f
feat: 카드를 추가하는 기능 구현
Libienz Mar 5, 2024
6a9b3a8
feat: Card 동등성 비교 및 해싱 함수 재정의
Libienz Mar 5, 2024
e5ccb9c
feat: CardDeck 카드를 뽑을 수 있는 기능 구현
Libienz Mar 5, 2024
2f8d112
test: 특정 카드 추가 테스트 의미 개선
Libienz Mar 6, 2024
fa90a65
feat: 카드의 수가 충분하지 않은 경우 pop메서드의 예외처리 구현
Libienz Mar 6, 2024
888c475
feat: 플레이어 이름 구현
Libienz Mar 6, 2024
b28e233
feat: 플레이어 구현
Libienz Mar 6, 2024
2978839
test: 테스트 도메인 설명 작성
Libienz Mar 6, 2024
c100676
test: 테스트 편의 픽스처 구현
Libienz Mar 6, 2024
3e432f8
feat: Player 도메인 동등성 비교 기능 구현
Libienz Mar 6, 2024
9f6b009
feat: Player의 일급 컬렉션 도메인 Players 구현
Libienz Mar 6, 2024
d4e3b02
feat: Player 관련 도메인 Getter 프로퍼티 추가
Libienz Mar 7, 2024
3628610
feat: Player가 가지고 있는 핸드 Getter 프로퍼티 추가
Libienz Mar 7, 2024
ccacf3d
feat: Card 게터 프로퍼티 추가
Libienz Mar 7, 2024
43b3f9f
feat: Player가 자신이 가지고 있는 카드의 합계를 반환할 수 있는 기능 추가
Libienz Mar 7, 2024
19e1ebb
feat: InputView 구현
Libienz Mar 7, 2024
96f1d18
feat: 출력 메시지 리졸버 구현
Libienz Mar 7, 2024
d324ce2
feat: OutputView 구현
Libienz Mar 7, 2024
d13d6ed
docs: 기능 구현 목록 정리 및 최신화
Libienz Mar 7, 2024
12d5f01
feat: DrawDecision 구현
Libienz Mar 7, 2024
e26f327
feat: 이름만 받아서 빈핸드를 가진 참가자들을 생성하는 정적 팩토리메서드 구현
Libienz Mar 7, 2024
e368348
feat: InputMapper 구현
Libienz Mar 7, 2024
0e340ad
feat: 정해진 카드 구성으로 덱을 만드는 Creator 구현
Libienz Mar 7, 2024
b706148
feat: 카드 숫자가 에이스에 해당하는 숫자인지 확인하는 기능 추가
Libienz Mar 7, 2024
0776cfd
feat: 카드가 에이스인지 확인하는 기능 추가
Libienz Mar 7, 2024
0f130c8
test: 카드 숫자가 에이스에 해당하는 숫자인지 검증하는 기능 테스트 추가
Libienz Mar 7, 2024
94c37a7
fix: 딜러 핸드 합계 구하는 메서드 수정
Libienz Mar 7, 2024
5b1551f
feat: 핸드에서 에이스 카드가 몇개인지 세는 기능 구현
Libienz Mar 7, 2024
65f58a3
feat: Judge가 핸드로부터 21에 가장 가까운 값을 구하는 기능 구현
Libienz Mar 7, 2024
04003e5
feat: Hand가 버스트 되었는지 판별하는 기능 구현
Libienz Mar 7, 2024
45002da
feat: 딜러 Hit 동작 구현
Libienz Mar 7, 2024
96f58be
feat: 딜러 핸드 게터 구현
Libienz Mar 7, 2024
f38f988
refactor: 딜러 관련 로직 파라미터 개선
Libienz Mar 7, 2024
9819609
feat: 플레이어의 승패 결정 로직 구현
Libienz Mar 7, 2024
7b36a34
feat: 딜러 게임 결과 데이터 클래스 작성
Libienz Mar 7, 2024
2336438
feat: Players 플레이어 수 반환하는 기능 구현
Libienz Mar 7, 2024
89a7c36
feat: Judge 딜러의 승패를 결정하는 로직 구현
Libienz Mar 7, 2024
2f8a47b
feat: 최종 승패 출력 로직 구현
Libienz Mar 7, 2024
32cc01a
feat: CardNumber 열거형 필드 추가
Libienz Mar 8, 2024
287ab8c
feat: Card의 숫자 이름을 반환하는 기능 구현
Libienz Mar 8, 2024
5579914
fix: 카드의 숫자가 아닌 카드 이름을 사용하도록 뷰로직 변경
Libienz Mar 8, 2024
022b04a
fix: 완성된 카드 결과 출력시 플레이어의 이름과 함께 출력하도록 뷰로직 수정
Libienz Mar 8, 2024
02706a3
refactor: 플레이어들의 점수 출력시 점수를 계산하지 않고 전달받도록 개선
Libienz Mar 8, 2024
e9620e6
feat: 컨트롤러 구현
Libienz Mar 8, 2024
e7dec0c
feat: 어플리케이션 진입점과 컨트롤러 연결
Libienz Mar 8, 2024
101e550
refactor: Player에서 사용되지 않는 메서드 제거 개선
Libienz Mar 8, 2024
83be362
refactor: 패키지 구조 세분화 개선
Libienz Mar 8, 2024
3e42598
feat: Score 클래스 구현
Libienz Mar 8, 2024
9cdd4e4
feat: ScoreCalculator 구현
Libienz Mar 8, 2024
4127be1
style: ScoreCalculator -> ScoreCalculateStrategy 클래스 이름 수정
Libienz Mar 8, 2024
f45b82e
refactor: Player가 전략을 전달받아 점수를 계산하는 기능 구현
Libienz Mar 8, 2024
59f6a93
refactor: 포장한 Score를 전달받아 뷰를 생성하도록 파라미터 개선
Libienz Mar 8, 2024
b0ecfde
refactor: 메서드 분리 개선 및 점수 계산 책임을 Judge에서 이전
Libienz Mar 8, 2024
e5eb87d
test: 테스트 이전으로 인한 삭제
Libienz Mar 8, 2024
b8e2c04
refactor: 히트 가능 책임을 Judge에서 이전
Libienz Mar 8, 2024
3fbdbcc
test: 책임 분리로 인해 유의미하지 않은 테스트 제거
Libienz Mar 8, 2024
7e748c6
refactor: 히트 할 수 있는지 여부를 결정하는 전략 Judge로부터 분리
Libienz Mar 8, 2024
db772d4
feat: 점수가 파라미터보다 높은지 확인하는 기능 구현
Libienz Mar 8, 2024
7ebe119
feat: 점수간 비교 기능 구현
Libienz Mar 8, 2024
087b133
refactor: 점수 비교 승패 판정 로직 도메인에게 물어보도록 개선
Libienz Mar 8, 2024
e68b09a
fix: 승패 판정, 카드 분배 문제 해결
Libienz Mar 8, 2024
fcf9aa7
refactor: Judge 메서드들 책임 분리 개선
Libienz Mar 8, 2024
a1b8d4b
fix: 딜러 초기 핸드를 한장만 노출하도록 뷰로직 수정
Libienz Mar 8, 2024
16de5b7
refactor: 누락된 딜러 전체 카드 출력 메세지 구현
Libienz Mar 8, 2024
eaa623f
refactor: 사용하지 않는 클래스 제거 개선
Libienz Mar 8, 2024
1b9d5ba
fix: 공백 뷰로직 수정
Libienz Mar 8, 2024
02ee533
docs: 체크리스트 최신화
Libienz Mar 8, 2024
663bc28
test: 반복되는 테스트 데이터 생성 fixture이용하도록 개선
Libienz Mar 8, 2024
38e466f
refactor: 메서드 순서 배치 개선
Libienz Mar 8, 2024
f86a043
feat: Deck에서 Hand를 생성하는 HandCreator 구현
Libienz Mar 8, 2024
f7593b5
fix: CardDeck에서 카드를 뽑을 때 불변리스트를 반환하지 않도록 수정
Libienz Mar 8, 2024
563bedf
feat: 게임 규칙에 맞게 플레이어를 생성하는 기능 구현
Libienz Mar 8, 2024
a8d1b14
refactor: PlayerCreator로 부터 플레이어와 딜러를 초기화하도록 개선
Libienz Mar 8, 2024
60d08c4
feat: Players 클래스에 특정 점수 이하인 플레이어를 세는 기능 구현
Libienz Mar 8, 2024
3589843
refactor: 구현한 기능을 이용하도록 책임 이전 개선
Libienz Mar 8, 2024
10e20b8
refactor: 사용하지 않는 파라미터 제거 개선
Libienz Mar 8, 2024
5728ff5
refactor: 책임 이전으로 사용하지 않는 메서드 제거 개선
Libienz Mar 8, 2024
867fcb4
feat: Hand가 자신의 채점 기준을 속성으로 가지도록 구현
Libienz Mar 8, 2024
a6b3eb0
feat: Hand가 자신을 채점하는 기능 구현
Libienz Mar 8, 2024
b6a027a
refactor: 책임 이전 개선 적용
Libienz Mar 8, 2024
530c89c
refactor: 점수를 플레이어를 통해 계산하도록 개선
Libienz Mar 8, 2024
4abbc51
refactor: Hand가 Hit전략을 속성으로 가지도록 개선
Libienz Mar 8, 2024
ee6ca01
refactor: Player에게 히트할 수 있는지 직접 물어보도록 개선
Libienz Mar 8, 2024
86de5c1
refactor: 람다 개선
Libienz Mar 8, 2024
2fd301b
refactor: inline개선
Libienz Mar 8, 2024
a794e12
test: 수정된 도메인에 따라 fixture 변경
Libienz Mar 8, 2024
b27538d
style: 우테코 스타일 포매팅 적용
Libienz Mar 8, 2024
7360053
docs: 카드합 및 점수 관련 기능 구현 목록 추가
Libienz Mar 9, 2024
2318ce5
refactor: 컨트롤러 레이어 삭제 및 관련 클래스 이름 변경 개선
Libienz Mar 9, 2024
40f1c66
refactor: 불필요한 클래스 참조 제거 개선
Libienz Mar 9, 2024
1379611
refactor: 에러메시지 구체화 개선
Libienz Mar 9, 2024
d87d04f
refactor: 도메인 내에 뷰로직 제거 개선
Libienz Mar 9, 2024
1d4170b
fix: 에러메시지 변경에 의한 테스트 코드 수정
Libienz Mar 9, 2024
0eb36e8
refactor: 덱에서 카드를 뽑을 때 마지막 카드를 뽑도록 개선
Libienz Mar 9, 2024
bbaca86
refactor: 팩토리 클래스에서 정적 팩토리 메서드로 로직 응집 개선
Libienz Mar 10, 2024
03c7006
style: 메서드명 구체화 개선
Libienz Mar 10, 2024
cd39577
refactor: 생성 검증 불필요한 통합 제거 개선
Libienz Mar 10, 2024
2d1e568
test: 메서드 소스 테스트 CsvSource로 단순화 개선
Libienz Mar 10, 2024
2b52bee
style: Player -> Participant 클래스 이름 변경
Libienz Mar 10, 2024
8c1c3c0
refactor: 상속을 위한 속성 접근제어자 수정 개선
Libienz Mar 10, 2024
af26e10
feat: Player 구현
Libienz Mar 10, 2024
b4e10f8
feat: 딜러 구현
Libienz Mar 10, 2024
9e56e1d
refactor: 상속 구조에 따른 일급컬렉션 클래스 변경
Libienz Mar 10, 2024
35dfddd
feat: 카드덱으로부터 핸드를 생성하는 기능 추가
Libienz Mar 10, 2024
a8a4908
refactor: 상속 구조 운용 결정을 통한 PlayerCreator 삭제 개선
Libienz Mar 10, 2024
97d070b
refactor: HitStrategy 로직 딜러와 플레이어에게 이전
Libienz Mar 10, 2024
ae0fb23
refactor: 추상 클래스 생성자 접근제어자 개선
Libienz Mar 10, 2024
a33e959
refactor: 점수 계산 기능 Hand 도메인으로 책임 이전
Libienz Mar 10, 2024
1264068
refactor: 사용하지 않는 클래스 제거 개선
Libienz Mar 10, 2024
9bc37a8
refactor: 점수 계산 로직 Hand 도메인 응집으로 인한 연관 클래스 수정
Libienz Mar 10, 2024
d2c0c42
refactor: 도메인 내 뷰로직 제거 개선
Libienz Mar 10, 2024
ce24ab4
refactor: Hand 생성 로직 Hand도메인 안으로 응집
Libienz Mar 10, 2024
a83dc83
refactor: 사용하지 않게 된 HandCreator 클래스 제거 개선
Libienz Mar 10, 2024
2a32365
feat: pop한 개수를 계산하는 기능 구현
Libienz Mar 10, 2024
0472a19
refactor: 몇장 더 뽑았는지 딜러에게 물어보도록 개선
Libienz Mar 10, 2024
71a4e81
refactor: 딜러 전적 계산 Judge클래스로 책임 이전
Libienz Mar 10, 2024
b3f67a8
refactor: 사용하지 않는 메서드 제거 개선
Libienz Mar 10, 2024
d5c52e8
refactor: 상속 구조를 통한 파라미터 타입 개선
Libienz Mar 10, 2024
d756ac4
refactor: 사용하지 않는 부분 삭제 개선
Libienz Mar 10, 2024
1464f13
test: TestFixture -> TestDataCreator로 이름 변경을 통한 의미 구체화
Libienz Mar 10, 2024
6bfeee6
refactor: 정적 팩토리 메서드를 가지는 클래스 생성 로직 퍼지지 않도록 생성자 접근 제어자 개선
Libienz Mar 10, 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
42 changes: 38 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,41 @@
# java-blackjack
# ♠️ java-blackjack ♠️

블랙잭 미션 저장소
- 블랙잭 게임을 변형한 프로그램을 구현한다.
- 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다.
- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다.
- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다.
- 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다.
- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다.
- 게임을 완료한 후 각 플레이어별로 승패를 출력한다.

## 우아한테크코스 코드리뷰
# 🛠️ 기능 구현 목록

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
- [x] 입력
- [x] 게임에 참여할 사람의 이름을 입력 받을 수 있다.
- [x] 한 장 더 받을지 여부를 입력 받을 수 있다.
- [x] 입력 검증
- [x] 카드 추가 여부를 올바른 형태 (y/n)으로 입력했는지 검증할 수 있다.
- [x] 도메인
- [x] 이름은 빈 문자열일 수 없다.
- [x] 게임 참가자의 핸드에 새로운 카드를 추가할 수 있다.
- [x] 이름이 중복되는 플레이어는 존재할 수 없다.
- [x] 플레이어가 없는 경우는 게임을 시작할 수 없다.
- [x] 게임 참가자는 딜러 제외 3명 이하여야 한다.
- [x] 카드합 비교를 통해서 플레이어의 승패를 결정할 수 있다.
- [x] 카드합 비교를 통해서 딜러의 승패를 계산할 수 있다.
- [x] 딜러는 17점 미만이면 카드를 받아야 한다.
- [x] 딜러는 17점 이상이면 카드를 그만 받아야 한다.
- [x] 핸드에서 에이스가 몇개있는지 파악할 수 있다
- [x] 핸드의 합을 계산할 수 있다.
- [x] 저지는 핸드에서 21에 가장 가까운 합을 구할 수 있다.
- [x] 핸드의 최소합이 21을 초과하면 플레이어는 버스트한다.
- [x] 핸드의 최소합이 21 이하면 플레이어는 카드를 뽑을지 여부를 선택할 수 있다.
- [x] 핸드에 카드를 추가할 수 있다.
- [x] 카드덱에서 카드를 지정한 개수만큼 건네줄 수 있다.
- [x] 카드덱에서 카드를 한 장 뽑아서 건네줄 수 있다.
- [x] 카드덱에서 보유한 카드 개수보다 많이 뽑으면 예외가 발생한다.
- [x] 참여자의 핸드에 초기 카드를 분배할 수 있다.
- [x] 출력
- [x] 각 참여자의 카드 정보를 출력할 수 있다.
- [x] 각 참여자의 카드 합을 출력할 수 있다.
- [x] 최종 승패를 출력할 수 있다.
18 changes: 18 additions & 0 deletions src/main/java/blackjack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package blackjack;

import blackjack.controller.BlackJackController;
import blackjack.view.InputView;
import blackjack.view.MessageResolver;
import blackjack.view.OutputView;

public class Application {

public static void main(String[] args) {

InputView inputView = new InputView();
OutputView outputView = new OutputView(new MessageResolver());

BlackJackController blackJackController = new BlackJackController(inputView, outputView);
blackJackController.run();
}
Copy link

@leegwichan leegwichan Mar 10, 2024

Choose a reason for hiding this comment

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

OutputViewInputView를 만들어서 Controller에 주입해주는 이유가 있을까요?
Controller에서 OutputViewInputView를 만들어도 되지 않을까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

필드 주입의 단점을 의식한 것이긴 해요 (DI 측면)

다만 변경될 가능성이 극히 낮음에도 YAGNI원칙을 무시한 부분이 아닐까라는 생각이 들기는 하네요 😅

}
131 changes: 131 additions & 0 deletions src/main/java/blackjack/controller/BlackJackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package blackjack.controller;

import static blackjack.domain.DrawDecision.YES;

import blackjack.domain.DealerGameResult;
import blackjack.domain.DrawDecision;
import blackjack.domain.card.Card;
import blackjack.domain.card.CardDeck;
import blackjack.domain.card.CardDeckCreator;
import blackjack.domain.player.HandCreator;
import blackjack.domain.player.Player;
import blackjack.domain.player.PlayerCreator;
import blackjack.domain.player.PlayerName;
import blackjack.domain.player.Players;
import blackjack.domain.rule.Judge;
import blackjack.domain.rule.Score;
import blackjack.domain.rule.ScoreCalculateStrategy;
import blackjack.view.InputView;
import blackjack.view.OutputView;
import java.util.List;

public class BlackJackController {

private final InputView inputView;
private final OutputView outputView;

public BlackJackController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
CardDeck cardDeck = initCardDeck();
Players players = initPlayers(cardDeck);
Player dealer = initDealer(cardDeck);
printPlayersInformation(players, dealer);

completePlayersHand(players, cardDeck);
completeDealerHand(dealer, cardDeck);

printDealerPopCount(dealer);

printPlayerScore(dealer);
printPlayersScore(players);
printDealerGameResult(dealer, players);
printPlayersGameResult(players, dealer);
}

private CardDeck initCardDeck() {
CardDeckCreator cardDeckCreator = new CardDeckCreator();
return cardDeckCreator.create();
}

private Players initPlayers(CardDeck cardDeck) {
InputMapper inputMapper = new InputMapper();
Copy link
Member

Choose a reason for hiding this comment

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

여기에서 InputMapper 객체를 생성하고 밑에 readHitDecision 메서드에서도 InputMapper 객체를 생성하는데 한번만 생성하는 방향을 고려해보는건 어떨까요?

아예 view에서 controller로 넘어올 때 매핑해올 수도 있을 거 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

아예 view에서 controller로 넘어올 때 매핑해올 수도 있을 거 같아요

뷰가 도메인을 알게되어 원치않는 사이드 이펙트 지점을 만드는 것이 두려웠어요 🤔
말씀하신 것의 일환으로 컨트롤러 내부에서 InputMapper를 필드로 관리하면 좋지 않았을까 생각이 드네요 😅

PlayerCreator playerCreator = new PlayerCreator(new HandCreator());
List<PlayerName> playerNames = inputMapper.mapToPlayers(inputView.readNames());

return new Players(playerNames.stream()
.map(playerName -> playerCreator.createPlayerFrom(playerName, cardDeck))
.toList());
}

private Player initDealer(CardDeck cardDeck) {
PlayerCreator playerCreator = new PlayerCreator(new HandCreator());
return playerCreator.createDealerFrom(cardDeck);
}

private void printPlayersInformation(Players players, Player dealer) {
outputView.printHandOutEvent(players, 2);
outputView.printDealerInitialHand(dealer);
players.getPlayers().forEach(outputView::printPlayerHand);
}

private void completePlayersHand(Players players, CardDeck cardDeck) {
players.getPlayers().forEach(player -> completePlayerHand(player, cardDeck));
}

private void completePlayerHand(Player player, CardDeck cardDeck) {
while (player.canHit() && readHitDecision(player) == YES) {
player.appendCard(cardDeck.popCard());
outputView.printPlayerHand(player);
}
}

private DrawDecision readHitDecision(Player player) {
InputMapper inputMapper = new InputMapper();
return inputMapper.mapToDrawDecision(inputView.readDrawPlan(player.getName()));
}

private void completeDealerHand(Player dealer, CardDeck cardDeck) {
while (dealer.canHit()) {
Card card = cardDeck.popCard();
dealer.appendCard(card);
}
}

private void printDealerPopCount(Player dealer) {
int dealerPopCount = dealer.handSize() - 2;
if (dealerPopCount > 0) {
outputView.printDealerPopCount(16, dealerPopCount);
}
}

private void printPlayersScore(Players players) {
players.getPlayers().forEach(this::printPlayerScore);
}

private void printPlayerScore(Player player) {
outputView.printPlayerScore(player, player.calculateHandScore());
}

private void printDealerGameResult(Player dealer, Players players) {
Score dealerScore = dealer.calculateHandScore();
int playerWinCount = players.countPlayerWithScoreAbove(dealerScore, new ScoreCalculateStrategy());
int dealerWinCount = players.countPlayer() - playerWinCount;

DealerGameResult dealerGameResult = new DealerGameResult(dealerWinCount, playerWinCount);
outputView.printDealerGameResult(dealerGameResult);
}

private void printPlayersGameResult(Players players, Player dealer) {
Judge judge = new Judge();
Score dealerScore = dealer.calculateHandScore();
players.getPlayers()
.forEach(player -> {
Score playerScore = player.calculateHandScore();
outputView.printPlayerGameResult(player, judge.isPlayerWin(dealerScore, playerScore));
});
}
}
22 changes: 22 additions & 0 deletions src/main/java/blackjack/controller/InputMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package blackjack.controller;

import blackjack.domain.DrawDecision;
import blackjack.domain.player.PlayerName;
import java.util.Arrays;
import java.util.List;

public class InputMapper {

private static final String DELIMITER = ",";

public List<PlayerName> mapToPlayers(String target) {
String[] split = target.split(DELIMITER);
return Arrays.stream(split)
.map(PlayerName::new)
.toList();
}

public DrawDecision mapToDrawDecision(String target) {
return DrawDecision.from(target);
}
}
20 changes: 20 additions & 0 deletions src/main/java/blackjack/domain/DealerGameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package blackjack.domain;

public class DealerGameResult {

private final int winCount;
private final int loseCount;

public DealerGameResult(int winCount, int loseCount) {
this.winCount = winCount;
this.loseCount = loseCount;
}

public int getWinCount() {
return winCount;
}

public int getLoseCount() {
return loseCount;
}
}
22 changes: 22 additions & 0 deletions src/main/java/blackjack/domain/DrawDecision.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package blackjack.domain;

import java.util.Arrays;

public enum DrawDecision {

YES("y"),
NO("n");

private final String code;

DrawDecision(String code) {
this.code = code;
}

public static DrawDecision from(String code) {
return Arrays.stream(DrawDecision.values())
.filter(drawDecision -> drawDecision.code.equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("[ERROR] 해당하는 값을 찾지 못하였습니다."));
}
}
Copy link

Choose a reason for hiding this comment

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

적절한 클래스 분리인 것 같습니다 👍

47 changes: 47 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package blackjack.domain.card;

import java.util.Objects;

public class Card {

private final CardShape cardShape;
private final CardNumber cardNumber;
Comment on lines +7 to +8

Choose a reason for hiding this comment

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

CardShape, CardNumber로 역할을 잘 나누셨네요!
저는 Shape, Number라고 쓰는 편이 더 간결하면서도 역할을 충분히 표현해줄 수 있다고 생각하는데, 리비는 어떻게 생각하세요?

Copy link
Member Author

Choose a reason for hiding this comment

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

도메인에서 어느정도 노출되는 정보가 있으니 커찬의 의견 합리적인 것 같습니다.

취향의 영역이라는 생각도 드네요 🤔


public Card(CardShape cardShape, CardNumber cardNumber) {
this.cardShape = cardShape;
this.cardNumber = cardNumber;
}

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

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

@Override
public int hashCode() {
return Objects.hash(cardShape, cardNumber);
}

public int getCardNumber() {
return cardNumber.getValue();
}

public String getCardNumberName() {
return cardNumber.getName();
}

public String getCardShape() {
return cardShape.getName();
}
}
27 changes: 27 additions & 0 deletions src/main/java/blackjack/domain/card/CardDeck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package blackjack.domain.card;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class CardDeck {

private final List<Card> cards;

public CardDeck(List<Card> cards) {
this.cards = cards;
}

public Card popCard() {
if (cards.isEmpty()) {
throw new IllegalArgumentException("[ERROR] 남아있는 카드가 부족하여 카드를 뽑을 수 없습니다");
}
return cards.remove(0);
Copy link
Member

Choose a reason for hiding this comment

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

cards를 ArrayList 로 사용하고 계신대요 ArrayList 의 remove 메서드는 시간복잡도가 효율적이지 못합니다!

다른 자료구조를 사용해보는건 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

전혀 생각하지 못한 부분이었는데 리뷰어와 레디에게 코멘트 들을 수 있었네요 👍
관련해서 생각해보겠습니다 😀

}

public List<Card> popCards(int count) {
return IntStream.range(0, count)
.mapToObj(i -> popCard())
.collect(Collectors.toList());
}
}
22 changes: 22 additions & 0 deletions src/main/java/blackjack/domain/card/CardDeckCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package blackjack.domain.card;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class CardDeckCreator {

public CardDeck create() {
List<Card> cards = createCards(CardShape.values(), CardNumber.values());
Collections.shuffle(cards);
return new CardDeck(cards);
}

Copy link

Choose a reason for hiding this comment

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

CardDeckCreatorCardDeck을 생성하는 로직을 품고 있군요!
CardDeck이 담당해도 괜찮은 역할 아닐까요? 분리한 이유가 궁금해요 💭

Copy link
Member Author

Choose a reason for hiding this comment

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

도메인을 규칙들(enum으로 부터 생성한다, 섞는다)로부터 분리하여 순수히 유지하고 싶었어요
단순히 enum으로 부터 카드를 생성하는 역할과 카드를 섞는 것은 CardDeck의 책임이 아니라고 생각한 것이기도 합니다.

다만 다음의 단점이 있을 것 같다고 생각해서 현재는 바꾼 상태입니다 🫠

  • 파생되는 클래스가 많음
  • 도메인에서 여러 규칙이 분리되어 다른 개발자가 의도하지 않은대로 도메인을 사용할 위험성이 있음

private List<Card> createCards(CardShape[] cardShapes, CardNumber[] cardNumbers) {
return Arrays.stream(cardShapes)
.flatMap(shape -> Arrays.stream(cardNumbers)
.map(number -> new Card(shape, number)))
.collect(Collectors.toList());
}
}
Copy link

Choose a reason for hiding this comment

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

카드 모양과 숫자에 대해 가능한 모든 조합을 생성하는 로직을 stream 이용해서 잘 풀어내셨네요 👍

다른 크루에게도 질문 드렸는데요! 중첩 반복문을 사용하는 거에 대해 어떻게 생각하시나요? 저는 중첩 반복문이 스트림을 이용하는 것보다 명확하다고 생각했어요. 만약, indent<2 라는 요구 사항이 없어도 스트림을 사용하실 건지 궁금해요 💭

Copy link
Member Author

Choose a reason for hiding this comment

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

이전에 어떤 PR을 염탐하다가 어떤 리뷰어분이 다음과 같이 코멘트 남겼던 것이 기억에 남네요 🤔

  • for문으로 iterate하는 것은 명령형
  • stream으로 도는 것은 선언형

개인적으로는 어떤 알고리즘이 돌아야 하는지 명령하는 것(HOW를 작성하는 것)보다 동작을 선언하는 것(WHAT을 작성하는 것)이 더 가독성 좋다고 생각해서 요구 사항이 없어도 스트림을 사용할 것 같습니다 👍

Copy link

Choose a reason for hiding this comment

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

근거가 확실해서 좋네요 👍
의견 감사합니다 ❤️

Loading