우아한 테크코스 2기 프리코스 - 2주차 미션 자동차 경주 게임 회고

김경준·2019년 12월 25일
0

각 주차의 미션들을 리뷰하면서 어떤 것을 배웠고 부족했던점들은 무엇이 있는지에 대해 정리합니다.
당시 제출 하였던 소스코드는 Github에서 확인하실 수 있습니다.

0. 2주차 미션 개요

week2-1.png
week2-2.png
week2-3.png


1. 미션을 통해 나는 무엇을 배웠는가

  • 리팩토링의 중요성

week2-4.png
week2-5.png

지난 수십여 년 동안 쌓아온 경험에서 얻은 교훈이라면, 프로그래밍은 과학보다 공예에 가깝다는 사실이다. 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미다.

Clean Code - Robert C. Martin

처음부터 좋은 코드를 작성할 수 있다면 좋겠지만 현실적으로 불가능하다.
2주차 미션은 오프라인 코딩테스트를 대비한다는 마음가짐으로 미션이 주어지면 최대한 빨리 구현하기, 그리고 제출 전까지 계속해서 리팩토링하는 방식으로 진행했다.
단순히 '구현이 끝났다' 에서 멈추는것이 아니라 '어떻게 하면 더 좋은 코드로 만들수 있을까' 를 고민하는 것이 훨씬 깊은 생각을 할 수 있도록 이끌어 준다고 느꼈고, 큰 도움이 되었다.

  • 조금 더 나아진 설계

jojoldu님의 순수 Java로 이루어진 프로젝트를 보며, 소스들을 어떤식으로 관리 하여야 하고 설계를 어떤식으로 하는것이 이상적인지에 대해 학습 할 수 있었다.

week2-7.png

1주차 미션의 소스들, 불용어의 사용과 BaseballGameApplication, BaseballGame 등 깔끔하지 못한 구조를 띄고 있다.

week2-6.png

2주차 미션의 소스들, Application의 main에서 RacingGame을 만들고 play하는 방식.

그 결과 시스템 설계에 나름대로의 규칙을 가질수 있게 되었다.
Application의 main에서 실행할 프로그램의 인스턴스를 생성한후 play하는 방식이다.


2. 무엇이 부족 하였는가

  • 필수적이지 않은 인스턴스 변수의 선언

public class Car {
	private final String name;
	private int position = 0;
	private boolean isWinner = false;

	public Car(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public int getPosition() {
		return position;
	}

	public boolean getIsWinner() {
		return isWinner;
	}

	void setIsWinner() {
		isWinner = true;
	}

	public void tryToGo() {
		if(Rule.isGo()) {
			position++;
		}
	}
}
public class RacingGame {
	private ArrayList<Car> cars = new ArrayList<>();
	private int tryNumber = 0;
	
	private void getTryNumber() {
		tryNumber = InputView.tryNumber();
		if (!InputValidator.isValidNumber(tryNumber)) {
            throw new InputMismatchException("시도 횟수는 1 이상 이어야 합니다.");
		}
	}

필드(인스턴스 변수)의 수를 줄이기 위해 노력한다

필드(인스턴스 변수)의 수가 많은 것은 객체의 복잡도를 높이고, 버그 발생 가능성을 높일 수 있다.
필드(인스턴스 변수)에 중복이 있거나, 불필요한 필드가 없는지 확인해 필드의 수를 최소화한다.

- 2주차 공통 피드백

isWinner라는 인스턴스의 변수의 추가로 인해 getter와 setter 또한 생겨난 상황이다.
isWinner라는 속성을 부여할 것이 아니라 maxPosition을 구한후 인스턴스의 position이 maxPosition인가? 를 검사하면 승리한 자동차가 누구인지 쉽게 알 수 있었다.

tryNumber 역시 불필요한 인스턴스 변수이다.

  • 시스템 생성과 시스템 사용의 분리

소프트웨어 시스템은 (애플리케이션 객체를 제작하고 의존성을 서로 '연결'하는) 준비 과정과 (준비 과정 이후에 이어지는) 런타임 로직을 분리해야 한다.

Clean Code - Robert C. Martin

public class Application {
	public static void main(String[] args) {
		RacingGame game = new RacingGame();
		game.play();
	}
}
public class RacingGame {
	private ArrayList<Car> cars = new ArrayList<>();
	private int tryNumber = 0;

	void play() {
		buildCars();
		getTryNumber();
		OutputView.printResultMessage();
		printResult();
		Rule.setIsWinners(cars);
		OutputView.printWinners(cars);
	}

	private void buildCars() {
		ArrayList<String> carNames = getCarNames();
		for (String carName : carNames) {
			Car car = new Car(carName);
			cars.add(car);
		}
	}

Application에서 RacingGame을 생성한후 바로 play한다. 그리고 play메소드에선 차를 만든다.
그렇다면 과연, 사용자에게 이름을 입력 받아 차를 만드는것이 게임 플레이 과정에 포함된다고 볼 수 있을까?

public class Application {
	public static void main(String[] args) {
		RacingGame game = new RacingGame(buildCars());
		game.play();
	}
	
	private static Cars buildCars() {
		List<Car> cars = new ArrayList<>();
		List<String> carNames = getCarNames();
		for (String carName : carNames) {
			Car car = new Car(carName);
			cars.add(car);
		}
		return new Cars(cars);
	}
}
public class RacingGame {
  	private final Cars cars;
  
  	public RacingGame(Cars cars) {
    	this.cars = cars;
	  }
  	
  	public void play() {}
}

Application에서 입력을 받아 RacingGame의 생성자로 넘겨주는 것이 훨씬 더 깔끔한 설계라고 생각된다.
또한, 일급 컬렉션을 사용하기 적절한 상황이었다.

profile
우아한형제들 서버 개발자

0개의 댓글

관련 채용 정보