나를 기록하다
article thumbnail
반응형

숫자야구

배우는 걸 좋아하지만 비전공자로써 함께 공부할 동료나 멘토가 없어서 어려움이 많았다. 학교는 본래의 전공으로 졸업을 했기에 부트캠프나 국비학원을 알아봤는데 강의에 대한 후기가 너무 갈려서 제대로 된 커리큘럼과 동료들이 있는 곳에서 동료들과 교류하며 성장하고 싶다는 생각에 우아한테크코스를 지원하였다.

더군다나 우아한테크코스에서는 프리코스 과정을 모든 지원자들에게 참가할 자격을 주기 때문에 높은 경쟁률(대략 30대 1)에도 프리코스를 경험하고자 도전하였다. 요즘은 다른 공부를 제쳐두고 프리코스에 몰입하느라 블로그 글과 알고리즘 문제는 제쳐두고 설계와 구현에 집중하고 있다. 이제부터 프리코스를 진행하며 매주 회고록을 작성하려 한다. 이 과정을 통해 객체지향적 사고를 할 수 있고 설계를 배우고자 한다.

1주차 미션 - 숫자 야구

1주차 미션은 숫자 야구이다. 작년 프리코스 온보딩과 숫자 야구만 미리 풀어본 상태에서 프리코스를 시작했기에 숫자 야구는 비교적 친숙했다. 하지만 MVC 설계나 스트림, 람다와 같은 개념들을 아직 완전히 학습하지 못한 상태에서 이번 과제를 진행했기에 과제를 진행하면서 붖고한 개념들을 학습하고 구현하는 방식으로 진행했다.

과제 설명

과제에서 요구하는 내용은 위와 같다.

이전 과제와 달라진 점은 JDK 17을 사용한다는 점이다.

JDK 17은 오라클 홈페이지에서 다운받아서 진행하였다.

우선 입학설명회와 OT에서 포비님이 말씀해주신 것처럼 아직 어색한 개념인 헥사고날, TDD, DDD, MVC와 같은 개념을 무조건적으로 적용하려고 하지 않고 아는 범위 내에서 객체지향적인 설계를 하려고 노력했다.

그래서 미션을 시작한 첫 날, 구현할 기능 목록을 작성하고 설계하는 데 대부분의 시간을 할애하였다.

혼자만의 힘으로 설계를 하면서 최근에 읽었던 객체지향의 사실과 오해라는 책을 통해 학습한 객체지향의 개념이 큰 도움이 되었다.

혹여 아직 객체지향의 사실과 오해라는 책을 읽어보지 않았다면 아래의 링크를 통해 읽어보길 추천드린다.


객체지향의 사실과 오해 리뷰

[객체지향의 사실과 오해] 1장. 협력하는 객체들의 공동체

[객체지향의 사실과 오해] 2장. 이상한 나라의 객체

[객체지향의 사실과 오해] 3장. 타입과 추상화

[객체지향의 사실과 오해] 4장. 역할, 책임, 협력

[객체지향의 사실과 오해] 5장. 책임과 메시지

[객체지향의 사실과 오해] 6장. 객체 지도

[객체지향의 사실과 오해] 7장. 함께 모으기


구현에 들어가기 전 처음에 작성한 구현할 기능 목록은 아래와 같다.


(초기)✏️️ 구현할 기능 목록

컴퓨터

  • 책임: 무작위 숫자를 생성할 책임을 가진다.
  • 범위: 1 이상 9 이하
  • 조건: 중복 금지

사용자

  • 책임: 조건에 맞는 숫자를 입력할 책임을 가진다.

심판

  • 책임: 볼, 스트라이크, 낫싱을 심판하는 책임을 가진다.
    • 볼: 같은 수가 다른 자리에 있는 경우
    • 스트라이크: 같은 수가 같은 자리에 있는 경우
    • 낫싱: 같은 수가 하나도 없는 경우

게임 관리자

  • 책임: 게임을 재시작 또는 종료할 책임을 가진다.
    • 3개의 숫자를 모두 맞히면 게임 종료
    • 게임이 종료 시 1을 누르면 재시작, 2를 누르면 게임 종료

유효성 검사 도구

  • 책임: 입력값이 유효한 값인지 검사하고 잘못된 값을 입력할 경우 예외를 발생시킬 책임을 가진다.
      • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.
    • 입력 값이 3자리가 아닌 경우 예외 처리
    • 입력 값의 각 자리 숫자가 1 이상 9 이하가 아닐 경우 예외 처리
    • 입력 값이 중복된 숫자를 포함하는 경우 예외 처리

이렇게 구성한 이유는 아직 객체지향적 설계에 능숙하지 않기에 도메인 위주로 설계하고 하나의 객체에 하나의 책임을 할당하려 했다.

물론 과제를 진행하면서 추가적으로 필요하다고 판단하여 추가하고 삭제한 부분도 있다. 최종 기능 목록은 아래에 다시 첨부하겠다.

도메인 설계

도메인 설계

내가 설계한 초기 도메인이다. 게임을 시작하라는 메시지를 받으면 컴퓨터에서 랜덤 숫자를 생성한다.

그 후 사용자에게 숫자를 입력하라는 메시지를 전달하면 사용자는 숫자를 입력하고 입력한 값을 유효성 검사 도구에게 입력을 검증하라는 메시지를 전달한다. 유효성 검사 도구는 유효성 검사를 마친 결과값을 심판에게 결과를 판정하라는 메시지와 함께 전달한다.

심판은 전달받은 결과값을 판정하여 결과를 출력하고 게임 관리자에게 사용자의 입력값에 따라 재시작 또는 종료를 진행하라는 메시지를 전달한다.

위 그림과 같이 설계하고 개발을 진행하면서 기능 단위를 더 분리해야 할 필요성을 느꼈다.

우선 사용자 도메인은 숫자를 입력할 책임 이외에 존재하지 않기에 사용자 도메인을 없애고 컨버터라는 도메인을 새로 생성하여 문자열 형태의 입력값을 정수형 배열로 변환하는 책임을 부여했다.


개발을 진행하며 수정한 구현할 기능 목록과 도메인 설계는 아래와 같다.

(최종)✏️️ 구현할 기능 목록

진행 순서

  1. 컴퓨터
    • 1에서 9까지 서로 다른 임의의 수 3개를 선택하여 배열에 저장한다.
  2. 사용자 값 입력
  3. 유효성 검사 도구
    • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료시킨다.
      • 입력 값의 개수 확인(3자리)
      • 입력 값이 1 이상 9 이하의 숫자인지 확인
      • 입력 값이 중복된 숫자를 포함하고 있는지 확인
  4. 컨버터
    • 입력 받은 String 타입의 문자열을 List 타입의 배열로 변환한다.
  5. 심판
    • 사용자 값과 컴퓨터의 값을 비교하여 볼 / 스트라이크 / 낫싱을 판정한다.
  6. 게임 관리(Controller)
    • 게임을 진행한다.
    • 3개의 숫자를 모두 맞히면 게임을 종료한다.
    • 1을 누르면 게임을 재시작하고, 2를 누르면 게임을 종료한다.

기능 및 역할(책임)

컴퓨터

  • 책임: 무작위 숫자를 생성할 책임을 가진다.
    • 범위: 1 이상 9 이하
    • 조건: 중복 금지

유효성 검사 도구

  • 책임: 입력 값이 유효한 값인지 검사하고 잘못된 값을 입력할 경우 예외를 발생시킬 책임을 가진다.
    • 예외 - 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.
    • 입력 값이 3자리가 아닌 경우 예외 처리
    • 입력 값이 1 이상 9 이하의 숫자가 아닌 경우 예외 처리
    • 입력 값이 중복된 숫자를 포함하는 경우 예외 처리

컨버터

  • 책임: 문자열 형태의 입력 값을 정수형 배열로 변환한다.

심판

  • 책임: 볼과 스트라이크를 판정하는 책임을 가진다.
  • 기준
    • 볼: 같은 수가 다른 자리에 있는 경우
    • 스트라이크: 같은 수가 같은 자리에 있는 경우

게임 관리(Controller)

  • 책임: 게임을 재시작 또는 종료한다.
    • 3개의 숫자를 모두 맞히면 게임 종료
    • 게임이 종료 시 1을 누르면 재시작, 2를 누르면 게임 종료

도메인 설계

도메인 설계

도메인 설계에서는 사용자를 제외시켰고, 유효성 검사 도구를 입력을 받는 순간 바로 검사를 할 수 있게 앞 부분에 위치시켰으며 검사를 마친 입력 값을 컨버터를 통해 변환하도록 다음에 위치시켰다.


UML 다이어그램

UML 다이어그램

외부에서 호출해야 하는 메서드들을 제외하고는 private로 설정하였다.

또한 InputView와 OutputView를 만들어 모델(컴퓨터, 심판)의 데이터를 컨트롤러를 통해 가져와서 뷰를 호출하여 출력하는 방식으로 구현하였다.

InputView와 OutputView를 만들었는데, OutputView의 역할에 대해 의문이 들었다.

ball과 strike를 계산해서 출력하는 것을 역할로 정했었는데, ball과 strike에 대한 정보를 알고 있는 심판(Referee)이 판정을 하면서 출력을 하면 되지 않을까? 이런 생각에 OutputView를 없애고 printJudgement라는 메서드를 심판 클래스에 추가하였다.

또한 기존의 OutputView에 존재하던 종료 메시지는 Controller에 printEndMessage 메서드를 생성하여서 해결하였다.

Computer - 숫자를 생성할 책임을 가진다.

package baseball.domain;

import camp.nextstep.edu.missionutils.Randoms;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public class Computer {

    private final List<Integer> computerNumbers;

    public Computer(int size) {
        computerNumbers = generateComputerNumbers(size);
    }

    private List<Integer> generateComputerNumbers(int size) {
        Set<Integer> uniqueNumbers = new LinkedHashSet<>();
        while (uniqueNumbers.size() < size) {
            int randomNumber = Randoms.pickNumberInRange(1, 9);
            uniqueNumbers.add(randomNumber);
        }
        return new ArrayList<>(uniqueNumbers);
    }

    public List<Integer> getComputerNumbers() {
        return computerNumbers;
    }
}

생성할 숫자의 개수인 size를 매개변수를 받는 기본 생성자의 생성과 함께 정수형 배열인 computerNumbers가 생성되게 하였고, computerNumbers는 private final으로 선언하여 외부에서 접근할 수 없고 값을 재할당할 수 없게 하였으며 getComputerNumbers를 통해서만 값을 받을 수 있게 하였다.

Validator - 유효성 검사를 수행할 책임을 가진다.

package baseball.util;

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

public class Validator {

    public Validator() {
        // initialize
    }

    public void checkValid(String input, int size) {
        isValidSize(input, size);
        isInteger(input);
        isDuplicate(input);
    }

    public void checkValid(String input, String retry, String exit) {
        isValidNumber(input, retry, exit);
    }

    private void isValidSize(String input, int size) throws IllegalArgumentException {
        if (input.length() != size) {
            throw new IllegalArgumentException("올바른 자리 수의 값을 입력하세요.");
        }
    }

    private void isInteger(String input) throws IllegalArgumentException {
        for (char c : input.toCharArray()) {
            if (c <= '0' || c > '9') {
                throw new IllegalArgumentException("1부터 9까지의 수를 입력하세요.");
            }
        }
    }

    private void isDuplicate(String input) throws IllegalArgumentException {
        List<Integer> inputNumbers = new ArrayList<>();
        char[] charNumbers = input.toCharArray();
        for (char charNumber : charNumbers) {
            inputNumbers.add((int) charNumber);
        }
        if (inputNumbers.size() != new HashSet<>(inputNumbers).size()) {
            throw new IllegalArgumentException("중복된 숫자를 포함하고 있습니다.");
        }
    }

    private void isValidNumber(String input, String retry, String exit) throws IllegalArgumentException {
        if (!input.equals(retry) && !input.equals(exit)) {
            throw new IllegalArgumentException("1(재시작)과 2(종료) 중 하나의 수를 입력하세요.");
        }
    }
}

유효성 검사에는 크게는 2가지 검사가 존재한다.

  • checkValid(String input, int size) - 사용자 입력 숫자를 검사한다. (세부 검사로 3가지 메서드가 존재)
    • isValidSize: 올바른 자리 수의 값을 입력하였는지 검사한다.
    • isInteger: 1부터 9까지의 숫자가 입력하였는지 검사한다. 0이 포함되거나 숫자가 아닐 경우 예외가 발생한다.
    • isDuplicate: 중복된 숫자를 포함하고 있는지 검사한다.
  • checkValid(String input, String retry, String exit) - 게임 종료 시의 사용자 입력을 검사한다.(재시작 또는 종료만 입력 가능)
    • isValidNumber: retry와 exit에 설정한 값 둘 중 하나라도 맞으면 진행하고 둘다 아니라면 IllegalArgumentException과 함께 예외 문구를 출력한다.

첫 번째 검사에서 3가지 검사는 private로 외부에서 알 수 없게 하였고 3가지 검사를 수행하는 메서드인 checkValid 메서드는 public으로 선언하여 외부에서 checkValid를 수행하면 예외가 발생하는지 여부를 체크하고 예외가 발생한다면 IllegalArgumentsException과 함께 예외 별로 문구가 출력된다.

두 번째 검사에서는 오버로딩을 이용하여 checkValid를 수행하는데, isValidNumber의 반환값에 따라 retry와 exit 중 해당 값이 없을 때 예외를 발생시키고 그 외에는 정상적으로 통과한다.

Converter - 문자열 입력 값을 정수형 배열로 변환할 책임을 가진다.

package baseball.util;

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

public class Converter {

    private final List<Integer> seperatedNumbers;

    public Converter(String input) {
        seperatedNumbers = seperate(input);
    }

    private List<Integer> seperate(final String input) {
        List<Integer> numbers = new ArrayList<>();
        for (char c : input.toCharArray()) {
            numbers.add(Character.getNumericValue(c));
        }
        return numbers;
    }

    public List<Integer> getSeperatedNumbers() {
        return seperatedNumbers;
    }
}

사용자 입력은 이후 Validator의 checkValid를 통해 유효성 검사를 마쳐서 예외가 발생하지 않았다면 Converter에게 문자열 입력 값을 정수형 배열로 변환하라는 메시지를 던지게 하였다.

입력값을 변환하라는 메세지를 받으면 Converter에서 seperate를 수행하여 입력 문자를 char[] 배열로 변환하고 char[] 배열을 순회하면서 seperatedNumbers라는 정수형 배열에 값을 저장한다.

여기서도 input을 매개변수로 전달받으면 seperate(input)을 수행하여 seperatedNumbers에 저장하는 생성자와 seperatedNumbers를 호출하는 getSeperatedNumbers 메서드를 제외하고는 private로 선언하여 외부에서 볼 수 없게 은닉하였다.

 

Referee - 볼 / 스트라이크를 판정할 책임을 가진다.

package baseball.domain;

import java.util.List;

public class Referee {
    private int ball;
    private int strike;

    public boolean isThreeStrike(List<Integer> computerNumbers, List<Integer> userNumbers) {
        strike = 0;
        count(computerNumbers, userNumbers);
        return strike == 3;
    }

    public void judge(List<Integer> computerNumbers, List<Integer> userNumbers) {
        count(computerNumbers, userNumbers);
        printJudgment(ball, strike);
    }

    private void count(List<Integer> computerNumbers, List<Integer> userNumbers) {
        for (int i = 0; i < userNumbers.size(); i++) {
            int computerNumber = computerNumbers.get(i);
            int userNumber = userNumbers.get(i);
            if (isStrike(computerNumber, userNumber)) {
                strike += 1;
            }
            if (!isStrike(computerNumber, userNumber) && isBall(computerNumbers, userNumber)) {
                ball += 1;
            }
        }
    }

    private boolean isStrike(int computerNumber, int userNumber) {
        return computerNumber == userNumber;
    }

    private boolean isBall(List<Integer> computerNumbers, int userNumber) {
        return computerNumbers.contains(userNumber);
    }

    private void printJudgment(int ball, int strike) {
        nothing(ball, strike);
        onlyBall(ball, strike);
        onlyStrike(ball, strike);
        ballStrike(ball, strike);
    }

    private void nothing(int ball, int strike) {
        if (ball == 0 && strike == 0) {
            System.out.println("낫싱");
        }
    }

    private void onlyBall(int ball, int strike) {
        if (ball != 0 && strike == 0) {
            System.out.println(ball + "볼");
        }
    }

    private void onlyStrike(int ball, int strike) {
        if (ball == 0 && strike != 0) {
            System.out.println(strike + "스트라이크");
        }
    }

    private void ballStrike(int ball, int strike) {
        if (ball != 0 && strike != 0) {
            System.out.println(ball + "볼 " + strike + "스트라이크");
        }
    }
}

Referee의 주요 기능은 judge(판정하라)이다. Referee의 judge 메서드는 Computer에서 생성한 computerNumbers와 Converter를 통해 입력 값을 분리한 userNumbers를 매개변수로 받는다.

그리고 computerNumber과 userNumber이 일치하면 true를 반환하는 isStrike 메서드를 이용하여 true이면 strike를 1 증가시키고, isStrike가 false인 경우 computerNumbers가 userNumber를 포함하면(같은 수가 다른 자리 수에 있으면) isBall이 true가 반환되고 ball을 1 증가시키도록 구현하였다.

마찬가지로 판정하는 judge(), 스트라이크와 볼 값을 받아오는 getBall(), getStrike() 메서드를 제외하고는 private로 은닉하였다.

 

+) 추가로 IDE 플러그인인 CodeMetrics에서 복잡도를 계산해주는데 기존의 코드에서는 printJudgment 내에 모든 if문을 합쳐서 처리하였더니 복잡도가 너무 높다고 낮추라는 경고 문구가 나왔다. 그래서 nothing, onlyBall, onlyStrike, ballStrike로 나누어서 메서드를 구현하고 각각의 메서드를 printJudgment에서 합쳐서 실행하도록 구현하였다.(이게 올바른 방식인진 모르겠다. 차후 공부해서 포스팅하겠다.)

 

InputView - 입력 뷰

package baseball.view;

public class InputView {

    private InputView() {
        // empty constructor
    }

    public static void requestInputData() {
        System.out.print("숫자를 입력해 주세요 : ");
    }

    public static void printRetryMessage() {
        System.out.println("게임을 다시 시작하려면 1. 종료하려면 2를 입력하세요.");
    }
}

입력 뷰에는 static으로 선언한 requestInputData(), printRetryMessage()가 있어 호출 시 원하는 메세지를 출력하도록 하였다.

 

Controller - Model, Utils, View 값 연결

내가 설계한 내용이 완전한 MVC인지는 아직 개념이 부족하여 확신이 서진 않지만, 나름대로 Model과 View가 서로를 의존하지 않고 서로를 모르도록 구현했다.

 

+) 다들 Service를 구현하였던데 Service 관련 자료를 찾아보았으나 아직까지는 왜 Service와 Controller를 분리해야 하는지 명확하게 이해하지 못했다. 이번 미션이 난이도가 낮은 미션이라 그런가? 2주차 미션이 곧 시작하는데 직접 몸소 느껴보면서 필요성을 알게 되면 관련 내용을 다시 포스팅해볼 계획이다.

package baseball;

import baseball.domain.Computer;
import baseball.domain.Referee;
import baseball.util.Converter;
import baseball.util.Validator;
import baseball.view.InputView;
import java.util.ArrayList;
import java.util.List;

public class Controller {

    public static final int SIZE = 3;
    public static final String RETRY = "1";
    public static final String EXIT = "2";
    private String userNumber;
    List<Integer> computerNumbers;
    List<Integer> userNumbers;
    InputView inputView = new InputView();
    Validator validator;
    Converter converter;
    Referee referee;

    public void run() {
        init();
        play();
        end();
    }

    public void init() {
        initComputerNumbers();
        generateComputerNumbers();
    }

    public void play() {
        do {
            initGame();
            inputUserNumber();
            checkInputData();
            generateUserNumbers();
            requestJudge(computerNumbers, userNumbers);
        } while (!referee.isThreeStrike(computerNumbers, userNumbers));
    }

    public void end() {
        printEndMessage();
        String message = inputView.printRetryMessage();
        validator.checkValid(message, RETRY, EXIT);
        if (message.equals(RETRY)) {
            run();
        }
    }

    private void initComputerNumbers() {
        computerNumbers = new ArrayList<>();
    }

    private void generateComputerNumbers() {
        Computer computer = new Computer(SIZE);
        computerNumbers = computer.getComputerNumbers();
    }


    private void initGame() {
        userNumbers = new ArrayList<>();
        validator = new Validator();
        referee = new Referee();
    }

    private void inputUserNumber() {
        userNumber = inputView.inputNumber();
    }

    private void checkInputData() {
        validator.checkValid(userNumber, SIZE);
    }

    private void generateUserNumbers() {
        converter = new Converter(userNumber);
        userNumbers = converter.getSeperatedNumbers();
    }

    private void requestJudge(List<Integer> computerNumbers, List<Integer> userNumbers) {
        referee.judge(computerNumbers, userNumbers);
    }

    private void printEndMessage() {
        System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
    }

}

우선 내부적으로 은닉한 메서드들부터 설명하겠다.

initComputerNumbers() - 컴퓨터 숫자 배열 초기화

  • 컴퓨터 숫자 배열인 computerNumbers를 초기화하는 기능

generateComputerNumbers() - 컴퓨터 숫자 배열 생성

  • Computer 생성자에 매개변수인 SIZE를 넣어 SIZE 크기의 컴퓨터 인스턴스를 생성
  • computer.getComputerNumbers()를 호출하여 computerNumbers에 저장

initGame() - 게임 초기화

  • userNumbers 초기화, Validator & Referee 생성자 호출

InputUserNumber() - 사용자 입력 요청, 사용자 입력

  • InputView의 inputNumber() 메서드를 실행하여 사용자 입력을 요청하고 입력값을 userNumber에 저장한다.

checkInputData() - 입력 값 검사

  • validator.checkValid(inputData, SIZE) - 유효성 검사 도구가 입력 값과 사이즈를 전달받고 유효성을 검사하라

generateUserNumbers() - 사용자 숫자 배열 생성

  • inputData를 매개변수로 컨버터 생성자에 전달
  • userNumbers에 컨버터의 getSeperatedNumbers()의 값을 저장한다.

requestJudge(List<Integer> computerNumbers, List<Integer> userNumbers) - 심판 판정 요청

  • 심판에게 컴퓨터 숫자 배열과 사용자 숫자 배열을 전달해 볼, 스트라이크를 판정할 것을 요청한다.
  • 심판은 판정한 볼과 스트라이크를 바탕으로 볼, 스트라이크를 출력한다.

 

위 메서드들을 통합한 컨트롤러의 메인 로직

사용자는 게임을 진행하기만 하면 되기에 run(), init(), play(), end() 메서드만 public으로 공개하고 나머지 비즈니스 로직 메서드는 private로 제한하여 외부에서 볼 수 없게 은닉화하였다. 또한 Application의 main에서는 Controller의 run()만 실행하면 게임이 진행이 되도록 run에 최종적인 메서드들을 포함시켰다.

 

run() - 컨트롤러의 모든 로직 실행 메서드

  • init() - 게임 초기화
  • play() - 게임 플레이
  • end() - 게임 종료
  • 위 메서드들을 순차적으로 실행하는 컨트롤러의 메서드

init() - 게임 초기화. 게임을 처음 시작할 때, 사용자가 게임 재시작을 요청할 때 게임을 초기화하는 로직을 담은 메서드

  • initComputerNumbers() - 컴퓨터 숫자 배열 초기화
  • generateComputerNumbers() - 컴퓨터 숫자 배열 생성

play() - 게임 플레이. 스트라이크가 3이 아닌 경우 반복하여 게임을 진행하는 로직을 담은 메서드

  • do - while 반복문을 통해 referee.isThreeStrike(computerNumbers, userNumbers)가 false 경우 아래 과정을 반복한다.
    1. initGame() - 게임 초기화
    2. inputUserNumber() - 사용자 입력
    3. checkInputData() - 입력 값 검사
    4. generatedUserNumbers() - 사용자 숫자 배열 생성
    5. requestJudge(computerNumbers, userNumbers) - 심판 판정 요청

end() - 게임 종료. 스트라이크가 3일 때 종료 메세지를 출력 후 게임 재시작 또는 게임 종료를 선택하는 로직을 담은 메서드

  • printEndMessage() - 게임 종료 메세지 출력
  • String message = InputView.printRetryMessage() - 재시작 또는 종료를 선택하라는 메세지 출력 후 사용자 입력을 저장
  • validator.checkValid(message, RETRY, EXIT) - RETRY 또는 EXIT이 아닐 경우 예외 발생시키고 게임 종료
  • 메세지에 저장된 값이 지정한 RETRY 값(여기선 1)과 같다면 run()을 호출하여 init(), play(), end() 과정이 반복되도록 함.

구현을 2일차에 마무리하였지만, 계속해서 리팩토링을 진행하였다. 이번 과제를 진행하면서 느낀 점은 공부를 많이 해야 내가 무엇이 부족하고 무엇을 모르는 지 알 수 있다는 점이다. 내가 구현한 코드를 봤을 때 코드가 매끄럽지 않은 부분이 여러 곳 존재하지만 어떻게 수정해야할지 정말 막막했다. 그래서 구현은 금방하였지만 리팩토링에 훨씬 많은 시간을 투자하였고, 부족한 내용을 공부하는데 1주차의 대부분의 시간을 투자하였다. 프리코스를 시작하기 전에도 계속 공부를 하였지만 이렇게 몰입하고 열정적으로 하진 않았다. 과제가 주어지고 스스로의 힘으로 해결하려고 몰입하다보니 예전에는 시계를 보면 1시간 정도의 집중을 했었다면, 프리코스를 진행할 때는 2시간, 3시간이 너무 빨리 지나갔고 하루종일 나의 설계와 객체지향적인 고민들을 하였다. 독학을 계속하면서 열정이 식고 공부 방향에서 헤매이던 나에게 프리코스는 합격 여부와 상관없이 정말 큰 도움이 되는 것 같다.


추가로 다른 분들의 코드를 보니 추상 클래스, 일급 컬렉션 등의 개념을 사용하고 성능을 고려하는 등 아직 내가 고려하지 못했던 많은 부분들을 염두하면서 구현을 해나가신 것을 보고 많이 배우고 있다. 특히 성능에 대해 이해를 하면서 구현을 하고 싶은데 아직 갈 길이 많이 먼 것 같다. 남은 3주차동안은 정말 몰입이란 걸 하면서 코드리뷰를 열심히 하는 다른 지원자분들처럼 성능과 효율을 고려한 코드를 작성해보고자 한다.

반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...