본문 바로가기

강의/TDD, Clean Code with Java 12기

4단계 자동차경주 (우승자) (8일차)

6일차, 7일차에는 3단계 프로젝트를 진행하였다.

그동안 문제를 해결하는데만 급급하였다.

코드를 어떻게 짜야하는지 고민해본 적이 없기 때문에, 3단계를 진행하는데 많은 어려움이 있었다.

 

좋은 코드를 작성하기 위해서는 어떻게 진행하여야 하는 가? 에 대한 질문에 답을 찾기 위해 노력하였다.

남의 코드를 많이 보면 될 것인가?

아니다. 나는 아직 기초가 없기 때문에 어떤 코드가 좋은 것인지 판별하기가 어려울 것이다.

 

그러던 와중에 [오브젝트 : 코드로 이해하는 객체지향 설계] 라는 책을 알게 되었다. 

책에서 와 닿는 부분이 있어서 적어본다.

 

모든 소프트웨어 모듈에는 세 가지 목적이 있다고 한다.

첫 번째 목적은 실행 중에 제대로 동작하는 것이다.

    - 이것은 모든 모듈의 존재 이유라고 할 수 있다.

두 번째 목적은 변경을 위해 존재하는 것이다.

    - 대부분의 모듈은 생명주기 동안 변경되기 때문에 간단한 작업만으로도 변경이 가능해야 한다.

    - 변경하기 어려운 모듈은 제대로 동작하더라도 개선해야 한다.

세 번째 목적은 코드를 읽는 사람과 의사소통 하는 것이다.

    - 모듈은 특별한 훈련 없이도 개발자가 쉽게 읽고 이해할 수 있어야 한다.

    - 읽는 사람과 의사소통 할 수 없는 모듈은 개선해야 한다.

 

나는 첫번째 목적만 달성하려고만 했던 것 같다.

그래서 이번 기회에 두 번째, 세 번째 목적을 달성하는 방법을 좀 더 생각해 보려고 한다.

 

위와 같이 구조를 나눴다.

처음에는 세가지를 나눠서 만들어 보려고 노력했다.

1. Car

    - 자동차의 현재 위치를 저장해 놓을 목적

2. Input view

    - input을 받아들이는 목적

3. Result view

    - 결과를 출력해주는 목적

 

그 다음에는 Car의 위치를 옮길 CarManager class를 만들었다.

CarManager는 RaceManager에게 한 칸 앞으로 가도 되겠냐고 물어보는 CanIGo 메소드를 들고 있으며

RaceManager는 주사위를 던져서 4 이상이면 CarManager에게 가도 된다고 끄덕여 주는 역할을 하고 있다.

 

Race 클래스는 RacingGame에서 심장부 역할을 담당하고 있다.

입력을 받고, 입력값이 올바른지 확인하고, 입력값 만큼 CarManager를 생성하고, 결과를 출력해주는 역할이다.

많은 역할이 몰린 것 같아 조금 걱정이 되긴 하지만, 지금까지는 만족 스럽다.

 

아래는 작성한 코드이다.

package step4;

public class Car {
    private int position;

    public Car(int position) {
        this.position = position;
    }

    public void MoveForward() {
        this.position++;
    }

    public int getPosition() {
        return position;
    }
}
package step4;

public class CarManager {
    private Car car;
    private RaceManager raceManager = new RaceManager();

    public CarManager(Car car) {
        this.car = car;
    }

    public void CanIGo() {
        if (raceManager.judge()) {
            car.MoveForward();
        }
    }

    public int getCurrCarPosition() {
        return car.getPosition();
    }
}
package step4;

import java.util.Scanner;

public class InputView {
    private static final String HOW_MANY_CAR = "자동차의 대수는 몇 대 인가요?";
    private static final String HOW_MANY_CYCLE = "시도할 횟수는 몇 회 인가요?";
    private static final int MIN_ROUND = 0;
    private static final int MIN_CAR = 0;
    private static final String CYCLE_ERROR_MESSAGE = "시도할 횟수는 0보다 커야 합니다.";
    private static final String CAR_ERROR_MESSAGE = "자동차 대수는 1대 이상이어야 합니다.";
    private Scanner sc = new Scanner(System.in);

    public int getNumOfCar() {
        System.out.println(HOW_MANY_CAR);
        return getInput();
    }

    public int getNumOfCycle() {
        System.out.println(HOW_MANY_CYCLE);
        return getInput();
    }

    private int getInput() {
        return sc.nextInt();
    }

    public void inputCarCheck(int numOfCar) {
        if (numOfCar <= MIN_CAR) {
            throw new IllegalArgumentException(CAR_ERROR_MESSAGE);
        }
    }

    public void inputRoundCheck(int numOfCycle) {
        if (numOfCycle <= MIN_ROUND) {
            throw new IllegalArgumentException(CYCLE_ERROR_MESSAGE);
        }
    }
}
package step4;

import java.util.Random;

public class Race {
    private int numOfCar;
    private int numOfCycle;
    private InputView inputView = new InputView();
    private ResultView resultView = new ResultView();
    private CarManager[] carManagers;

    public int[] requestInput() {
        numOfCar = inputView.getNumOfCar();
        numOfCycle = inputView.getNumOfCycle();
        return new int[]{numOfCar, numOfCycle};
    }

    public void prepareCars() {
        carManagers = new CarManager[numOfCar];
        for (int i = 0; i < numOfCar; i++) {
            Car car = new Car(1);
            carManagers[i] = new CarManager(car);
        }
    }

    public void validInputConfirm() {
        inputView.inputCarCheck(numOfCar);
        inputView.inputRoundCheck(numOfCycle);
    }

    public void playRace() {
        for (int i = 0; i < numOfCar; i++) {
            carManagers[i].CanIGo();
        }
    }

    public void printResult() {
        for (int i = 0; i < numOfCar; i++) {
            resultView.showCurrCarPosition(carManagers[i].getCurrCarPosition());
        }

    }
}
package step4;

import java.util.Random;
import java.util.Scanner;

public class RaceManager {
    private static final int DICE_THRETHOLD = 4;
    private static Random rd = new Random();

    public boolean judge() {
        int rdNumber = rd.nextInt(10);
        if (rdNumber >= 4) {
            return true;
        }
        return false;
    }
}
package step4;

import step3.model.Car;

public class ResultView {
    public void showCurrCarPosition(int location) {
        for (int i = 0; i < location; i++) {
            System.out.print("-");
        }
        System.out.println();

    }
}
import step4.Race;

import java.util.Scanner;

public class RacingCarMain {
    public static void main(String[] args) {
        Race race = new Race();
        int[] raceInfo = race.requestInput();
        int numOfCar = raceInfo[0];
        int numOfCycle = raceInfo[1];

        race.validInputConfirm();
        race.prepareCars();

        for (int i = 0; i < numOfCycle; i++) {
            race.printResult();
            race.playRace();
            System.out.println();
        }
        race.printResult();
    }
}