Skip to content

Conversation

gaegulgaegul
Copy link

안녕하세요. 한솔님!
도전 미션 구현해봤습니다.
인수테스트를 작성하고 TDD를 통해 기능을 구현하려고 했지만
조건이 너무 까다롭다고 생각되어 프로덕트 코드를 작성하고 테스트를 작성하는 방향으로 구현해봤습니다.
제약 조건은 만족하지만 ArrivalTime#getTimeOfFastestPath 내부에서 객체의 값에 변경이 너무 많다고 생각하는데
이 부분은 어떻게 생각하시는지 궁금합니다.
리뷰 요청드리겠습니다.

Copy link

@catsbi catsbi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 명섭님. 추가미션은 저도 너무 오랜만에 보는지라 반갑기도하고 가물가물하네요 ㅎㅎ
꽤나 어려운미션인만큼 구현했다는 자체만으로도 감탄인데, 테스트 케이스가 제가 원하는 케이스들이 다 있어서 인상깊었습니다 👍

ArrivalTime이라는 객체에 대해 고민하고 계신 것 같은데, 제가 봐도 고민을 참 많이 하신 것 같습니다. depth에 대해 고민이라면 우선 쪼개보시죠. 그리고 하나하나에 집중해서 리팩토링을 하다보면 좀 더 윤곽이 보이지 않을까요?

@Transactional
public LineResponse saveLine(LineRequest request) {
Line line = lineRepository.save(new Line(request.getName(), request.getColor(), request.getAdditionalFare()));
Line line = lineRepository.save(request.toLineEntity());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EntitySupplier 인터페이스를 LineRequest가 구현하도록한다면, 다음과 같이 작성해서 Request객체가 toLineEntity를 제공하는지에 대한 신뢰성이 높아질 수 있고 유연성도높아질 수 있죠.

public LineResponse saveLine(EntitySupplier<Line> supplier) {
   Line line = lineRepository.save(supplier.supply());

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supplier를 사용하는 방식은 처음 알았습니다. 유용한 정보 감사합니다.

public void updateLine(Long id, LineRequest lineRequest) {
Line line = findById(id);
line.update(lineRequest.getName(), lineRequest.getColor(), line.getAdditionalFare());
line.update(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위처럼 여기도 EntitySupplier가 가능하다면 이렇게 가능하겠죠.

line.update(supplier.supply());

AgeDiscountPolicy ageDiscountPolicy = AgeDiscountPolicy.of(getCurrentUserAge(user.getUsername()));
SubwayMap subwayMap = new SubwayMap(lines);
Path path = subwayMap.findPath(upStation, downStation, type, ageDiscountPolicy);
DiscountPolicy discountPolicy = DiscountPolicy.of(getCurrentUserAge(user.getUsername()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

할인정책이 하나 이상일 경우도 생길 수 있을 때 어떤식으로 리팩토링을 고려할 수 있을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

체인 형식의 디자인 패턴으로 구현하면 개선할 수 있을 것 같습니다.

Comment on lines 11 to 12
private List<Section> sections;
private List<Line> lines;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

둘 다 일급 컬렉션을 활용해 볼 수 있을 것 같네요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!


@Override
protected GraphPath<Station, SectionEdge> getGraphPath(Station source, Station target) {
KShortestPaths kShortestPaths = new KShortestPaths(getGraph(), 100);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KShortestPaths를 잘 사용하셨네요 매직넘버도 분리해주면 좋을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

SimpleDirectedWeightedGraph<Station, SectionEdge> graph = new SimpleDirectedWeightedGraph<>(SectionEdge.class);

// 지하철 역(정점)을 등록
lines.stream()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정점과 간선 등록등의 중복로직들이 있다면, 일종의 템플릿이라 할 수 있고 템플릿 메서드 패턴을 사용할 수도 있을 것 같네요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

Comment on lines +91 to +122
@DisplayName("막차 시간(23:00) 기준으로 환승이 없는 경로 도착시간 구하기")
@Test
void noneTransferAtEndTime() {
// given
List<Line> lines = List.of(노선A, 노선B);
List<Section> sections = List.of(구간BC, 구간CD);
String time = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 0)).format(DATE_TIME_PATH);
ArrivalTime arrivalTime = new ArrivalTime(sections, lines, time);

// when
LocalDateTime value = arrivalTime.value();

// then
assertThat(value).isEqualTo(LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.of(5, 10)));
}

@DisplayName("막차 시간(23:00) 기준으로 환승이 있는 경로 도착시간 구하기")
@Test
void withTransferAtEndTime() {
// given
List<Line> lines = List.of(노선A, 노선B);
List<Section> sections = List.of(구간AB, 구간BE);
String time = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 0)).format(DATE_TIME_PATH);
ArrivalTime arrivalTime = new ArrivalTime(sections, lines, time);

// when
LocalDateTime value = arrivalTime.value();

// then
assertThat(value).isEqualTo(LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.of(5, 24)));
}
} No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 두 케이스에 대한 테스트가 있는지가 중요했는데 잘 작성해주셨네요 👍

Comment on lines 31 to 43
for (Section section : sections) {
Line line = findLine(section);
if (!line.equals(currentLine)) {
currentLine = line;
currentDateTime = calculateTimeInBeforeStations(currentDateTime, currentLine, section);

if (goingLastTime(currentDateTime, currentLine.getEndTime())) {
return getTimeOfFirstPath();
}
}

currentDateTime = currentDateTime.plusMinutes(section.getDuration());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depth를 낮추기 위해 좀 더 고민해볼 필요가 있습니다. 나중에 차차 실무에서 어쩔수 없이 depth가 높아지는 상황은 올 수 있지만, 학습할때는 무리해서라도 depth를 지킬 필요가 있죠.

보통 개념적으로 다른 행위가 수행되거나 의미있는 로직이 중첩될 경우 depth가 길어집니다.

}

@Override
protected GraphPath<Station, SectionEdge> getGraphPath(Station source, Station target) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다익스트라다 알고리즘이 내가 원하는 결과를 간략하게 잘 뽑아내지 못한다면 직접 DFS나 BFS등을 이용해서 최단경로를 구하는 방법도 있습니다.

return fastestDepartureTime.plusMinutes(beforeDuration);
}

private Line findLine(Section section) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일급 컬렉션이 구현되어있다면 lines.indexOf(section)도 가능하겠죠

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일급 컬렉션으로 구현하니 코드가 간결해지네요 ㅎㅎ

@gaegulgaegul
Copy link
Author

안녕하세요. 한솔님!
제가 개인적으로 일이 생겨서 도전 과제 피드백 주신 부분을 이제서야 적용해보게 되었습니다.
요금정책에 관련해서는 책임 연쇄 패턴을 사용하여 구현할 수 있었습니다. 하지만 ArrivalTime#getTimeOfFastestPath 메서드의 내부 로직을 리팩터링하는 과정에서 전역 변수로 변경되는 값을 저장할 수 있도록 하고 메서드 분리만 적용하게 되어 코드의 가독성 측면에서 좋아졌다고 실감되진 않는 느낌이 들었습니다.
그 동안 미션을 수행하면서 다양한 피드백을 주셔서 감사합니다. 리뷰어로 활동하시느라 고생하셨습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants