Backend/Java

[숫자야구게임 Step2] 실제 애플리케이션이 동작할 코드를 작성하자

Seyun(Marco) 2024. 4. 26. 10:23
728x90

서론

  • 이제 실제 콘솔 애플리케이션이 동작하도록 실제 View와 Controller의 결합을 해보도록 하겠습니다.

View

  • 실제 사용자가 값을 입력하고, 로직이 실행된 뒤의 결과를 출력하기 위한 계층으로 우리는 콘솔 애플리케이션이니 입력은 Scanner 를 통해 받고, 출력은 System.out.println 을 통해 합니다.
public static int inputGameRecordId() {
    System.out.println("게임 기록을 조회할 ID를 입력해주세요.");

    return scanner.nextInt();
}
  • 입력 받는 값은 게임 기록을 조회할 ID입니다.
  • 단순히 이걸 입력 받을 수 있는 메서드를 만들어서 처리해주면 됩니다.
package baseball.view;

import baseball.dto.CheckBallResponse;
import baseball.dto.GameRecordResponse;
import baseball.dto.GameRecordsResponse;

import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

public class OutputView {

    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd hh:mm:ss";
    private static final String DELIMITER = "";

    private OutputView() {
    }

    public static void printExitMessage() {
        System.out.println("애플리케이션이 종료되었습니다.");
    }

    public static void printResult(final CheckBallResponse checkBallResponse) {
        if (checkBallResponse.isNotting()) {
            System.out.println("낫싱");
            return;
        }

        printStrikeAndBallCount(checkBallResponse.strikeCount(), checkBallResponse.ballCount());
        printGameSuccessMessage(checkBallResponse.isSuccess());
    }

    public static void printPickComputerNumbers() {
        System.out.println("컴퓨터가 숫자를 뽑았습니다.");
    }

    public static void printErrorMessage(final String message) {
        System.out.println(message);
    }

    public static void printGameRecords(final List<GameRecordsResponse> gameRecords) {
        System.out.println("게임 기록");
        if (gameRecords.isEmpty()) {
            System.out.println("기록이 없습니다.");
            System.out.println("-------기록 종료-------");
            return;
        }
        for (final GameRecordsResponse gameRecord : gameRecords) {
            final String stringBuilder = "[" + gameRecord.id() + "] " +
                    "시작시간: " +
                    gameRecord.startAt().format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)) +
                    " / " +
                    "종료시간:" + gameRecord.endAt().format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)) +
                    " / " +
                    "횟수: " + gameRecord.playerTimes();

            System.out.println(stringBuilder);
        }
    }

    public static void printGameRecord(final GameRecordResponse gameRecord) {
        System.out.println(gameRecord.id() + "번 게임 결과");

        gameRecord.playerRecordResponses().forEach(playerRecord -> {
            final String playerNumber = playerRecord.numbers()
                    .stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(DELIMITER));
            System.out.println("숫자를 입력해주세요: " + playerNumber);
            if (playerRecord.isNotting()) {
                System.out.println("낫싱");
                return;
            }

            printStrikeAndBallCount(playerRecord.strikeCount(), playerRecord.ballCount());
            printGameSuccessMessage(playerRecord.isSuccess());
        });

        System.out.println("-------기록 종료-------");
    }

    private static void printStrikeAndBallCount(final int strikeCount, final int ballCount) {
        System.out.println(strikeCount + "스트라이크 " + ballCount + "볼");
    }

    private static void printGameSuccessMessage(final boolean isSuccess) {
        if (isSuccess) {
            System.out.println("3개의 숫자를 모두 맞히셨습니다.");
            System.out.println("-------게임 종료-------");
        }
    }
}

  • 기록에 맞게 View에 코드를 짜주면 됩니다.
  • 큰 어려운 로직은 없어서 따로 설명은 하지 않을려고 하빈다.
  • 그러나 기존에 코드와 중복코드들이 존재할 수 있습니다. 그런 부분들을 메서드로 추출해서 코드를 제거해줍니다.

Application

package baseball;

import baseball.controller.BaseBallGameController;
import baseball.domain.Commend;
import baseball.dto.CheckBallResponse;
import baseball.dto.CheckBallsRequest;
import baseball.dto.GameRecordResponse;
import baseball.dto.GameRecordsResponse;
import baseball.generator.BaseBallNumberGenerator;
import baseball.generator.BaseBallNumberShuffleGenerator;
import baseball.repository.GameRepositoryImpl;
import baseball.view.InputView;
import baseball.view.OutputView;

import java.util.List;

public class GameApplication {
    private static final BaseBallNumberGenerator baseBallNumberGenerator = new BaseBallNumberShuffleGenerator();
    private static final BaseBallGameController baseBallGameController = new BaseBallGameController(new GameRepositoryImpl());

    public static void run() {
        Commend commend = Commend.END;
        try {
            do {
                commend = Commend.of(InputView.inputMenu());
                if (commend == Commend.START) {
                    final int gameId = baseBallGameController.gameStart(baseBallNumberGenerator);
                    OutputView.printPickComputerNumbers();
                    gameInProgress(gameId);
                } else if (commend == Commend.GAME_RECORD) {
                    final List<GameRecordsResponse> gameRecords = baseBallGameController.getGames();
                    OutputView.printGameRecords(gameRecords);
                    final int gameId = InputView.inputGameRecordId();

                    final GameRecordResponse game = baseBallGameController.getGame(gameId);

                    OutputView.printGameRecord(game);
                }
            }
            while (commend != Commend.END);
            applicationEnd();
        } catch (final Exception e) {
            OutputView.printErrorMessage(e.getMessage());
            run();
        }
    }

    private static void applicationEnd() {
        OutputView.printExitMessage();
    }

    private static void gameInProgress(final int gameId) {
        try {
            boolean isFinished = true;

            while (isFinished) {
                final List<Integer> userNumbers = InputView.inputNumbers();
                final CheckBallsRequest checkBallsRequest = new CheckBallsRequest(userNumbers, gameId);
                final CheckBallResponse checkBallResponse = baseBallGameController.checkBalls(checkBallsRequest);
                OutputView.printResult(checkBallResponse);
                isFinished = !checkBallResponse.isSuccess();
            }
        } catch (final Exception e) {
            OutputView.printErrorMessage(e.getMessage());
            gameInProgress(gameId);
        }
    }
}

  • 해당 메서드에선 각 View Controller를 연결해서 처리하는 행위를 해줍니다.
  • 실제 웹 애플리케이션과는 다르지만, 여기서 최대한 사용자 입력과 출력에 대한 부분들을 Controller에게 전달하는 것을 목표로 하였습니다.
  • if문에 이번엔 GAME_RECORD를 추가해 처리하도록 로직을 작성하였습니다.
public class Application {
    public static void main(String[] args) {
        GameApplication.run();
    }
}

  • 마지막 main 메서드에서는 GameApplication의 run 메서드만 호출하면 끝입니다.

간단한 회고

  • 이번엔 CRUD에서 R을 초점을 맞추는 작업을 해봤습니다.
  • Step2부터는 실제 했던 숫자야구게임을 좀 더 요구사항을 추가하여 진행하고 있습니다.
  • 초반에 Game이라는 도메인을 만들지 않아, 지금처럼 확장하려고 할때 어려움이 있었던거 같습니다.
  • 이번에 Game이라는 도메인을 만들어 아키텍처를 변경하였는데, 아키텍처를 변경하는 작업은 쉬운게 아니라는걸 깨닫는거 같습니다.

결론

  • 이제 다음 Step3로 넘어갑니다.
  • Step3의 요구사항은 통계입니다. 게임을 진행했던 부분들에 대한 간단한 통계를 보여줄 예정이빈다.
  • 간단한 와이어 프레임을 같이 첨부합니다.
게임을 새로 시작하려면 1, 기록을 보려면 2, 통계를 보려면 3, 종료하려면 9을 입력하세요.
1

컴퓨터가 숫자를 뽑았습니다.

숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요: 145
1볼
숫자를 입력해주세요: 671
2볼
숫자를 입력해주세요: 216
1스트라이크
숫자를 입력해주세요: 713
3스트라이크

3개의 숫자를 모두 맞히셨습니다.
-------게임 종료-------

게임을 새로 시작하려면 1, 기록을 보려면 2, 통계를 보려면 3, 종료하려면 9을 입력하세요.
2

- [1] / 시작시간: 2024. 04. 07 23:12 / 종료시간: 2024. 04. 07 23:13 / 횟수: 5

게임 기록을 조회할 ID를 입력해주세요.
1

숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요: 145
1볼
숫자를 입력해주세요: 671
2볼
숫자를 입력해주세요: 216
1스트라이크
숫자를 입력해주세요: 713
3스트라이크

3개의 숫자를 모두 맞히셨습니다.
-------게임 종료-------
-------기록 종료-------

게임을 새로 시작하려면 1, 기록을 보려면 2, 통계를 보려면 3, 종료하려면 9을 입력하세요.
3

가장 적은 횟수: 5회 - [1]
가장 많은 횟수: 5회 - [1]
평균횟수: 5회

-------통계 종료-------

게임을 새로 시작하려면 1, 기록을 보려면 2, 통계를 보려면 3, 종료하려면 9을 입력하세요.
9

애플리케이션이 종료되었습니다.
  • 전체적인 Step2의 애플리케이션의 코드는 아래서 확인가능합니다.
  • GitHub - ksy90101/java-baseball at step2
  • 이 글과 관련된 피드백이 있다면 언제든 댓글로 남겨주세요!
728x90
728x90