-
Notifications
You must be signed in to change notification settings - Fork 203
도전 - 가장 빠른 도착 경로 조회 #230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: gaegulgaegul
Are you sure you want to change the base?
도전 - 가장 빠른 도착 경로 조회 #230
Conversation
There was a problem hiding this 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()); |
There was a problem hiding this comment.
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());
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
할인정책이 하나 이상일 경우도 생길 수 있을 때 어떤식으로 리팩토링을 고려할 수 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
체인 형식의 디자인 패턴으로 구현하면 개선할 수 있을 것 같습니다.
private List<Section> sections; | ||
private List<Line> lines; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
둘 다 일급 컬렉션을 활용해 볼 수 있을 것 같네요
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
KShortestPaths를 잘 사용하셨네요 매직넘버도 분리해주면 좋을 것 같습니다.
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정점과 간선 등록등의 중복로직들이 있다면, 일종의 템플릿이라 할 수 있고 템플릿 메서드 패턴을 사용할 수도 있을 것 같네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵!
@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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 두 케이스에 대한 테스트가 있는지가 중요했는데 잘 작성해주셨네요 👍
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()); | ||
} |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일급 컬렉션이 구현되어있다면 lines.indexOf(section)도 가능하겠죠
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일급 컬렉션으로 구현하니 코드가 간결해지네요 ㅎㅎ
안녕하세요. 한솔님! |
안녕하세요. 한솔님!
도전 미션 구현해봤습니다.
인수테스트를 작성하고 TDD를 통해 기능을 구현하려고 했지만
조건이 너무 까다롭다고 생각되어 프로덕트 코드를 작성하고 테스트를 작성하는 방향으로 구현해봤습니다.
제약 조건은 만족하지만 ArrivalTime#getTimeOfFastestPath 내부에서 객체의 값에 변경이 너무 많다고 생각하는데
이 부분은 어떻게 생각하시는지 궁금합니다.
리뷰 요청드리겠습니다.