-
Notifications
You must be signed in to change notification settings - Fork 0
레디 step2 #13
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
base: main
Are you sure you want to change the base?
레디 step2 #13
Changes from all commits
6f92f8f
c7a83f1
2206919
dec4970
ffbc5e3
ee8b66e
f37f288
285f322
197f942
93e5b86
8aba953
b8b1ef9
d51ecdf
4a03db2
874cd41
52a9b0f
f5c0b4d
87d8f09
2988947
24e020d
2fb529f
09ea27a
02f44e0
6b5c8e6
e166122
e580c16
930c385
c401e21
e5cc892
f22ec42
353e923
2fb5001
28f5b8d
b9f19af
c48d744
8b9c0ca
6ca77d6
4721bfa
7f672e4
5aa83c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| # 블랙잭 규칙 | ||
|
|
||
| ## 참여자 | ||
|
|
||
| * [x] 다시한번 룰 숙지 필요 | ||
|
|
||
| **승** | ||
|
|
||
| * (딜러 카드 합 < 참여자 카드 합) && 참여자가 버스트가 아닌 경우 | ||
| * 딜러가 버스트인 경우 && 참여자가 버스트가 아닌 경우 | ||
|
|
||
| **무** | ||
|
|
||
| * (딜러 카드 합 == 참여자 카드 합) && 참여자가 버스트가 아닌 경우 | ||
|
|
||
| **패** | ||
|
|
||
| * 참여자가 버스트인 경우 | ||
| * (딜러 카드 합 > 참여자 카드 합) | ||
|
|
||
| <br> | ||
|
|
||
| ## 블랙잭 | ||
|
|
||
| * 카드의 갯수가 2장이고 카드의 합이 21인 경우 블랙잭이다. | ||
|
|
||
| <br> | ||
|
|
||
| # 기능 요구 사항 | ||
|
|
||
| * [x] 참여자 이름 입력 받는다. | ||
| * [x] 양 끝 공백을 제거한다. | ||
| * [x] 참여자는 쉼표(,)로 구분한다. | ||
| * [x] null 이나 공백인 경우 예외가 발생한다. | ||
| * [x] 쉼표로 시작시 예외가 발생한다. ex) ,pobi,jason | ||
| * [x] 쉼표로 끝날시 예외가 발생한다. ex) pobi,jason, | ||
| * [x] 쉼표가 연속으로 올시 예외가 발생한다. ex) pobi,,jason | ||
| * [x] 참여자 이름이 중복시 예외가 발생한다. | ||
| * [x] 총 참여자의 수는 2이상 8이하여야 한다. | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 각 참여자의 배팅 금액을 입력받는다. | ||
| * [x] 정수가 아닐 시 예외가 발생한다. | ||
| * [x] 양의 정수가 아닐 시 예외가 발생한다. | ||
| * [x] 10으로 나누어 떨어지지 않으면 예외가 발생한다. | ||
| * 블랙잭시 1.5배를 해주기 위함 | ||
| * [x] 배팅 금액은 최대 100_000_000로 이를 초과시 예외가 발생한다. | ||
| * 건전한 플레이를 하기 위함 | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 딜러가 카드 2장을 분배하다. | ||
| * [x] 카드는 총 6벌을 둔다. (52 * 6) | ||
| * [x] 딜러의 카드를 출력한다. (1장) | ||
| * [x] 참여자의 카드를 출력한다. (2장) | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 참여자는 hit(y) / stay(n)를 선택한다. | ||
| * [x] y, n 가 아닐시 예외가 발생한다. | ||
| * [x] 참여자가 hit을 하는 경우 현재 가지고 있는 카드를 출력한다. | ||
| * [x] 참여자가 hit을 한 적 없이 stay를 하는 경우 현재 가지고 있는 카드를 출력한다. | ||
| * [x] 카드 합이 블랙잭인 경우 블랙잭 메시지를 출력한다. | ||
| * [x] 카드 합이 21 초과시 버스트 메시지를 출력한다. | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 딜러의 카드의 합을 계산한다. | ||
| * [x] 카드 내의 ACE 가 포함된 경우 | ||
| * ACE: 11 | ||
| * 11로 했을 때 카드의 합이 21을 초과한 경우 1로 계산 | ||
| * [x] 17 이상이 될때까지 카드를 받는다. | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 모든 참여자의 카드의 합을 계산한다. | ||
| * [x] 딜러의 승패무를 확인한다. | ||
| * [x] 참여자의 승패무를 확인한다. | ||
| * [x] 게임 결과를 출력한다. | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 딜러의 최종 수익을 계산한다. | ||
| * [x] 수익을 출력시 세자리마다 쉼표를 찍는다. ex) 100,000 | ||
| * [x] 참가자의 최종 수익을 계산한다. | ||
| * [x] 참가자만 블랙잭인 경우 배팅 금액의 1.5배를 받는다. | ||
| * [x] 참가자와 딜러 모두 블랙잭인 경우 배팅한 금액을 돌려받는다. | ||
| * [x] 참가자가 패한다면 배팅한 금액을 잃는다. | ||
| * [x] 참가자가 승리한다면 배팅한 금액을 받는다. | ||
|
|
||
| --- | ||
|
|
||
| ## 리팩터링 목록 | ||
|
|
||
| ~~추상 클래스 활용~~ | ||
|
|
||
| * [x] 카드덱이 비었을때 꺼낸 경우 예외 발생 | ||
| * [x] createEmptyPacket 메서드명 수정 | ||
| * [x] 참가자의 이름 딜러 불가 | ||
| * [x] Name 객체 포장하기 | ||
| * [x] Result 함수형 프로그래밍 사용 | ||
| * [x] TestFixture 사용 | ||
| * [x] 패키지 정리 | ||
| * [x] 메서드 컨벤션 작성 및 확인 | ||
| * [x] final 컨벤션 통일 | ||
| * [x] CardDeck 생성자 닫기 | ||
|
|
||
| <br> | ||
|
|
||
| * [x] 예외 시 재입력 기능 | ||
| * [x] 예외 메시지 상수화 | ||
| * 커스텀 예외 구현 | ||
| * [x] CardDeck 웨어하우스로 바라보기 | ||
| * [ ] ~~컨트롤러 메서드 수정하기~~ | ||
| * [x] ~~Participant 추상 클래스 대신 클래스로 변경하기~~ | ||
| * 추상 클래스를 유지하고 추상 메서드 추가 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package application; | ||
|
|
||
| import controller.BlackJackController; | ||
| import controller.InputController; | ||
| import java.util.Scanner; | ||
| import view.InputView; | ||
| import view.OutputView; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| final Scanner scanner = new Scanner(System.in); | ||
| final InputView inputView = new InputView(scanner); | ||
| final OutputView outputView = new OutputView(); | ||
| final InputController inputController = new InputController(inputView, outputView); | ||
| final BlackJackController blackJackController = new BlackJackController(inputController, outputView); | ||
| blackJackController.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package constants; | ||
|
|
||
| public enum ErrorCode { | ||
|
|
||
| NOT_EXIST_MESSAGE, | ||
| INVALID_SEPARATOR, | ||
| INVALID_INPUT, | ||
| INVALID_SIZE, | ||
| INVALID_BET_AMOUNT, | ||
| DUPLICATE_NAME, | ||
| RESERVED_NAME, | ||
| BLANK_VALUE, | ||
| EMPTY_CARD, | ||
| INVALID_COMMAND, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package controller; | ||
|
|
||
| import domain.Game; | ||
| import domain.amount.Amount; | ||
| import domain.amount.BetAmount; | ||
| import domain.participant.Dealer; | ||
| import domain.participant.Name; | ||
| import domain.participant.Names; | ||
| import domain.participant.Player; | ||
| import domain.participant.Players; | ||
| import dto.ParticipantsDto; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import view.OutputView; | ||
|
|
||
| public class BlackJackController { | ||
|
|
||
| private final InputController inputController; | ||
| private final OutputView outputView; | ||
|
|
||
| public BlackJackController(final InputController inputController, final OutputView outputView) { | ||
| this.inputController = inputController; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| public void run() { | ||
| final Names names = inputController.getNames(); | ||
| final Dealer dealer = new Dealer(); | ||
| final Players players = registerPlayers(names); | ||
|
|
||
| final Game game = new Game(dealer, players); | ||
|
|
||
| initHands(game); | ||
| dealWithPlayers(game); | ||
| dealerTurn(game); | ||
|
|
||
| printFinalResult(players, dealer); | ||
| } | ||
|
|
||
| private void dealerTurn(final Game game) { | ||
| game.dealerTurn(outputView::printDealerTurnMessage); | ||
| } | ||
|
|
||
| private Players registerPlayers(final Names names) { | ||
| final List<Player> playerList = new ArrayList<>(); | ||
| for (final Name name : names.getPlayerNames()) { | ||
| final BetAmount betAmount = inputController.getBetAmount(name.getValue()); | ||
| playerList.add(new Player(name, betAmount)); | ||
| } | ||
| return new Players(playerList); | ||
| } | ||
|
|
||
| private void dealWithPlayers(final Game game) { | ||
| if (game.isAlreadyEnd()) { | ||
| outputView.printDealerBlackJack(); | ||
| return; | ||
| } | ||
| game.checkingPlayersBlackJack(outputView::printBlackJack); | ||
| game.dealWithPlayers( | ||
| inputController::getAnswer, | ||
| outputView::printHands, | ||
| outputView::printPlayerEndMessage); | ||
|
Comment on lines
+60
to
+63
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. game에 메서드를 넘겨주는군요 |
||
| } | ||
|
|
||
| private void initHands(final Game game) { | ||
| game.initHands(outputView::printStartDeal); | ||
| } | ||
|
|
||
| private void printFinalResult(final Players players, final Dealer dealer) { | ||
| outputView.printHandsResult(ParticipantsDto.of(dealer, players)); | ||
| final Map<Player, Amount> finalResult = players.calculateResult(dealer); | ||
| final Amount dealerAmount = dealer.calculateRevenue(finalResult); | ||
| outputView.printGameResult(finalResult, dealerAmount); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package controller; | ||
|
|
||
| import domain.participant.Answer; | ||
| import domain.amount.BetAmount; | ||
| import domain.participant.Names; | ||
| import exception.CustomException; | ||
| import java.util.List; | ||
| import view.InputView; | ||
| import view.OutputView; | ||
|
|
||
| public class InputController { | ||
|
|
||
| private final InputView inputView; | ||
| private final OutputView outputView; | ||
|
Comment on lines
+11
to
+14
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 MVC의 Controller의 역할이 무엇인지 생각해보면 |
||
|
|
||
| public InputController(final InputView inputView, final OutputView outputView) { | ||
| this.inputView = inputView; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| public Names getNames() { | ||
| Names names; | ||
| do { | ||
| names = readNames(); | ||
| } while (names == null); | ||
| return names; | ||
| } | ||
|
|
||
| public Answer getAnswer(String name) { | ||
| Answer answer; | ||
| do { | ||
| answer = readAnswer(name); | ||
| } while (answer == null); | ||
| return answer; | ||
| } | ||
|
|
||
| public BetAmount getBetAmount(String name) { | ||
| BetAmount betAmount; | ||
| do { | ||
| betAmount = readBetAmount(name); | ||
| } while (betAmount == null); | ||
| return betAmount; | ||
| } | ||
|
|
||
| private Names readNames() { | ||
| try { | ||
| List<String> rawNames = inputView.readNames(); | ||
| return Names.from(rawNames); | ||
| } catch (CustomException exception) { | ||
| outputView.printException(exception.getErrorCode()); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| private Answer readAnswer(String name) { | ||
| try { | ||
| String value = inputView.readAnswer(name); | ||
| return Answer.from(value); | ||
| } catch (CustomException exception) { | ||
| outputView.printException(exception.getErrorCode()); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| private BetAmount readBetAmount(final String name) { | ||
| try { | ||
| Long value = inputView.readBetAmount(name); | ||
| return new BetAmount(value); | ||
| } catch (CustomException exception) { | ||
| outputView.printException(exception.getErrorCode()); | ||
| return null; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package domain; | ||
|
|
||
| import domain.participant.Answer; | ||
| import domain.participant.Dealer; | ||
| import domain.participant.Participant; | ||
| import domain.participant.Player; | ||
| import domain.participant.Players; | ||
| import dto.DealerHandsDto; | ||
| import dto.ParticipantDto; | ||
| import dto.ParticipantsDto; | ||
| import java.util.function.BiConsumer; | ||
| import java.util.function.Consumer; | ||
| import java.util.function.Function; | ||
|
|
||
| public class Game { | ||
|
|
||
| private final Dealer dealer; | ||
| private final Players players; | ||
|
|
||
| public Game(final Dealer dealer, final Players players) { | ||
| this.dealer = dealer; | ||
| this.players = players; | ||
| } | ||
|
|
||
| public void initHands(BiConsumer<DealerHandsDto, ParticipantsDto> handsPrinter) { | ||
| dealer.initHands(players); | ||
| handsPrinter.accept(DealerHandsDto.from(dealer), ParticipantsDto.of(players)); | ||
| } | ||
|
Comment on lines
+25
to
+28
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 도메인 내부에서 간접적으로 뷰의 로직을 주입받아 실행하고 있는데, 코드에 명시적인 의존성은 없지만, 실질적인 의존성은 여전히 남아있습니다. 뷰의 메서드 시그니처가 변경된다면 도메인이 변경되어야 하기 때문입니다. 저도 비슷한 코드를 작성했는데, 구구와 리뷰어 모두에게 이런 취지의 피드백을 받았습니다!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 추가적으로, 자바에 기본으로 정의되어있는 함수형 인터페이스는 그 의도를 명확히 전달할 수 없습니다. 꼭 이렇게 하고 싶다면, 별도의 인터페이스를 정의하여 타입으로부터 의도를 설명하는 것이 좋을 것 같습니다. |
||
|
|
||
| public boolean isAlreadyEnd() { | ||
| return dealer.isBlackJack(); | ||
| } | ||
|
|
||
| public void checkingPlayersBlackJack(Consumer<String> blackJackPrinter) { | ||
| players.filter(Participant::isBlackJack) | ||
| .forEach(player -> blackJackPrinter.accept(player.getName())); | ||
| } | ||
|
|
||
| public void dealWithPlayers(Function<String, Answer> answerReader, | ||
| Consumer<ParticipantDto> handsPrinter, | ||
| Consumer<Boolean> endMessagePrinter) { | ||
| players.forEach(player -> deal(player, answerReader, | ||
| handsPrinter, | ||
| endMessagePrinter | ||
| )); | ||
| } | ||
|
|
||
| public void deal(final Player player, Function<String, Answer> answerReader, | ||
| Consumer<ParticipantDto> handsPrinter, | ||
| Consumer<Boolean> endMessagePrinter) { | ||
| if (player.isBlackJack()) { | ||
| return; | ||
| } | ||
| boolean handsChanged = false; | ||
| boolean turnEnded = false; | ||
|
|
||
| while (!turnEnded) { | ||
| final Answer answer = answerReader.apply(player.getName()); | ||
| dealer.deal(player, answer); | ||
| printHandsIfRequired(player, handsChanged, answer, handsPrinter); | ||
| handsChanged = true; | ||
| turnEnded = isTurnEnded(player, answer, endMessagePrinter); | ||
| } | ||
| } | ||
|
|
||
| public void dealerTurn(Runnable dealerTurnMessagePrinter) { | ||
| if (players.isAllBust()) { | ||
| return; | ||
| } | ||
| dealer.deal(); | ||
| for (int i = 0; i < dealer.countAddedHands(); i++) { | ||
| dealerTurnMessagePrinter.run(); | ||
| } | ||
| } | ||
|
|
||
| private void printHandsIfRequired(final Player player, final boolean handsChanged, final Answer answer, | ||
| Consumer<ParticipantDto> handsPrinter) { | ||
| if (shouldShowHands(handsChanged, answer)) { | ||
| handsPrinter.accept(ParticipantDto.from(player)); | ||
| } | ||
| } | ||
|
|
||
| private boolean isTurnEnded(final Player player, final Answer answer, Consumer<Boolean> endMessagePrinter) { | ||
| if (player.canDeal()) { | ||
| return !answer.isHit(); | ||
| } | ||
| endMessagePrinter.accept(player.isBust()); | ||
| return true; | ||
| } | ||
|
|
||
| private boolean shouldShowHands(final boolean handsChanged, final Answer answer) { | ||
| return answer.isHit() || !handsChanged; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Controller란, 어떤 요청이 들어왔을 때 그 요청을 처리할 수 있는 model 에게 그 요청의 처리를 위임하고, 처리 결과를 View에 전달하는 것을 말합니다. 하지만, 우리 미션에서는 요청을 표현하는 것이 없습니다. 그저 프로그램을 실행하면 알아서 동작을 하기 때문에 Controller라는 이름은 부적절하다고 생각합니다!