Post

2주차 정리

2주차 정리

피드백 정리

1. getter를 지양하자

과제를 구현하다보니 화면 출력 기능에는 도저히 getter가 없으면 안 될 것 같았다. 해당 도메인 객체에 출력하는 책임을 부여한다면 getter는 필요없겠지만, 과연 그것이 객체지향적일까? getter를 만드는 것과 출력의 책임을 부여하는 것. 그 사이에서 고민이 많았다. 출력의 책임은 출력 객체에서 오롯이 관리하고 출력을 위한 getter 정도는 괜찮겠다 생각해서 만들어줬다.

그러다보니 다음과 같이 출력이 아닌 코드에도 생각없이 getter를 사용하게 되었다.

1
2
3
 public List<Car> getWinnerList() {
        return  cars.stream()
                .filter(car -> car.getPosition() == maxPosition());

getter가 있으니 쓰면 되겠다 라고 생각해서 쓴건데 사실 이런 단순 비교에는 samePosition() 같은 메서드로 getter없이 요청할 수 있었다.

1
2
3
 public List<Car> getWinnerList() {
        return  cars.stream()
                .filter(car -> car.samePosition(maxPosition));

getter가 있어도 내가 안 쓰면 되지~ 라고 생각했는데 내가 쓰고 있었다. 만약 내가 아닌 다른 개발자가 해당 도메인 객체를 사용한다면 getter를 사용하지 말라는 주석가지고는 그의 코딩을 막을 수 없을 것이다.

결론은 그냥 웬만하면 getter를 만들어놓지도 말자 이다. getter를 미리 만들어뒀기 때문에 간단하게 samePosition()을 생각할 수도 있는데도 무작정 getter를 꺼내서 절차지향적으로 개발했다.

2. 테스트 의도를 쉽고 빠르게 이해할 수 있도록 이름을 잘 지어주자

다음과 같은 테스트 코드가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
   @Test
    void getWinnerList_one_winner() {    
        // given
        Cars cars = getCars(carA, carB);
        carA.play(alwaysMoveStrategy);

        // when
        List<Car> winnerList = cars.getWinnerList();

        // then
        assertThat(winnerList).containsExactly(carA);
    }

getWinnerList.. one winner?? 우승자 리스트를 가져와서 하나의 우승자..? 무슨 말이지?

물론 내가 작성한 코드기 때문에 나는 안다. 그런데 만약 수많은 테스트를 다른 개발자가 한 꺼번에 돌렸을 때, 이 테스트가 실패한다면 메서드의 의도를 파악하기 어렵다. 코드를 작성한 사람에게 물어보거나, 코드를 분석해야한다.

image

영어를 잘 못하면 그냥 한글로 메서드를 자세히 적든가, @DisplayName으로 자세히 알려주자. 개발은 나만 하는 것이 아니다. 심지어 나도 나중에 가면 무슨 테스트였는 지 까먹는다.

image

image

이제 테스트가 실패하는 상황에 대해 한 번에 알아볼 수 있게 되었다.

3. 일급컬렉션과 Iterable

일급컬렉션

일급컬렉션이란 자바 컬렉션 하나 만을 멤버변수로 가지고 있는 객체를 말한다. 예를 들면 다음과 같은 객체이다.

1
2
3
4
5
6
7
8
9
10
11
public class Cars {
    private final List<Car> cars;

    private Cars(List<Car> cars) {
        this.cars = cars;
    }

    public static Cars from(List<Car> cars) {
        return new Cars(new ArrayList<>(cars));
    }
}

일급컬렉션을 사용하면 다음과 같은 장점이 있다.

  1. 컬렉션의 불변성을 보장하기 쉽게 해준다.

위의 Cars 를 보면 List<Car> cars는 final로 선언되어 객체가 생성될 때만 들어가기 때문에 불변을 보장한다.

  1. 컬렉션에 비즈니스 로직을 추가할 수 있다.

보통 List나 MapSet 등의 컬렉션을 사용하다보면 해당 컬렉션의 메서드가 아닌 데이터 검증, 비즈니스에 종속적인 로직 등이 필요한 경우가 있다. 이 때, 일급컬렉션을 사용하지 않으면 코드가 상당히 절차지향적으로 작성된다.

1
2
3
4
5
6
7
8
  List<Car> cars = new ArrayList<>();
  Car car = new Car("exception");

  if (car.sameName("exception")) {
    throw new IllegalStatementException();
  }

  cars.add(car);

하지만 일급컬렉션을 사용하면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  // 클라이언트 코드
  Cars cars = Cars.from(new ArrayList<>());  
  cars.addCar(new Car("exception"));

  // 일급컬렉션 Cars
  public class Cars {
      private final List<Car> cars;
      ...

      public void addCar(Car car) {
          if (car.sameName("exception")) {
                throw new IllegalStatementException();
          }
          cars.add(car);
      }
}

이렇게 Cars에 들어가는 Car를 Cars에서 검증하게 되면서 책임이 나눠지며 클라이언트 코드는 줄고, 비즈니스에 종속적인 자료구조가 탄생한다.

Iterable

클래스가 Iterable을 구현하게 되면 객체를 순회할 수 있게된다.

image

Cars의 경우 Car의 List를 감싼 일급컬렉션이기 때문에 구현할 때 Iterable<Car>와 같이 작성하면 된다. 그리고 순회를 해야하므로 iterator()를 오버라이딩 해주면 사용 준비완료이다.

그런데 이 방법에는 문제가 있다. 일급컬렉션 역시 도메인 객체인데, Cars의 내부에 있는 Car를 순회하며 접근하는 것이 과연 객체지향적일까에 대한 문제이다.

리뷰어님은 도메인 객체라면 메시지를 보내는 방식을 선호하신다고 하셨다. 즉, iterable을 구현하여 순회하는 것은 메시지를 보내는 게 아니라, 내부 변수를 꺼내보는 것이라고 하셨다.

하지만 내 생각은 조금 달랐다. 일급컬렉션도 어쨌든 컬렉션인데, 내부 값들을 보지 못하면 컬렉션이 무슨 소용인가 싶었다. 컬렉션을 감쌌기 때문에 일급 컬렉션인건데, 불변성과 비즈니스 로직등을 추가해서 비즈니스 종속적인 컬렉션이면 일급컬렉션의 장점을 살려 구현한 것 아닐까? 굳이 순회를 막으며 내부 요소들에 대한 접근까지 못하게 하는 것은 비효율적이라고 생각한다.

또, Iterable을 구현하면 테스트할 때 다양한 메서드를 사용할 수 있다.

image

간단하게 예를 들면, 위와 같이 컬렉션 메서드를 사용할 수 있다.
만약 Cars가 Iterable을 구현하고 있지 않다면, hasSize(), containsExactly()가 아니라 테스트 때문에 Cars에 size()와 contains() 를 구현해줘야할 것이다.

모짝미, 네트워킹 데이

어제 NEXTSTEP 과정에서 오프라인 모짝미를 진행했다. 개발자라면 다들 모각코(모여서 각자 코딩)는 알 것이다. 모짝미는 모여서 짝지어서 미션의 줄임말이다. 각자가 아니라 짝 프로그래밍(Pair Programming)을 하는 것이다. 나는 짝 프로그래밍을 해본 적이 없기 때문에 약간 긴장 반 설렘 반으로 선릉역으로 향했다. 우아한 테크코스도 그 건물에서 진행되는 지, 장소가 우테코로 도배되어 있었다. 부러웠다…

포비 박재성님과 제이슨 박재성님을 실제로 뵙게 되어 신기했다. 네트워킹데이라고 한 시간 정도 참가자들이 미리 작성한 질문에 대해 대답해주시는 시간이 있었는데, 감명깊게 들었다. 다들 비슷한 고민이고 나만 힘든 게 아니구나 싶었다. 포비님이 비전공자분이 CS 관련해서 질문을 주셨는데, 전공자라고 CS 잘하는 거 아니라고 하셨다. 내 얘기같아서 웃음이 났다.

그리고 나와 미션 진행 단계가 같은 분과 짝 프로그래밍을 진행했다. 짝 프로그래밍에는 Driver(운전수)와 Navigator가 있다. 드라이버는 키보드를 놓고 직접 코딩하는 사람을 말하고, 네비게이터는 옆에서 훈수(?)두는 사람을 말한다. 10분 간격으로 이 역할을 번갈아가며 했다. 짝으로 같이 하신 분이 Enum타입에 BiFunction을 사용해서 Enum에 비즈니스 로직을 넣으셨는데 무척 인상깊었다. 그 코드로, if 문으로 떡칠 되어있던 클라이언트 코드를 단 한 줄로 수정할 수 있었다.

후기

좋은 경험이었다. 다른 개발자와 개발적인 대화를 나눌 수 있는 것이 좋았다. 혼자 개발공부 하면서, 내가 잘 하고 있는 건지 내 위치를 어느정도 되는 지 가늠하기가 어렵다. NEXTSTEP 측에서 좋은 자리를 마련해줘서 좋았고, 2주뒤에 있을 모짝미에도 참여하려고 한다. 정말 커리큘럼 비용이 1도 아깝지 않다.

This post is licensed under CC BY 4.0 by the author.