[LEV1] 자동차 경주 게임

오잉·2023년 2월 13일
0

우아한테크코스

목록 보기
3/7
post-thumbnail

진행 기간

2023.02.07 - 2023.02.13 (7일간)

미션 내용

기능 요구사항

  • 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
  • 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
  • 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
  • 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
  • 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
  • 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.

실행 결과

위 요구사항에 따라 3대의 자동차가 5번 움직였을 경우 프로그램을 실행한 결과는 다음과 같다.


중요시 했던 부분 : getter 지양

자동차경주 미션에서 특히 중심을 뒀던 부분은 getter 지양였다.
사실 지금까지 나는 강경 getter 반대파는 아니었는데,
이번 미션을 함께한 페어가 우리 이번에 진짜 getter 하나도 안쓰고 해보자는 의견이어서
처음으로 이악물고 getter 피하기를 해봤다.

우리가 getter를 사용하지 않기 위해 했던 시도는 다음과 같다.

1. getDesc 메소드

(라운드 실행 결과를 출력하기 위해) RacingCar의 현상태를 문자열로 재구성해 반환

// RacingCar class
// 결과적으로 이 메소드는 삭제되었다 (대신 Dto를 사용했다)
	public String getDesc() {
        String delimiter = " : ";
        String description = name +
                delimiter +
                "-".repeat(point);
        return description;
    }

출력에 getter를 쓰지 않기 위해 여러 방법을 찾던 도중에, 위와 같은 방법을 사용한 블로그를 발견했다.
이로써 객체 내부의 상태값을 객체밖으로 노출시키지 않았다고 좋아했었는데..!
이 방법을 사용하면, 도메인인 RacingCar 클래스가 View 로직(출력이 어떻게 되어야 하는지)까지 알아야 한다. 즉, View 요구사항이 바뀌면 View가 아닌 Domain이 수정되어야 한다.
getter 피해보겠다고 더 말도 안되는 상황이 발생하는 것 같아서 이 메소드는 삭제했다.

2. compareTo 메소드 오버라이드

(가장 큰 position을 가진 RacingCar를 구하기 위해) 인자로 전달된 RacingCar의 position과 현 RacingCar의 position의 차이값 반환

// RacingCar class
    @Override
    public int compareTo(RacingCar o) {
        return o.position - this.position;
    }
// RoundManager class
    public List<RacingCarDto> getSortedRacingCars() {
        Collections.sort(racingCars);
        return racingCars.stream().map(RacingCar::toDto).collect(Collectors.toList());
    }

자동차 경주의 우승자를 구하기 위해, 각 자동차의 위치값을 비교해야 했다.
getter를 사용해 position들을 하나하나 꺼내와서 비교하는 방법말고는 없을까 한참 고민하다가, 멋진 방법을 찾았다.
RacingCar가 Comparable인터페이스를 implements하도록 한 후, compareTo 메소드를 오버라이드하는 방법을 사용했다. 이 방법을 사용하면 Collections.sort() 메소드가 우리가 원하는 기준에 맞춰서 정렬해준다.

3. toDto 메소드

(position값 순서대로 정렬된 객체들을 그대로 전달하지 않고 복사본을 전달하기 위해) dto로 복사

// RacingCar class
// 결과적으로 이 메소드는 (도메인 내부에서) 삭제되었다
    public RacingCarDto toDto() {
        return new RacingCarDto(name, position);
    }

처음에는 position값 순서대로 정렬된 객체들을 그대로 전달하지 않고 복사본을 전달하기 위해 Dto를 만들었다. (이후에는 라운드 실행 결과 출력 위해 view로 전달하는 과정에서도 Dto가 사용되었다)
dto 자체를 도입한 건 잘했는데, toDto 메소드의 위치가 잘못되었다.
기존 toDto 메소드는 RacingCar 내부에 있었는데, 이렇게 되면 도메인이 dto에 대해 의존을 가지게 된다. dto는 말그래도 data trasfer object로서 계층 간 데이터 전달용 객체인데 도메인이 이러한 객체에 의존하는 것은 말이 되지 않는다.
결과적으로 dto의 생성자 인자로 도메인을 받게 하도록 수정하고, dto를 생성하는 메소드를 도메인 외부로 빼냈다.


리뷰 정리

step1 리뷰

  1. parsing 전 검증
    null체크 & 공백

  2. 테스트용 클래스의 위치
    테스트에만 사용되는 클래스가 메인 코드 사이에 섞여있다면 다른 개발자와의 협업 과정에서 오해가 생길 수 있다 -> test 패키지 밑에 두기

step2 리뷰

  1. setter & getter
  • setter
    • 절대 사용 금지
    • 도메인은 setter가 아닌 정해진 메소드들을 통해 내부에서 유효하게 값이 변경되어야 한다
    • VO는 생성시 값을 final로 유지하며 값 변경이 필요한 경우 아예 새로 생성해야 한다.
  • getter
    • 도메인에서는 가급적 사용하지 않는다.
    • DTO는 값을 전달하기 위한 객체이기 때문에 getter 가능 -> 뷰나 컨트롤러에서 사용할 수 있다.
  1. 코드 내에서 문제 해결을 하는 것도 좋지만, 문제 상황이 발생했다는 것은
    코드에 뭔가 심각한 실수가 있다는 뜻 => 명확히 알 수 있도록 예외를 던지자

기존코드

public Range(int min, int max) {
        if (max < min) {
            int temp = min;
            min = max;
            max = temp;
        }
        this.min = min;
        this.max = max;
    }

개선 코드

public Range(int min, int max) {
        if (max < min) {
            throw new IllegalArgumentException("Range 객체의 생성자에 잘못된 인자가 전달되었습니다.");
        }
        this.min = min;
        this.max = max;
    }
  1. 실패하는 테스트가 프로젝트에 있어서는 안된다.
    커밋 전 all test 돌리기 필수
    기존 코드 수정 시 바로 테스트에 반영해야 한다 (테스트와 프로덕션 코드 동기화)

배운 점 & 좋았던 점

  • 매직넘버 감싸기 (4가 의미하는 바를 명확히)
  • intellij로 git 사용하는 법 ...
    지금까지 터미널로 git add git commit git push 다 했던 사람으로서 ... 너무 충격적이었다 !!
  • getter를 피하기 위해 다양한 시도를 해봤다
  • 클래스가 3개 이상의 인스턴스 변수를 갖지 않도록 노력했다
  • 반복문 -> 재귀함수 (https://tecoble.techcourse.co.kr/post/2020-04-30-iteration_vs_recursion/)

아쉬웠던 점

  • 리뷰 방식 이해를 잘못해서 1단계 리뷰과정을 제대로 진행하지 못한채 2단계에 돌입했다.
  • 설계에 너무 오랜 시간을 들였던 것 같다
  • 메소드명, 변수명 등 하나하나에 너무 집착해서 속도가 나지 않았다

피드백

  • 앞으로는 너무 많은 고민을 하지 말고, 일단 만들자. 만들고서 하나씩 고치자.

우테코에 와서 진행한 첫 레벨 첫 미션!
첫주라 그런지 적응하느라 매우 정신이 없었다.
git을 잘 사용하지 못하는지라 코드리뷰 방식을 이해하는 게 굉장히 힘들었다. rebase..? fetch..?
결국 리뷰 일정 헷갈려서 1단계 리뷰를 한번밖에 못 받은채로 merge 되어서 너무 슬펐다 흑흑... (미션에 1,2단계가 있고, 코드 리뷰에도 step1, step2, step3 이런 단어들이 있길래 둘이 같은 건줄 알았다...)
첫 페어프로그래밍을 진행했다. 지금까지는 그냥 혼자 생각하고 혼자 구현하는데에 익숙했었는데, 처음으로 누군가와 끊임없이 생각을 나누고 서로를 설득하고 함께 만들어나가는 과정을 겪으니까 매우 새롭고 좋았다!

profile
오잉이라네 오잉이라네 오잉이라네 ~

0개의 댓글