지하철 경로 조회 생각거리

June·2022년 5월 29일
1

우테코

목록 보기
49/84

1. 페어하며 배운 것

지하철 경로 조회 미션 페어 하며 배운 것

2. 새로 필드가 추가될 때

2단계를 진행하며 Line 객체에 extraFare라는 필드를 추가하게 되었다. 기존 생성자를 그대로 두고, extraFare만 받는 생성자를 따로 만들고 extraFare를 생성자에서 받지 않으면 기본적으로 0으로 설정하게 해주었다. 이렇게 했음에도 불구하고 많은 곳에서 테스트가 깨지고 기존 테스트코드 부분에 extraFare 값을 넣어줘야 하기도 했다.

처음 생각난 것은 빌더 패턴을 사용했어야한다는 점이었다. 어노테이션을 붙이면 쉽게 사용할 수 있지만 이번에는 별로 사용하고 싶지가 않았다. 리뷰어 분과 이야기를 나누었을 때, 빌더 패턴을 사용했으면 당장 테스트가 실패하지는 않겠지만 컴파일이 깨지지 않기 때문에 잡을 수 있던 문제도 잡지 못하는 경우도 생길 수 있다고 말씀해주셨다. 가장 값싼 오류인 컴파일 오류를 잘 사용할 수 있다는 말씀이셨다.

추가로 IntelliJ에서 'Ctrl + F6'로 change Signature를 사용했으면 좀 더 편하다는 걸 나중에 로마를 통해 알았다.

3. 외부라이브러리 커스텀

이번 미션을 하면서 외부 라이브러리를 사용할 일이 있었다.
별 생각없이 처음에는 주어진 라이브러리를 사용했었다. 그러다보니 나중에 최단거리 경로들을 조회했을 때 경로를 기준으로 어느 노선인지 조회를 하고, 거리가 얼마인지 다시 계산해야하는 번거로움이 있었다.

기존 코드

WeightedMultigraph<Station, DefaultWeightedEdge> graph = new WeightedMultigraph<>(DefaultWeightedEdge.class);

...
public List<Section> findSectionsByStations(List<Station> stations) {
        List<Section> result = new ArrayList<>();
        for (int i = 0; i < stations.size() - 1; i++) {
            int index = i;
            Section section = values.stream()
                    .filter(it -> it.hasSameUpDownStation(stations.get(index), stations.get(index + 1)))
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("해당 구간을 찾을 수 없습니다"));
            result.add(section);
        }
        return  result;
}

루키와 이야기를 하다보니 훨씬 간단하고 좋은 방법이 있었다. 라이브러리에서 사용하는 객체를 상속해서 내가 원하는대로 커스텀하는 것이었다.

커스텀 코드

public class CustomWeightedEdge extends DefaultWeightedEdge {

    private final Long lineId;
    private final int distance;

    public CustomWeightedEdge(Long lineId, int distance) {
        this.lineId = lineId;
        this.distance = distance;
    }

    public Long getLineId() {
        return lineId;
    }

    @Override
    protected double getWeight() {
        return distance;
    }
}

라이브러리를 그대로 사용하지 않고 필요하게 커스텀해서 사용을하니 훨씬 간편헀다. 나는 각 Edge에서 lineId와 거리가 필요했으므로 기존 DefaultWeightedEdge를 상속해서 필요한 필드를 추가해주었다. 덕분에 나중에 경로를 구한 후 어느 노선들을 지나왔는지 바로 경로에서 구할 수 있었다.

외부 라이브러리를 쓸 때는 주어진대로 생각없이 쓰지말고 더 잘 쓸 수 없을지 고민해보자.

4. 빈 등록할지 말지

아직 빈을 등록하는 기준이 뭔지 아직 답을 못 찾은 것 같다. 내 코드에서는 FePolicyImpl만 빈으로 등록하게 하고 나머지는 직접 Factory에서 전략을 넣어주고 있다. 이 전략들도 다 빈으로 등록하여 의존성 주입을 하게 하는지 좋을지 고민을 했다. 만약 스프링이 의존 주입을 하게하면 편하겠지만 스프링 프레임워크에 지나치게 의존적이지 않은가 생각이 들었다. 또 빈이 기본적으로 싱글톤으로 관리가 되니 최대한 빈을 적게 등록하는게 좋은게 아닌가하는 생각도 있었다.

하지만 지금와서 생각해보니 애초에 프레임워크를 사용한다는 것이 객체의 생성과 의존성 주입을 편하게 하겠다는 이유가 있는데 굳이 순수하게 자바코드만으로도 최대한 돌아가야한다 고집할 필요가 있냐싶다.

4. 주생성자 vs 부생성자 기준

Q: 주생성자와 부생성자에 대한 궁금증입니다. 필드를 최대한 많이 가지고 있는 생성자를 주생성자로 잡기 vs 필드를 최소한으로 가지고 있는 생성자를 주생성자로 잡기에서 고민을 했습니다. 전자의 경우에는 부생성자에서 null을 넣어줘야하고 후자의 경우에는 부생성자에서 모든 필드를 다루지 못하므로 this.value = value와 같은 할당문이 추가로 붙게 됩니다.

A: 완전한 취향의 문제라고 생각은 해요. 뭐 컨벤션이 있더라도 팀에서 그렇게 합의를 했으면 그 방향대로 하면 되지 않을까요? 저는 해당 객체의 모든 필드를 가진 생성자를 주 생성자로 사용하긴 해요. 부득이하게 null을 넣어주어야 하는 경우들이 있을 수 있지만, 명시적으로도 그게 더 낫다고 생각하긴 합니다. 적어도 그 생성자를 타고 들어갔을 때 어떤 값이 null일 수 있겠구나를 알 수 있으니까요.

5. 원시 타입 vs 박싱 타입

스프링 강의를 들으면 박싱 타입을 사용하여서 별 생각 없이 박싱타입을 사용하였다.
이펙티브 자바에서는 박싱 타입의 사용을 지양하라고 했지만 또 몇 예외의 경우가 있는데 그 중 하나가 하이버네이트에서는 박싱타입을 권장하는 것이었다.

박싱 타입을 사용할 경우 null이 가능해지고, DB에서 null이 가능하다면 그것을 나타내기에 자연스럽다고 생각하였다.

이번 미션에서 id의 경우 DB에 들어가기 전에 id가 없다. 그래서 id의 경우에는 박싱 타입을 사용해도 될 것 같다. 굳이 원시 타입을 사용해서 디폴트 값인 0보다는 값이 할당되지 않은 상태를 잘 나타내줄 것 같기 때문이다.

만약 DB에 not null 제약조건이 있다면 원시타입을 써서 혹시 null이라면 NPE가 터지게 유도해야하지 않나라는 생각도 있다.

이 부분도 아직 완전히 정리가 된 것이 아니라 다음 미션을 하면서 더 고민해봐야할 것 같다.

6. 포멧팅

종종 기본적인 포멧팅을 지키지 않고 커밋을 할 때가 있다. 그래서 항상 제출하기 전에는 포멧을 다시 체크해봐야겠다 생각했고 체크리스트를 만들어 봤다.

  1. EOF
  2. 접근제어자
  3. 테스트 안깨지는지
  4. 포멧팅 (패키지에서 Ctrl + o)
  5. 사용하지 않는 라이브러리 제거

7. 남은 궁금증

아직 해결하지 못한 궁금증 중 하나는 Line이 Section을 가지게할지 Section이 Line을 가지게 할지이다. 기본적으로 상호참조를 할 경우는 고려하지 않기로 했다. 상호참조를 할 경우 toString과 같이 서로를 재귀적으로 참조할 수도 있고, 다른 메서드들을 작성할 때도 상호참조를 신경써줘야하는 등 일이 많아지기 때문이다.

처음에는 Line이 Section을 가지는 것이 가장 '자연스럽'다라고 생각했다. Section들이 모여서 Line을 이루기 때문이라고 생각했다. 하지만 생각해보면 Section이 Line을 가지는 것이 크게 도메인적으로 이상하지는 않다.

결국 지금까지 생각해본 것으로는 결국 'Line'을 기준으로 'Section'을 조회할 일이 많을지 Section을 기준으로 Line을 조회할일이 많을지에 따라서 관계를 정의해야하지 않나 싶다.

0개의 댓글