ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,66 @@
+package blackjack.controller;
+
+import blackjack.domain.card.Deck;
+import blackjack.domain.participant.Dealer;
+import blackjack.domain.participant.Player;
+import blackjack.domain.participant.PlayersFactory;
+import blackjack.domain.result.GameResult;
+import blackjack.dto.DrawCardRequestDto;
+import blackjack.dto.PlayersNameInputDto;
+import blackjack.view.InputView;
+import blackjack.view.OutputView;
+
+import java.util.List;
+
+public class BlackJackController {
+    private final InputView inputView = new InputView();
+    private final OutputView outputView = new OutputView();
+
+    public void run() {
+        PlayersNameInputDto namesInput = inputView.getPlayersName();
+        Dealer dealer = new Dealer();
+        Deck deck = new Deck();
+        List<Player> players = PlayersFactory.createPlayers(namesInput.getPlayersName());
+
+        drawTowCards(dealer, players, deck);
+        drawCardToPlayers(players, deck);
+        drawCardToDealer(dealer, deck);
+        outputView.printCardsResult(dealer, players);
+        outputView.printGameResult(GameResult.of(dealer, players));
+    }
+
+    private void drawTowCards(Dealer dealer, List<Player> players, Deck deck) {
+        for (Player player : players) {
+            player.receiveCard(deck.drawCard());
+            player.receiveCard(deck.drawCard());
+        }
+        dealer.receiveCard(deck.drawCard());
+        dealer.receiveCard(deck.drawCard());
+        outputView.printFirstCardsGiven(players, dealer);
+        outputView.printDealerCard(dealer);
+        outputView.printPlayersCard(players);
+    }
+
+    private void drawCardToPlayers(List<Player> players, Deck deck) {
+        for (Player player : players) {
+            drawCardToPlayer(player, deck);
+        }
+    }
+
+    private void drawCardToPlayer(Player player, Deck deck) {
+        DrawCardRequestDto drawCardRequest = inputView.getPlayersResponse(player);
+        while (player.drawable() && drawCardRequest.isYes()) {
+            player.receiveCard(deck.drawCard());
+
+            outputView.printCards(player);
+            drawCardRequest = inputView.getPlayersResponse(player);
+        }
+    }
+
+    private void drawCardToDealer(Dealer dealer, Deck deck) {
+        while (dealer.drawable()) {
+            dealer.receiveCard(deck.drawCard());
+            outputView.printDealerCardGiven();
+        }
+    }
+}

리뷰: Controller를 Entry-point로써 사용하고 있는데, 현재와 같이 해당 값들을 이를 멤버변수로 가지게 되면 해당 게임을 여러명이 동시에 요청하는 경우 데이터 부정합이 발생할 수 있어요. 멤버변수를 지역변수로 가져가는 것이 좋아 보여요🙂
@@ -0,0 +1,66 @@
+package blackjack.controller;
+
+import blackjack.domain.card.Deck;
+import blackjack.domain.participant.Dealer;
+import blackjack.domain.participant.Player;
+import blackjack.domain.participant.PlayersFactory;
+import blackjack.domain.result.GameResult;
+import blackjack.dto.DrawCardRequestDto;
+import blackjack.dto.PlayersNameInputDto;
+import blackjack.view.InputView;
+import blackjack.view.OutputView;
+
+import java.util.List;
+
+public class BlackJackController {
+    private final InputView inputView = new InputView();
+    private final OutputView outputView = new OutputView();
+
+    public void run() {
+        PlayersNameInputDto namesInput = inputView.getPlayersName();
+        Dealer dealer = new Dealer();
+        Deck deck = new Deck();
+        List<Player> players = PlayersFactory.createPlayers(namesInput.getPlayersName());
+
+        drawTowCards(dealer, players, deck);
+        drawCardToPlayers(players, deck);
+        drawCardToDealer(dealer, deck);
+        outputView.printCardsResult(dealer, players);
+        outputView.printGameResult(GameResult.of(dealer, players));
+    }
+
+    private void drawTowCards(Dealer dealer, List<Player> players, Deck deck) {
+        for (Player player : players) {
+            player.receiveCard(deck.drawCard());
+            player.receiveCard(deck.drawCard());
+        }
+        dealer.receiveCard(deck.drawCard());
+        dealer.receiveCard(deck.drawCard());
+        outputView.printFirstCardsGiven(players, dealer);
+        outputView.printDealerCard(dealer);
+        outputView.printPlayersCard(players);
+    }
+
+    private void drawCardToPlayers(List<Player> players, Deck deck) {
+        for (Player player : players) {
+            drawCardToPlayer(player, deck);
+        }
+    }
+
+    private void drawCardToPlayer(Player player, Deck deck) {
+        DrawCardRequestDto drawCardRequest = inputView.getPlayersResponse(player);
+        while (player.drawable() && drawCardRequest.isYes()) {
+            player.receiveCard(deck.drawCard());
+
+            outputView.printCards(player);
+            drawCardRequest = inputView.getPlayersResponse(player);
+        }
+    }
+
+    private void drawCardToDealer(Dealer dealer, Deck deck) {
+        while (dealer.drawable()) {
+            dealer.receiveCard(deck.drawCard());
+            outputView.printDealerCardGiven();
+        }
+    }
+}

리뷰: 메소드 분리가 잘 되어 있어서, 가독성이 좋네요🙂
@@ -0,0 +1,66 @@
+package blackjack.controller;
+
+import blackjack.domain.card.Deck;
+import blackjack.domain.participant.Dealer;
+import blackjack.domain.participant.Player;
+import blackjack.domain.participant.PlayersFactory;
+import blackjack.domain.result.GameResult;
+import blackjack.dto.DrawCardRequestDto;
+import blackjack.dto.PlayersNameInputDto;
+import blackjack.view.InputView;
+import blackjack.view.OutputView;
+
+import java.util.List;
+
+public class BlackJackController {
+    private final InputView inputView = new InputView();
+    private final OutputView outputView = new OutputView();
+
+    public void run() {
+        PlayersNameInputDto namesInput = inputView.getPlayersName();
+        Dealer dealer = new Dealer();
+        Deck deck = new Deck();
+        List<Player> players = PlayersFactory.createPlayers(namesInput.getPlayersName());
+
+        drawTowCards(dealer, players, deck);
+        drawCardToPlayers(players, deck);
+        drawCardToDealer(dealer, deck);
+        outputView.printCardsResult(dealer, players);
+        outputView.printGameResult(GameResult.of(dealer, players));
+    }
+
+    private void drawTowCards(Dealer dealer, List<Player> players, Deck deck) {
+        for (Player player : players) {
+            player.receiveCard(deck.drawCard());
+            player.receiveCard(deck.drawCard());
+        }
+        dealer.receiveCard(deck.drawCard());
+        dealer.receiveCard(deck.drawCard());
+        outputView.printFirstCardsGiven(players, dealer);
+        outputView.printDealerCard(dealer);
+        outputView.printPlayersCard(players);
+    }
+
+    private void drawCardToPlayers(List<Player> players, Deck deck) {
+        for (Player player : players) {
+            drawCardToPlayer(player, deck);
+        }
+    }
+
+    private void drawCardToPlayer(Player player, Deck deck) {
+        DrawCardRequestDto drawCardRequest = inputView.getPlayersResponse(player);
+        while (player.drawable() && drawCardRequest.isYes()) {
+            player.receiveCard(deck.drawCard());
+
+            outputView.printCards(player);
+            drawCardRequest = inputView.getPlayersResponse(player);
+        }
+    }
+
+    private void drawCardToDealer(Dealer dealer, Deck deck) {
+        while (dealer.drawable()) {
+            dealer.receiveCard(deck.drawCard());
+            outputView.printDealerCardGiven();
+        }
+    }
+}

리뷰: DTO도 자신이 가지고 있는 데이터에 대해서, 어떤 행위/판단을 할 수 있다고 생각해요. 입력받은 값이 Y/N인지 판단하는 로직은 DTO로 가도 된다고 생각합니다.

다만 해당 로직이 여러 곳에서 사용되거나, 이를 하나의 도메인 행위로 정의한다면 DTO -> Domain으로 전환한 다음 도메인에서 판단하는 것도 좋을 것 같아요. 현재는 해당 부분에 대한 도메인 로직이 Controller에 나와 있기에 원하시는 형태로 포장해주시면 좋을 것 같습니다🙂 

추가로 네이밍은 팀바팀이고 사바사지만, 개인적으로 프로그램에게 넘어온 Input을 담는 DTO는 Request/우리 프로그램에서 반환해주는DTO는 Response로 사용하고 있어요. response로 작성해주신 맥락은 알겠으나, Client-Server가 항상 유동적으로 달라질 수 있다면 DTO가 많아졌을 때 헷갈릴 수 있다고 생각해요🙂 (추가로 DTO는 > Dto로 적고 있긴합니다 - 취향)
@@ -0,0 +1,17 @@
+package blackjack.utils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class StringUtil {
+    private static final String COMMA = ",";
+    private static final String RESPONSE_RESTRICT_MESSAGE = "y 혹은 n 만 입력할 수 있습니다.";
+
+    public static List<String> splitByComma(String input) {
+        List<String> names = Arrays.asList(input.split(COMMA));
+        return names.stream()
+                .map(String::trim)
+                .collect(Collectors.toList());
+    }
+}

리뷰: `Utils성 클래스`는 일반적으로 정말 유틸리티한 것들만 기능을 담는 것이 일반적이에요. 현재 유틸로직이 Y/N이라는 어떤 룰에 대해서 너무 많이 알고 있는 상태인 것 같습니다

도메인 혹은 DTO의 로직으로 변경하는 것이 좋아보이고, Util에서 사용하신다면 검증 대상이 되는 Y/N도 파라미터로 받는다면 조금 더 범용적으로 사용할 수 있는 메소드가 될 것 같아요.
@@ -0,0 +1,32 @@
+package blackjack.domain.card;
+
+import blackjack.domain.card.strategy.DrawStrategy;
+import blackjack.domain.card.strategy.RandomDrawStrategy;
+import lombok.Getter;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Deck {
+
+    private static final String ALERT_NO_CARD_LEFT = "사용 가능한 카드를 모두 소진하였습니다.";
+
+    @Getter
+    private final List<Card> deck;
+
+    public Deck() {
+        this.deck = new LinkedList<>(DeckFactory.createDeck());
+    }
+
+    public Card drawCard() {
+        if (deck.isEmpty()) {
+            throw new RuntimeException(ALERT_NO_CARD_LEFT);
+        }
+        return deck.remove(generateNextDrawCardIndex(new RandomDrawStrategy()));
+    }
+
+    private int generateNextDrawCardIndex(DrawStrategy drawStrategy) {
+        return drawStrategy.getNextIndex(deck);
+    }
+
+}

리뷰: LinkedList로 선언하신 이유는 카드를 한장씩 삭제할 때 ArrayList가 비효율적이라서 그렇게 하신걸까요? 👏
@@ -0,0 +1,32 @@
+package blackjack.domain.card;
+
+import blackjack.domain.card.strategy.DrawStrategy;
+import blackjack.domain.card.strategy.RandomDrawStrategy;
+import lombok.Getter;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Deck {
+
+    private static final String ALERT_NO_CARD_LEFT = "사용 가능한 카드를 모두 소진하였습니다.";
+
+    @Getter
+    private final List<Card> deck;
+
+    public Deck() {
+        this.deck = new LinkedList<>(DeckFactory.createDeck());
+    }
+
+    public Card drawCard() {
+        if (deck.isEmpty()) {
+            throw new RuntimeException(ALERT_NO_CARD_LEFT);
+        }
+        return deck.remove(generateNextDrawCardIndex(new RandomDrawStrategy()));
+    }
+
+    private int generateNextDrawCardIndex(DrawStrategy drawStrategy) {
+        return drawStrategy.getNextIndex(deck);
+    }
+
+}

리뷰: 현재는 경기도중에 카드가 모두 소진되면, 프로그램이 종료되는 구조인데요. 일반적으로 경기를 하다, 카드가 모두 소진된다고 그 게임이 무효가 되지 않듯이 조금 더 구체적인 방법/구현을 고민해보면 좋을 것 같아요. (구현은 하지 않으셔도 됩니다)

도메인에 대해서 조금 더 능동적으로 이해하면 어떨까 라는 의미에서 드린 피드백입니다. (참고로 블랙잭에서는 참가 인원을 제한하고 있더라구요🙂)
@@ -0,0 +1,32 @@
+package blackjack.domain.card;
+
+import blackjack.domain.card.strategy.DrawStrategy;
+import blackjack.domain.card.strategy.RandomDrawStrategy;
+import lombok.Getter;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Deck {
+
+    private static final String ALERT_NO_CARD_LEFT = "사용 가능한 카드를 모두 소진하였습니다.";
+
+    @Getter
+    private final List<Card> deck;
+
+    public Deck() {
+        this.deck = new LinkedList<>(DeckFactory.createDeck());
+    }
+
+    public Card drawCard() {
+        if (deck.isEmpty()) {
+            throw new RuntimeException(ALERT_NO_CARD_LEFT);
+        }
+        return deck.remove(generateNextDrawCardIndex(new RandomDrawStrategy()));
+    }
+
+    private int generateNextDrawCardIndex(DrawStrategy drawStrategy) {
+        return drawStrategy.getNextIndex(deck);
+    }
+
+}

리뷰: Random한 값을 테스트하기 위해서, 레이싱카에서 했던 방식으로 이를 외부에서 주입받는 형태로 하면 어떨까요?
@@ -0,0 +1,39 @@
+package blackjack.domain.card;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DeckFactory {
+
+    private static final List<Card> cardsDeck;
+
+    static {
+        cardsDeck = createCardsDeck();
+    }
+
+    private static List<Card> createCardsDeck() {
+        List<Card> cards = new ArrayList<>();
+
+        for (Denomination denomination : Denomination.values()) {
+            cards.addAll(createCards(denomination));
+        }
+        return cards;
+    }
+
+    private static List<Card> createCards(Denomination denomination) {
+        List<Card> cards = new ArrayList<>();
+
+        for (Type type : Type.values()) {
+            cards.add(new Card(denomination, type));
+        }
+
+        return cards;
+    }
+
+    public static List<Card> createDeck() {
+        return Collections.unmodifiableList(cardsDeck);
+    }
+}
+
+

리뷰: 스태틱하게 어플리케이션이 시작됨과 동시에 생긴다는 것을 명시적으로 하기 위해서 static { } 형태로 초기화를 할 수도 있습니다🙂
@@ -0,0 +1,39 @@
+package blackjack.domain.card;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DeckFactory {
+
+    private static final List<Card> cardsDeck;
+
+    static {
+        cardsDeck = createCardsDeck();
+    }
+
+    private static List<Card> createCardsDeck() {
+        List<Card> cards = new ArrayList<>();
+
+        for (Denomination denomination : Denomination.values()) {
+            cards.addAll(createCards(denomination));
+        }
+        return cards;
+    }
+
+    private static List<Card> createCards(Denomination denomination) {
+        List<Card> cards = new ArrayList<>();
+
+        for (Type type : Type.values()) {
+            cards.add(new Card(denomination, type));
+        }
+
+        return cards;
+    }
+
+    public static List<Card> createDeck() {
+        return Collections.unmodifiableList(cardsDeck);
+    }
+}
+
+

리뷰: 메소드의 네이밍으로 봤을 때 `Deck` 을 만들 것  같은데, List<Card> 를 반환하고 있는 부분이 직관적으로 이해되지 않는 것 같습니다.  추가로 어플리케이션이 뜸과 동시에 `cardDecks`를  만들고 있어서, 사용한다면 getter 형태로 사용해야하지 않을까 싶네요.

혹시 이 메소드를 사용하는 사람 입장에서는 `어플리케이션에서 해당 값(List<Card>)은 항상 고정되어 있다라는걸 몰라도 되고 필요할 때 달라하면 된다` 느낌으로 작성하신걸까요? > 요 경우에도 사용자 입장에선 항상 덱을 만들거라 예상하고 로직을 진행할 수 있어서 위험할 것 같아요!
@@ -0,0 +1,38 @@
+package blackjack.domain.card;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CardTest {
+
+    @DisplayName("카드 객체의 숫자와 종류값이 같으면 같은 객체로 판단한다")
+    @Test
+    void equalsTest() {
+        //given, when, then
+        assertThat(new Card(Denomination.TWO, Type.DIAMOND))
+                .isEqualTo(new Card(Denomination.TWO, Type.DIAMOND));
+
+    }
+
+    @DisplayName("카드 객체의 숫자와 종류를 출력한다")
+    @Test
+    void printCardTwoDiamond() {
+        //given
+        Card card = new Card(Denomination.TWO, Type.DIAMOND);
+
+        //when, then
+        assertThat(card).hasToString("2다이아몬드");
+    }
+
+    @DisplayName("카드 객체의 숫자와 종류를 출력한다")
+    @Test
+    void printCardAceHeart() {
+        //given
+        Card card = new Card(Denomination.ACE, Type.HEART);
+
+        //when, then
+        assertThat(card.toString()).isEqualTo("A하트");
+    }
+}

리뷰: `assertThat(card).hasToString("2다이아몬드")` 이런 문법도 있습니다🙂
@@ -0,0 +1,102 @@
+package blackjack.domain.participant;
+
+import blackjack.domain.card.Card;
+import blackjack.domain.card.Denomination;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Getter
+public abstract class Participant {
+    private static final int DIFFERENCE_OF_ACE_SCORE = 10;
+    private static final int BLACKJACK = 21;
+    private static final int ZERO = 0;
+    private static final String CHECK_NULL_OR_EMPTY = "이름이 빈 칸 혹은 null 값이 아닌지 확인해주세요.";
+    private static final String CHECK_CONTAINING_ONLY_LETTERS_AND_DIGITS = "이름은 특수문자를 포함하지 않은 문자와 숫자로 지정해주세요.";
+
+    private final String name;
+    private final List<Card> cards;
+
+    protected Participant(String name) {
+        validateName(name);
+
+        this.name = name;
+        this.cards = new ArrayList<>();
+    }
+
+    private void validateName(String name) {
+        validateNullOrEmpty(name);
+        validateAlphaNumeric(name);
+    }
+
+    private void validateNullOrEmpty(String name) {
+        if (StringUtils.isBlank(name)) {
+            throw new IllegalArgumentException(CHECK_NULL_OR_EMPTY);
+        }
+    }
+
+    private void validateAlphaNumeric(String name) {
+        if (!StringUtils.isAlphanumericSpace(name)) {
+            throw new IllegalArgumentException(CHECK_CONTAINING_ONLY_LETTERS_AND_DIGITS);
+        }
+    }
+
+    protected abstract boolean drawable();
+
+    public void receiveCard(Card card) {
+        cards.add(card);
+    }
+
+    public int getCardsSum() {
+        int scoreOfAceAsEleven = sumOfCardsScore();
+        int aceCount = getAceCount();
+
+        while (canCountAceAsOne(scoreOfAceAsEleven, aceCount)) {
+            scoreOfAceAsEleven = scoreOfAceAsOne(scoreOfAceAsEleven);
+            aceCount--;
+        }
+
+        return scoreOfAceAsEleven;
+    }
+
+    private int scoreOfAceAsOne(int scoreOfAceAsEleven) {
+        return scoreOfAceAsEleven - DIFFERENCE_OF_ACE_SCORE;
+    }
+
+    private boolean canCountAceAsOne(int scoreOfAceAsEleven, int aceCount) {
+        return scoreOfAceAsEleven > BLACKJACK && aceCount > ZERO;
+    }
+
+    private int getAceCount() {
+        return (int) cards.stream()
+                .filter(Card::isAce)
+                .count();
+    }
+
+    private int sumOfCardsScore() {
+        return cards.stream()
+                .map(Card::getDenomination)
+                .mapToInt(Denomination::getScore)
+                .sum();
+    }
+
+    public boolean isBust() {
+        return getCardsSum() > BLACKJACK;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Participant that = (Participant) o;
+        return Objects.equals(name, that.name) && Objects.equals(cards, that.cards);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, cards);
+    }
+}

리뷰: ACE의 수를 결정할 때, 1 또는 11이 되는 로직은 Denomination의 역할이 아닐까요?
@@ -0,0 +1,27 @@
+package blackjack.domain.participant;
+
+import lombok.Getter;
+
+@Getter
+public class Player extends Participant {
+
+    private static final int PLAYER_DRAW_THRESHOLD = 21;
+    private static final String DEALER_NAME = "딜러";
+    private static final String CHECK_SAME_NAME_AS_DEALER = "이름은 딜러와 같을 수 없으니 다른 이름을 지정해주세요.";
+
+    public Player(String name) {
+        super(name);
+        validateDealerNameDuplicated(name);
+    }
+
+    private void validateDealerNameDuplicated(String name) {
+        if (name.equals(DEALER_NAME)) {
+            throw new IllegalArgumentException(CHECK_SAME_NAME_AS_DEALER);
+        }
+    }
+
+    @Override
+    public boolean drawable() {
+        return getCardsSum() < PLAYER_DRAW_THRESHOLD;
+    }
+}

리뷰: 재밌는 제약사항인 것 같습니다🙂
@@ -0,0 +1,27 @@
+package blackjack.domain.participant;
+
+import lombok.Getter;
+
+@Getter
+public class Player extends Participant {
+
+    private static final int PLAYER_DRAW_THRESHOLD = 21;
+    private static final String DEALER_NAME = "딜러";
+    private static final String CHECK_SAME_NAME_AS_DEALER = "이름은 딜러와 같을 수 없으니 다른 이름을 지정해주세요.";
+
+    public Player(String name) {
+        super(name);
+        validateDealerNameDuplicated(name);
+    }
+
+    private void validateDealerNameDuplicated(String name) {
+        if (name.equals(DEALER_NAME)) {
+            throw new IllegalArgumentException(CHECK_SAME_NAME_AS_DEALER);
+        }
+    }
+
+    @Override
+    public boolean drawable() {
+        return getCardsSum() < PLAYER_DRAW_THRESHOLD;
+    }
+}

리뷰: 현재 사용하신 이유는 OutputView 로직을 단순하게 하기 위함인 것 같아요. 화면에 보여주는 형태가 달라졌을 때, 도메인을 수정해야 한다면 의존관계가 잘 못 설계된거라고 생각합니다!
@@ -0,0 +1,27 @@
+package blackjack.domain.participant;
+
+import lombok.Getter;
+
+@Getter
+public class Player extends Participant {
+
+    private static final int PLAYER_DRAW_THRESHOLD = 21;
+    private static final String DEALER_NAME = "딜러";
+    private static final String CHECK_SAME_NAME_AS_DEALER = "이름은 딜러와 같을 수 없으니 다른 이름을 지정해주세요.";
+
+    public Player(String name) {
+        super(name);
+        validateDealerNameDuplicated(name);
+    }
+
+    private void validateDealerNameDuplicated(String name) {
+        if (name.equals(DEALER_NAME)) {
+            throw new IllegalArgumentException(CHECK_SAME_NAME_AS_DEALER);
+        }
+    }
+
+    @Override
+    public boolean drawable() {
+        return getCardsSum() < PLAYER_DRAW_THRESHOLD;
+    }
+}

리뷰: 해당 메소드는 이겼다가, 아니라 `경기 결과를 반환해주는 메소드`니까 이름을 변경해주시면 좋을 것 같습니다. 추가로 `Rule`이 사용자와 딜러를 모두 받아 결과를 반환해주는게 역할이 적절하지 않을까요? (현재의 rule.compare과 getWinningResult를 합친 메소드를 Rule에서 제공하면 어떨까요?)
@@ -0,0 +1,27 @@
+package blackjack.domain.participant;
+
+import lombok.Getter;
+
+@Getter
+public class Player extends Participant {
+
+    private static final int PLAYER_DRAW_THRESHOLD = 21;
+    private static final String DEALER_NAME = "딜러";
+    private static final String CHECK_SAME_NAME_AS_DEALER = "이름은 딜러와 같을 수 없으니 다른 이름을 지정해주세요.";
+
+    public Player(String name) {
+        super(name);
+        validateDealerNameDuplicated(name);
+    }
+
+    private void validateDealerNameDuplicated(String name) {
+        if (name.equals(DEALER_NAME)) {
+            throw new IllegalArgumentException(CHECK_SAME_NAME_AS_DEALER);
+        }
+    }
+
+    @Override
+    public boolean drawable() {
+        return getCardsSum() < PLAYER_DRAW_THRESHOLD;
+    }
+}

리뷰: `로직상 없을 수가 없다` 라는 생각으로 `get`을 사용해주신 것 같아요. 다만 요게 없는 경우에는 `NullPoint`가 날텐데요. 이 메시지를 보고 어떤 의미인지 찾기가 어려울 것 같아요.

요렇게 애매한 경우에 저는 orElseThrow를 한다거나, 로그를 통해서 프로그램 자체가 잘못되었음을 알려주는 형태로 남기는 편이에요.
@@ -0,0 +1,50 @@
+package blackjack.domain.result;
+
+import blackjack.domain.participant.Dealer;
+import blackjack.domain.participant.Player;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.function.BiFunction;
+
+public enum Rule{
+    // 카드 값의 합이 21을 넘으면 승패 결정 (플레이어 기준으로 선언)
+    PLAYER_BUST(((player, dealer) -> player.isBust()), WinningResult.LOSE, 1),
+    DEALER_BUST(((player, dealer) -> dealer.isBust()), WinningResult.WIN, 2),
+
+    // 카드 값의 합이 21을 넘지 않으면서, 값이 더 큰쪽이 승패결정(이미 여기서 결정, BlackJack 체크 필요없음)
+    PLAYER_HIGHER(((player, dealer) -> player.getCardsSum() > dealer.getCardsSum()), WinningResult.WIN, 3),
+    DEALER_HIGHER(((player, dealer) -> player.getCardsSum() < dealer.getCardsSum()), WinningResult.LOSE, 4),
+
+    // 무승부 결정
+    TIES(((player, dealer) -> player.getCardsSum() == dealer.getCardsSum()), WinningResult.TIE, 5);
+
+    private BiFunction<Player, Dealer, Boolean> compare;
+    private WinningResult winningResult;
+    @Getter
+    private int verificationOrder;
+
+    Rule(BiFunction<Player, Dealer, Boolean> compare, WinningResult winningResult, int verificationOrder) {
+        this.compare = compare;
+        this.winningResult = winningResult;
+        this.verificationOrder = verificationOrder;
+    }
+
+    public Boolean findMatchingRule(Player player, Dealer dealer) {
+        return compare.apply(player, dealer);
+    }
+
+    public WinningResult getWinningResult() {
+        return winningResult;
+    }
+
+    public static WinningResult resultPlayerVersusDealer(Player player, Dealer dealer) {
+
+        return Arrays.stream(Rule.values())
+                .sorted(new RuleComparator())
+                .filter(rule -> rule.findMatchingRule(player, dealer))
+                .findFirst()
+                .orElseThrow(() -> new RuntimeException("일치하는 결과를 찾을 수 없습니다."))
+                .getWinningResult();
+    }
+}

리뷰: 요 로직들이 순서에 따라 영향을 받을 것 같아요. `원래는 플레이어가 버스트인 경우 항상 패배로 간주되는데 무승부 Enum을 맨 위로 올리면, 비긴거로 간주되는 형식`으로여! 그렇다면 기존 `Enum의 순서가 중요하다`는 것을 명시적으로 드러내거나, 다른 방법을 사용해줘야 다른 사람이 실수할 여지를 줄일 수 있을 것 같아요ㅎㅎ(여긴 가장 중요한 로직이니까 더더욱이요!)