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단계 미션 코드리뷰 제출합니다 #26

Merged
merged 31 commits into from
Mar 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e8dba5f
feat : 상속, interface 학습테스트 요구사항 1단계 구현
lxxjn0 Mar 10, 2020
370dc4c
feat : 상속, interface 학습테스트 요구사항 2단계 구현
lxxjn0 Mar 10, 2020
8c679ad
docs : 구현 기능 목록 작성
lxxjn0 Mar 10, 2020
7c3b189
feat : Card 인스턴스 구현
lxxjn0 Mar 10, 2020
97c5212
feat : CardRepository 클래스 구현
lxxjn0 Mar 11, 2020
557148b
feat : Deck 인스턴스 구현
lxxjn0 Mar 11, 2020
2e25261
feat : Hand 인스턴스 구현
lxxjn0 Mar 11, 2020
45e8ff6
feat : User 인스턴스 구현
lxxjn0 Mar 11, 2020
89c0b31
test : Deck 인스턴스와 User 인스턴스의 테스트 코드 수정
lxxjn0 Mar 11, 2020
bf5e0bf
refactor : User 인스턴스 draw 메서드 오버로딩 구현
lxxjn0 Mar 11, 2020
7bf52b0
feat : Score 인스턴스 구현
lxxjn0 Mar 12, 2020
b8198b3
feat : Hand 인스턴스의 카드 점수를 계산하는 기능 구현
lxxjn0 Mar 12, 2020
a127551
feat : User 인스턴스를 추상 클래스로 수정 및 이를 상속받는 Dealer, Player 인스턴스 구현
lxxjn0 Mar 12, 2020
1596767
feat : DrawOpinion 열거형 인스턴스 구현
lxxjn0 Mar 12, 2020
c60f13d
feat : User의 버스트 여부를 판단하는 기능 구현
lxxjn0 Mar 12, 2020
3fe3055
feat : PlayerFactory 클래스 구현
lxxjn0 Mar 12, 2020
b5f8eee
feat : Player들의 이름을 입력받아 생성하는 기능 구현
lxxjn0 Mar 12, 2020
5974bf7
feat : User들이 처음 2장의 카드를 받았다는 메시지 출력 기능 구현
lxxjn0 Mar 12, 2020
fcef622
test : User 테스트 코드 수정
lxxjn0 Mar 12, 2020
577558a
refactor : User 추상 클래스의 추상 메서드 접근 지정자 수정
lxxjn0 Mar 12, 2020
625ee56
feat : 각각의 플레이어들이 카드를 더 받을지 여부에 따라 draw를 하는 기능 구현
lxxjn0 Mar 12, 2020
c4b112f
feat : 딜러가 카드를 더 뽑는 기능 구현
lxxjn0 Mar 12, 2020
6cbf3ef
feat : 유저들의 최종 카드와 점수를 출력하는 기능 구현
lxxjn0 Mar 13, 2020
c2581d2
feat : ResultType 열거형 인스턴스 구현
lxxjn0 Mar 13, 2020
f8397a4
feat : 유저들의 최종 결과를 구하는 기능 구현
lxxjn0 Mar 13, 2020
a2d19cd
feat : 유저들의 최종 결과를 출력하는 기능 구현
lxxjn0 Mar 13, 2020
18742a3
test : StringUtil 클래스 테스트 수정
lxxjn0 Mar 13, 2020
4a9d635
refactor : OutputView 메서드 일부 수정
lxxjn0 Mar 13, 2020
4dc08f7
refactor : 버스트 처리 추가 및 기타 코드 수정
lxxjn0 Mar 14, 2020
aa986da
refactor : Score 인스턴스 유효성 검사 추가
lxxjn0 Mar 14, 2020
709cee4
refactor : 피드백 반영
lxxjn0 Mar 15, 2020
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ bin/
.idea
*.iws
*.iml
*.ipr
*.iprtest\
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
# java-blackjack

블랙잭 게임 미션 저장소

<br>

## 구현 기능 목록

- 참여할 플레이어들의 이름을 입력받는 기능

- 이름을 쉼표를 기준으로 나누는 기능

- 유저(딜러, 플레이어)들에게 카드를 2장씩 나누어줬다는 메시지를 출력하는 기능

- 유저들이 받은 카드를 출력하는 기능

- 딜러는 1장, 플레이어는 2장을 출력하는 기능

- 각각의 플레이어에 대한 점수를 계산하는 기능

- 각각의 플레이어에게 카드를 더 받을지 여부를 입력받는 기능

- 카드를 받은 후, 현재 보유한 카드를 출력하는 기능

- 점수가 21이 초과하는지 확인하는 기능

- Ace의 경우 1점 또는 11점으로 계산이 가능한 기능

- King, Queen, Jack의 경우 10점으로 계산하는 기능

- 딜러에 대한 점수를 계산하는 기능

- 딜러의 점수가 16점 이하이면 한장의 카드를 추가로 받는 기능

- 딜러의 점수가 17점 이상이면 카드를 추가로 받지 못하는 기능

- 유저(딜러, 플레이어)들의 최종 카드 상태와 점수를 출력하는 기능

- 유저(딜러, 플레이어)들의 최종 결과를 출력하는 기능

- 딜러보다 플레이어가 점수가 높으면 승, 낮으면 패로 계산하는 기능

<br>

## 우아한테크코스 코드리뷰

* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
26 changes: 26 additions & 0 deletions src/main/java/blackjack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package blackjack;

import static blackjack.util.StringUtil.*;

import java.util.List;

import blackjack.controller.BlackjackController;
import blackjack.domain.BlackjackTable;
import blackjack.domain.card.CardFactory;
import blackjack.domain.card.Deck;
import blackjack.domain.user.Dealer;
import blackjack.domain.user.Player;
import blackjack.domain.user.PlayerFactory;
import blackjack.view.InputView;

public class Application {
public static void main(String[] args) {
Deck deck = new Deck(CardFactory.create());
Dealer dealer = new Dealer(Dealer.NAME);
List<Player> players = PlayerFactory.create(parsingPlayerNames(InputView.inputPlayerNames()));
BlackjackTable blackjackTable = new BlackjackTable(deck);

BlackjackController blackjackController = new BlackjackController(blackjackTable);
blackjackController.playGame(dealer, players);
}
}
67 changes: 67 additions & 0 deletions src/main/java/blackjack/controller/BlackjackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package blackjack.controller;

import static blackjack.view.InputView.*;
import static blackjack.view.OutputView.*;

import java.util.List;

import blackjack.domain.BlackjackTable;
import blackjack.domain.DrawOpinion;
import blackjack.domain.result.Report;
import blackjack.domain.user.Dealer;
import blackjack.domain.user.Player;
import blackjack.domain.user.User;

public class BlackjackController {
private final BlackjackTable blackjackTable;

public BlackjackController(BlackjackTable blackjackTable) {
this.blackjackTable = blackjackTable;
}

public void playGame(Dealer dealer, List<Player> players) {
List<User> users = blackjackTable.collectToUsersFrom(dealer, players);

drawInitialCardsFrom(users);
continueDrawCardsEach(dealer, players);

printUsersCardsAndScore(users);

Report blackJackReport = Report.from(dealer, players);
printBlackjackReport(blackJackReport);
}

private void drawInitialCardsFrom(List<User> users) {
blackjackTable.drawInitialCards(users);
printUsersInitialDraw(BlackjackTable.INITIAL_DRAW_NUMBER, users);
}

private void continueDrawCardsEach(Dealer dealer, List<Player> players) {
for (Player player : players) {
drawCardsFrom(player);
}
drawCardsFrom(dealer);
}

private void drawCardsFrom(Player player) {
while (canDraw(player) && wantDraw(player)) {
blackjackTable.drawCardFrom(player);
printUserHand(player, player.getHand());
}
}

private boolean canDraw(User user) {

Choose a reason for hiding this comment

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

이 메서드는 없어도 되지 않을까요?
dealer.canDraw()나 player.canDraw()로 써도 충분하지 않을까요?
메서드로 한 번 더 감싸면 추가적인 어떤 기능이 있나? 라는 생각이 들어서 이 메서드가 어떻게 구현됐는지 파악해야되요

Copy link
Author

Choose a reason for hiding this comment

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

조건문의 형식을 맞추기 위해서 한번 더 분리를 진행했는데 특별한 기능이 없는 메서드인 만큼 다시 원래대로 돌려두겠습니다!!

return user.canDraw();
}

private boolean wantDraw(Player player) {
return DrawOpinion.of(inputDrawOpinion(player)).isYes();

Choose a reason for hiding this comment

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

Suggested change
return DrawOpinion.of(inputDrawOpinion(player)).isYes();
return DrawOpinion.of(inputDrawOpinion(player))
.isYes();

보통 한 줄에 점 하나만 찍혀야 읽기 좋아요

}

private void drawCardsFrom(Dealer dealer) {
while (canDraw(dealer)) {
blackjackTable.drawCardFrom(dealer);
printDealerDrawCard();
}
}
}
36 changes: 36 additions & 0 deletions src/main/java/blackjack/domain/BlackjackTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package blackjack.domain;

import java.util.ArrayList;
import java.util.List;

import blackjack.domain.card.Deck;
import blackjack.domain.user.Dealer;
import blackjack.domain.user.Player;
import blackjack.domain.user.User;

public class BlackjackTable {
public static final int INITIAL_DRAW_NUMBER = 2;

private final Deck deck;

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

public List<User> collectToUsersFrom(Dealer dealer, List<Player> players) {

Choose a reason for hiding this comment

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

BlackjackTable 클래스가 딜러, 플레이어를 인스턴스 변수로 갖는 것도 괜찮겠네요

Copy link
Author

Choose a reason for hiding this comment

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

아직 완벽하게 컨트롤러와 분리를 못해서 이렇게 뒀는데, 좀 더 확실히 도메인으로 분리하면서 인스턴스 변수로 두어보겠습니다 :)

List<User> users = new ArrayList<>();
users.add(dealer);
users.addAll(players);
return users;
}

public void drawInitialCards(List<User> users) {
for (User user : users) {
user.draw(deck, INITIAL_DRAW_NUMBER);
}
}

public void drawCardFrom(User user) {
user.draw(deck);
}
}
25 changes: 25 additions & 0 deletions src/main/java/blackjack/domain/DrawOpinion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package blackjack.domain;

import java.util.Arrays;

public enum DrawOpinion {
YES("y"),
NO("n");

private final String opinion;

DrawOpinion(String opinion) {
this.opinion = opinion;
}

public static DrawOpinion of(String opinion) {
return Arrays.stream(values())
.filter(drawOpinion -> drawOpinion.opinion.equals(opinion))
.findAny()
.orElseThrow(() -> new InvalidDrawOpinionException(InvalidDrawOpinionException.INVALID));
}

public boolean isYes() {
return this.equals(YES);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package blackjack.domain;

public class InvalidDrawOpinionException extends IllegalArgumentException {
public static final String INVALID = "y 또는 n만 입력 가능합니다.";

public InvalidDrawOpinionException(String s) {
super(s);
}
}
44 changes: 44 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package blackjack.domain.card;

import java.util.Objects;

public class Card {
private final Symbol symbol;
private final Type type;

public Card(Symbol symbol, Type type) {
this.symbol = symbol;
this.type = type;
}

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

public int getScore() {
return symbol.getScore();
}

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

@Override
public int hashCode() {
return Objects.hash(symbol, type);
}

@Override
public String toString() {
return symbol.getSymbol() + type.getType();
}
}
28 changes: 28 additions & 0 deletions src/main/java/blackjack/domain/card/CardFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package blackjack.domain.card;

import static java.util.stream.Collectors.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

public class CardFactory {
private static final List<Card> CARDS;

static {
CARDS = Arrays.stream(Symbol.values())
.flatMap(CardFactory::createBy)
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
}

private static Stream<Card> createBy(Symbol symbol) {
return Arrays.stream(Type.values())
.map(type -> new Card(symbol, type));
}

public static List<Card> create() {
return new ArrayList<>(CARDS);
}
}
32 changes: 32 additions & 0 deletions src/main/java/blackjack/domain/card/Deck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package blackjack.domain.card;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;

public class Deck {
private static final int TOP = 0;

private final List<Card> cards;

public Deck(List<Card> cards) {
validate(cards);
Collections.shuffle(cards);
this.cards = cards;
}

private void validate(List<Card> cards) {
long distinctCardSize = new HashSet<>(cards).size();

if (cards.size() != distinctCardSize) {
throw new InvalidDeckException(InvalidDeckException.INVALID);
}
}

public Card draw() {
if (cards.isEmpty()) {

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.

저도 이 부분에 대해 고민을 했는데, 실제 블랙잭 규칙은 사용자가 8명이란 제한이 있어서 한 카드 팩으로 게임이 가능한 것 같았습니다.

그래도 혹시나 사용자의 제한이 없다면 추가적인 카드팩이 필요하다고 생각이 들어서 해당 경우를 만들어줬는데 한 카드팩이 소진되면 예외를 발생시켜 게임이 종료되도록 수정하겠습니다!!

throw new InvalidDeckException(InvalidDeckException.DECK_EMPTY);
}
return cards.remove(TOP);
}
}
11 changes: 11 additions & 0 deletions src/main/java/blackjack/domain/card/InvalidDeckException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package blackjack.domain.card;

public class InvalidDeckException extends IllegalArgumentException {
public static final String INVALID = "카드 덱에 중복이 존재합니다.";
public static final String DECK_EMPTY = "덱의 모든 카드를 소진하였습니다.";
public static final String NULL = "덱이 존재하지 않습니다.";

public InvalidDeckException(String s) {
super(s);
}
}
37 changes: 37 additions & 0 deletions src/main/java/blackjack/domain/card/Symbol.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package blackjack.domain.card;

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

private final int score;
private final String symbol;

Symbol(int score, String symbol) {
this.score = score;
this.symbol = symbol;
}

public boolean isAce() {
return this.equals(ACE);
}

public int getScore() {
return score;
}

public String getSymbol() {
return symbol;
}
}
18 changes: 18 additions & 0 deletions src/main/java/blackjack/domain/card/Type.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package blackjack.domain.card;

public enum Type {
SPADE("스페이드"),
HEART("하트"),
CLUB("클럽"),
DIAMOND("다이아몬드");

private final String type;

Type(String type) {
this.type = type;
}

public String getType() {
return type;
}
}
Loading