diff --git a/README.md b/README.md index d6299154c..d50ab8feb 100644 --- a/README.md +++ b/README.md @@ -1,253 +1,33 @@ # 지하철 노선도 경로 조회 미션 - 등록된 지하철 노선도에서 경로를 조회하는 기능을 구현한다. -
- -## 🚀 기능 요구사항 - -> 프리코스 3주차 미션에서 사용한 코드를 참고해도 무관하다. - -### 초기 설정 -- 프로그램 시작 시 역, 노선, 구간 정보를 초기 설정 해야 한다. -- 거리와 소요 시간은 양의 정수이며 단위는 km와 분을 의미한다. -- 아래의 사전 등록 정보로 반드시 초기 설정을 한다. - -``` - 1. 지하철역으로 교대역, 강남역, 역삼역, 남부터미널역, 양재역, 양재시민의숲역, 매봉역이 등록되어 있다. - 2. 지하철 노선으로 2호선, 3호선, 신분당선이 등록되어 있다. - 3. 노선에 역이 아래와 같이 등록되어 있다.(왼쪽 끝이 상행 종점) - - 2호선: 교대역 - ( 2km / 3분 ) - 강남역 - ( 2km / 3분 ) - 역삼역 - - 3호선: 교대역 - ( 3km / 2분 ) - 남부터미널역 - ( 6km / 5분 ) - 양재역 - ( 1km / 1분 ) - 매봉역 - - 신분당선: 강남역 - ( 2km / 8분 ) - 양재역 - ( 10km / 3분 ) - 양재시민의숲역 - ``` - -### 경로 조회 기능 -- 출발역과 도착역을 입력받아 경로를 조회한다. -- 경로 조회 시 총 거리, 총 소요 시간도 함께 출력한다. -- 경로 조회 기준은 `최단 거리` `최소 시간`이 있다. - -### 예외 처리 -- 경로 조회 시 출발역과 도착역이 같으면 에러를 출력한다. -- 경로 조회 시 출발역과 도착역이 연결되어 있지 않으면 에러를 출력한다. -- 그 외 정상적으로 프로그램이 수행되지 않은 경우 에러를 출력한다. - -
- -## ✍🏻 입출력 요구사항 -- `프로그래밍 실행 결과 예시`와 동일하게 입출력을 구현한다. -- 기대하는 출력 결과는 `[INFO]`를 붙여서 출력한다. 출력값의 형식은 예시와 동일하게 한다. -- 에러 발생 시 `[ERROR]`를 붙여서 출력한다. 에러의 문구는 자유롭게 작성한다. - -### 💻 프로그래밍 실행 결과 예시 -#### 경로 조회 -``` -## 메인 화면 -1. 경로 조회 -Q. 종료 - -## 원하는 기능을 선택하세요. -1 - -## 경로 기준 -1. 최단 거리 -2. 최소 시간 -B. 돌아가기 - -## 원하는 기능을 선택하세요. -1 - -## 출발역을 입력하세요. -교대역 - -## 도착역을 입력하세요. -양재역 - -## 조회 결과 -[INFO] --- -[INFO] 총 거리: 6km -[INFO] 총 소요 시간: 14분 -[INFO] --- -[INFO] 교대역 -[INFO] 강남역 -[INFO] 양재역 - -## 메인 화면 -1. 경로 조회 -Q. 종료 - -... -``` - -#### 에러 출력 예시 - -``` -## 메인 화면 -1. 경로 조회 -Q. 종료 - -## 원하는 기능을 선택하세요. -1 - -## 경로 기준 -1. 최단 거리 -2. 최소 시간 -B. 돌아가기 - -## 원하는 기능을 선택하세요. -1 - -## 출발역을 입력하세요. -강남역 - -## 도착역을 입력하세요. -강남역 - -[ERROR] 출발역과 도착역이 동일합니다. - -## 경로 기준 -1. 최단 거리 -2. 최소 시간 -B. 돌아가기 - -## 원하는 기능을 선택하세요. - -... - -``` - -
- -## 🎱 프로그래밍 요구사항 -- 자바 코드 컨벤션을 지키면서 프로그래밍한다. - - 기본적으로 [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html)을 원칙으로 한다. - - 단, 들여쓰기는 '2 spaces'가 아닌 '4 spaces'로 한다. -- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. - - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다. -- 3항 연산자를 쓰지 않는다. -- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. - - 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다. -- else 예약어를 쓰지 않는다. - - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. - - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. -- 프로그래밍 요구사항에서 별도로 변경 불가 안내가 없는 경우 파일 수정과 패키지 이동을 자유롭게 할 수 있다. -- 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 `[ERROR]` 로 시작해야 한다. - -### 프로그래밍 요구사항 - Application -- Application 클래스를 활용해 구현해야 한다. -- Application의 패키지 구조는 변경하지 않는다. -- Application 클래스에 있는 Scanner를 사용하고 별도의 Scanner 객체를 만들지 않는다. -```java -public class Application { - public static void main(String[] args) { - final Scanner scanner = new Scanner(System.in); - ... - } -} -``` - -### 프로그래밍 요구사항 - Station, Line -- Station, Line 클래스를 활용하여 지하철역과 노선을 구현해야 한다. -- 제공하는 각 클래스의 기본 생성자를 추가할 수 없다. -- 필드(인스턴스 변수)인 name의 접근 제어자 private을 변경할 수 없다. -- 가능하면 setter 메소드(ex. setXXX)를 추가하지 않고 구현한다. - -```java -public class Station { - private String name; - - public Station(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - // 추가 기능 구현 -} - -``` - -### 프로그래밍 요구사항 - StationRepository, LineRepository -- Station과 Line의 상태를 저장할 수 있는 StationRepository, LineRepository를 제공한다. -- 필요 시 StationRepository, LineRepository 이 외 추가로 Repository를 만들 수 있다. -- 추가로 생성되는 객체에 대해서 XXXRepository 네이밍으로 저장 클래스를 추가한다. -- 객체들의 상태를 관리하기 위해서 XXXRepository 클래스를 활용해 저장 로직을 구현해야 한다. -- 작성된 메서드는 수정할 수 없고, 필요에 따라 메서드를 자유롭게 추가할 수 있다. - -```java -public class StationRepository { - private static final List stations = new ArrayList<>(); - - public static List stations() { - return Collections.unmodifiableList(stations); - } - - public static void addStation(Station station) { - stations.add(station); - } - - public static boolean deleteStation(String name) { - return stations.removeIf(station -> Objects.equals(station.getName(), name)); - } - - public static void deleteAll() { - stations.clear(); - } -} -``` - -
- -## ❗️힌트 -### 최단 경로 라이브러리 -- jgrapht 라이브러리를 활용하면 간편하게 최단거리를 조회할 수 있음 -- Dijkstra 알고리즘을 반드시 이해할 필요는 없고 미션에 적용할 정도로만 이해하면 됨 -- JGraphtTest 클래스의 테스트를 활용하여 미션에 필요한 라이브러리의 기능을 학습할 수 있음 -- 정점(vertex)과 간선(edge), 그리고 가중치 개념을 이용 - - 정점: 지하철역 - - 간선: 지하철역 연결정보 - - 가중치: 거리 or 소요 시간 -- 최단 거리 기준 조회 시 가중치를 거리로 설정 - -```java -@Test -public void getDijkstraShortestPath() { - WeightedMultigraph graph - = new WeightedMultigraph(DefaultWeightedEdge.class); - graph.addVertex("v1"); - graph.addVertex("v2"); - graph.addVertex("v3"); - graph.setEdgeWeight(graph.addEdge("v1", "v2"), 2); - graph.setEdgeWeight(graph.addEdge("v2", "v3"), 2); - graph.setEdgeWeight(graph.addEdge("v1", "v3"), 100); - - DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(graph); - List shortestPath = dijkstraShortestPath.getPath("v3", "v1").getVertexList(); - - assertThat(shortestPath.size()).isEqualTo(3); -} -``` - -#### 테스트 설명 - - - -- 역 사이의 거리를 고려하지 않는 경우 V1->V3 경로가 최단 경로 -- 역 사이의 거리를 고려할 경우 V1->V3 경로의 거리는 100km, V1->V2->V3 경로의 거리는 4km이므로 최단 경로는 V1->V2->V3 - -
- -## 📈 진행 요구사항 -- 미션은 [java-subway-path-precourse 저장소](https://github.com/woowacourse/java-subway-path-precourse) 를 fork/clone해 시작한다. -- 기능을 구현하기 전에 java-subway-path-precourse/docs/README.md 파일에 구현할 기능 목록을 정리해 추가한다. -- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다. - - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다. -- [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 절차를 따라 미션을 제출한다. - - [프리코스 과제 FAQ](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse/faq) 문서를 참고하여 진행할 수 있다. -
- -## 📝 License - -This project is [MIT](https://github.com/woowacourse/java-subway-path-precourse/blob/master/LICENSE.md) licensed. +## 초기 설정 + - 프로그램 시작 시 역, 노선, 구간 정보를 설정 + - 거리와 소요 시간은 양의 정수(단위는 km와 분) + - 2호선: 교대역 - ( 2km / 3분 ) - 강남역 - ( 2km / 3분 ) - 역삼역 + - 3호선: 교대역 - ( 3km / 2분 ) - 남부터미널역 - ( 6km / 5분 ) - 양재역 - ( 1km / 1분 ) - 매봉역 + - 신분당선: 강남역 - ( 2km / 8분 ) - 양재역 - ( 10km / 3분 ) - 양재시민의숲역 + +## 경로 조회 기능 + - 출발역과 도착역을 입력받아 경로를 조회한다. + - 경로 조회 시 총 거리, 총 소요 시간도 함께 출력한다. + - 경로 조회 기준은 '최단 거리' '최소 시간' + +## 구현기능 목록 + - 구간의 정보(역과 역을 연결하고 거리와 시간정보)을 가지는 Section 클래스 추가 + - 구간의 정보를 저장하는 Repository 추가 + - 노선 안에 구간을 추가하는 기능 추가 + - 역 안에 구간의 정보(Section)를 담는 기능 추가 + - 초기 설정값 세팅하는 기능 추가 + - 메인메뉴 view 화면 추가 + - 경로 조회 메뉴 view 화면 추가 + - 출발역과 도착역을 입력받는 기능을 추가 + - 경로를 어느 기준으로 선택할지 보는 화면 추가 + - 최단 거리를 기준으로 조회하는 기능 추가 + - 최소 시간 기준으로 조회하는 기능 추가 + + +## 고려할 사항 + - 이동은 양측방향이 다 가능함 + - 환승은 허용됨 + - 역이 이어져 있는지 어떻게 증명할것인가 diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java index 0bcf786cc..5a4f7f105 100644 --- a/src/main/java/subway/Application.java +++ b/src/main/java/subway/Application.java @@ -1,10 +1,15 @@ package subway; +import subway.util.DefaultSetting; +import subway.view.MainMenu; + import java.util.Scanner; public class Application { public static void main(String[] args) { final Scanner scanner = new Scanner(System.in); // TODO: 프로그램 구현 + DefaultSetting.defaultSetting(); + new MainMenu(scanner).startMainMENU(); } } diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java index f4d738d5a..feeccfeaf 100644 --- a/src/main/java/subway/domain/Line.java +++ b/src/main/java/subway/domain/Line.java @@ -1,5 +1,10 @@ package subway.domain; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + public class Line { private String name; @@ -12,4 +17,20 @@ public String getName() { } // 추가 기능 구현 + + private List stationList = new ArrayList<>(); + + public List stations() { + return Collections.unmodifiableList(stationList); + } + + public void addStation(String stationName) { + Station station = StationRepository.findStationByName(stationName); + station.addLine(this.name); + stationList.add(station); + } + + public boolean deleteStation(String name) { + return stationList.removeIf(station -> station.getName().equals(name)); + } } diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/domain/LineRepository.java index 2c4a723c9..c3f66e140 100644 --- a/src/main/java/subway/domain/LineRepository.java +++ b/src/main/java/subway/domain/LineRepository.java @@ -20,7 +20,12 @@ public static boolean deleteLineByName(String name) { return lines.removeIf(line -> Objects.equals(line.getName(), name)); } - public static void deleteAll() { - lines.clear(); + public static Line findLineByName(String lineName) { + for (Line line : lines) { + if (line.getName().equals(lineName)) { + return line; + } + } + return null; } } diff --git a/src/main/java/subway/domain/Section.java b/src/main/java/subway/domain/Section.java new file mode 100644 index 000000000..51c65cd01 --- /dev/null +++ b/src/main/java/subway/domain/Section.java @@ -0,0 +1,25 @@ +package subway.domain; + +public class Section { + private String linkedStationName; + private int distance; + private int time; + + public Section(String linkedStationName, int distance, int time) { + this.linkedStationName = linkedStationName; + this.distance = distance; + this.time = time; + } + + public String getLinkedStationName() { + return linkedStationName; + } + + public int getDistance() { + return distance; + } + + public int getTime() { + return time; + } +} diff --git a/src/main/java/subway/domain/SectionRepository.java b/src/main/java/subway/domain/SectionRepository.java new file mode 100644 index 000000000..b7493856c --- /dev/null +++ b/src/main/java/subway/domain/SectionRepository.java @@ -0,0 +1,26 @@ +package subway.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class SectionRepository { + private static final List
sections = new ArrayList<>(); + + public static List
sections() { + return Collections.unmodifiableList(sections); + } + + public static void addSetion(Section section) { + sections.add(section); + } + + public static boolean deleteSectionByName(String name) { + return sections.removeIf(line -> Objects.equals(line.getLinkedStationName(), name)); + } + + public static void deleteAll() { + sections.clear(); + } +} diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java index bdb142590..c6022790c 100644 --- a/src/main/java/subway/domain/Station.java +++ b/src/main/java/subway/domain/Station.java @@ -1,5 +1,9 @@ package subway.domain; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + public class Station { private String name; @@ -12,4 +16,42 @@ public String getName() { } // 추가 기능 구현 + private List lineList = new ArrayList<>(); + + public void addLine(String lineName) { + lineList.add(lineName); + } + + public boolean includedLine(String lineName) { + return lineList.contains(lineName); + } + + private List
sectionList = new ArrayList<>(); + + public Station addSection(String linkedStationName, int distance, int time) { + sectionList.add(new Section(linkedStationName, distance, time)); + return this; + } + + public List
sections() { + return Collections.unmodifiableList(sectionList); + } + + public int getSectionDistance(String stationName) { + for (Section section : sectionList) { + if (section.getLinkedStationName().equals(stationName)) { + return section.getDistance(); + } + } + return 0; + } + + public int getSectionTime(String stationName) { + for (Section section : sectionList) { + if (section.getLinkedStationName().equals(stationName)) { + return section.getTime(); + } + } + return 0; + } } diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java index 8ed9d103f..ae0194700 100644 --- a/src/main/java/subway/domain/StationRepository.java +++ b/src/main/java/subway/domain/StationRepository.java @@ -1,5 +1,8 @@ package subway.domain; +import subway.util.DistanceRouteSearch; +import subway.util.TimeRouteSearch; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -14,6 +17,8 @@ public static List stations() { public static void addStation(Station station) { stations.add(station); + DistanceRouteSearch.addVertex(station.getName()); + TimeRouteSearch.addVertex(station.getName()); } public static boolean deleteStation(String name) { @@ -23,4 +28,13 @@ public static boolean deleteStation(String name) { public static void deleteAll() { stations.clear(); } + + public static Station findStationByName(String name) { + for (Station station : stations()) { + if (station.getName().equals(name)) { + return station; + } + } + return null; + } } diff --git a/src/main/java/subway/service/RouteSearchService.java b/src/main/java/subway/service/RouteSearchService.java new file mode 100644 index 000000000..fdccc4ba7 --- /dev/null +++ b/src/main/java/subway/service/RouteSearchService.java @@ -0,0 +1,4 @@ +package subway.service; + +public class RouteSearchService { +} diff --git a/src/main/java/subway/util/CalculatorRoute.java b/src/main/java/subway/util/CalculatorRoute.java new file mode 100644 index 000000000..7096887d4 --- /dev/null +++ b/src/main/java/subway/util/CalculatorRoute.java @@ -0,0 +1,31 @@ +package subway.util; + +import subway.domain.Station; +import subway.domain.StationRepository; + +import java.util.List; + +public class CalculatorRoute { + + public static int calculatorRoutDistance(List stationList) { + int result = 0; + String tempStataion = stationList.get(0); + for (int i = 1; i < stationList.size(); i++) { + Station station = StationRepository.findStationByName(tempStataion); + result += station.getSectionDistance(stationList.get(i)); + tempStataion = stationList.get(i); + } + return result; + } + + public static int calculatorRouteTime(List stationList) { + int result = 0; + String tempStataion = stationList.get(0); + for (int i = 1; i < stationList.size(); i++) { + Station station = StationRepository.findStationByName(tempStataion); + result += station.getSectionTime(stationList.get(i)); + tempStataion = stationList.get(i); + } + return result; + } +} diff --git a/src/main/java/subway/util/DefaultSetting.java b/src/main/java/subway/util/DefaultSetting.java new file mode 100644 index 000000000..387479395 --- /dev/null +++ b/src/main/java/subway/util/DefaultSetting.java @@ -0,0 +1,85 @@ +package subway.util; + +import subway.domain.Line; +import subway.domain.LineRepository; +import subway.domain.Station; +import subway.domain.StationRepository; +import subway.util.enums.DefaultLines; +import subway.util.enums.DefaultStations; + +public class DefaultSetting { + private static final String KYODAE = "교대역"; + private static final String GANGNAM = "강남역"; + private static final String YEOKSAM = "역삼역"; + private static final String NORTH_TERMINAL = "남부터미널역"; + private static final String YANGJAE = "양재역"; + private static final String YANGJAE_CITIZEN_FOREST = "양재시민의숲역"; + private static final String MAEBONG = "매봉역"; + private static final String LINE2 = "2호선"; + private static final String LINE3 = "3호선"; + private static final String SINBUNDANG_LINE = "신분당선"; + + public static void defaultSetting() { + for (DefaultStations ds : DefaultStations.values()) { + StationRepository.addStation(new Station(ds.getName())); + } + + for (DefaultLines dl : DefaultLines.values()) { + LineRepository.addLine(new Line(dl.getName())); + } + + Line line2 = LineRepository.findLineByName(LINE2); + line2.addStation(KYODAE); + line2.addStation(GANGNAM); + line2.addStation(YEOKSAM); + + Line line3 = LineRepository.findLineByName(LINE3); + line3.addStation(KYODAE); + line3.addStation(NORTH_TERMINAL); + line3.addStation(YANGJAE); + line3.addStation(MAEBONG); + + Line sinBunDang = LineRepository.findLineByName(SINBUNDANG_LINE); + sinBunDang.addStation(GANGNAM); + sinBunDang.addStation(YANGJAE); + sinBunDang.addStation(YANGJAE_CITIZEN_FOREST); + + StationRepository.findStationByName(KYODAE) + .addSection(GANGNAM, 2, 3) + .addSection(DefaultStations.NORTH_TERMINAL.getName(), 3, 2); + StationRepository.findStationByName(GANGNAM) + .addSection(KYODAE, 2, 3) + .addSection(YEOKSAM, 2, 3) + .addSection(YANGJAE, 2, 8); + StationRepository.findStationByName(YEOKSAM) + .addSection(GANGNAM, 2, 3); + StationRepository.findStationByName(NORTH_TERMINAL) + .addSection(KYODAE, 3, 2) + .addSection(YANGJAE, 6, 5); + StationRepository.findStationByName(YANGJAE) + .addSection(NORTH_TERMINAL, 6, 5) + .addSection(MAEBONG, 1, 1) + .addSection(GANGNAM, 2, 8) + .addSection(YANGJAE_CITIZEN_FOREST, 10, 3); + StationRepository.findStationByName(MAEBONG) + .addSection(YANGJAE, 1, 1); + StationRepository.findStationByName(YANGJAE_CITIZEN_FOREST) + .addSection(YANGJAE, 10, 3); + + DistanceRouteSearch.addDistanceEdgeWeight(KYODAE, GANGNAM, 2); + DistanceRouteSearch.addDistanceEdgeWeight(GANGNAM, YEOKSAM, 2); + DistanceRouteSearch.addDistanceEdgeWeight(KYODAE, NORTH_TERMINAL, 3); + DistanceRouteSearch.addDistanceEdgeWeight(NORTH_TERMINAL, YANGJAE, 6); + DistanceRouteSearch.addDistanceEdgeWeight(YANGJAE, MAEBONG, 1); + DistanceRouteSearch.addDistanceEdgeWeight(GANGNAM, YANGJAE, 2); + DistanceRouteSearch.addDistanceEdgeWeight(YANGJAE, YANGJAE_CITIZEN_FOREST, 10); + + TimeRouteSearch.addDistanceEdgeWeight(KYODAE, GANGNAM, 3); + TimeRouteSearch.addDistanceEdgeWeight(GANGNAM, YEOKSAM, 3); + TimeRouteSearch.addDistanceEdgeWeight(KYODAE, NORTH_TERMINAL, 2); + TimeRouteSearch.addDistanceEdgeWeight(NORTH_TERMINAL, YANGJAE, 5); + TimeRouteSearch.addDistanceEdgeWeight(YANGJAE, MAEBONG, 1); + TimeRouteSearch.addDistanceEdgeWeight(GANGNAM, YANGJAE, 8); + TimeRouteSearch.addDistanceEdgeWeight(YANGJAE, YANGJAE_CITIZEN_FOREST, 3); + } +} diff --git a/src/main/java/subway/util/DistanceRouteSearch.java b/src/main/java/subway/util/DistanceRouteSearch.java new file mode 100644 index 000000000..8e92cfa31 --- /dev/null +++ b/src/main/java/subway/util/DistanceRouteSearch.java @@ -0,0 +1,25 @@ +package subway.util; + +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.WeightedMultigraph; + +import java.util.List; + +public class DistanceRouteSearch { + private static final WeightedMultigraph distanceGraph + = new WeightedMultigraph<>(DefaultWeightedEdge.class); + + public static void addVertex(String stationName) { + distanceGraph.addVertex(stationName); + } + + public static void addDistanceEdgeWeight(String station1, String station2, int distance) { + distanceGraph.setEdgeWeight(distanceGraph.addEdge(station1, station2), distance); + } + + public static List getShortestRoute(String startStation, String arrivalStation) { + DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(distanceGraph); + return dijkstraShortestPath.getPath(startStation, arrivalStation).getVertexList(); + } +} diff --git a/src/main/java/subway/util/TimeRouteSearch.java b/src/main/java/subway/util/TimeRouteSearch.java new file mode 100644 index 000000000..76c8a48de --- /dev/null +++ b/src/main/java/subway/util/TimeRouteSearch.java @@ -0,0 +1,25 @@ +package subway.util; + +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.WeightedMultigraph; + +import java.util.List; + +public class TimeRouteSearch { + private static final WeightedMultigraph timeGraph + = new WeightedMultigraph<>(DefaultWeightedEdge.class); + + public static void addVertex(String stationName) { + timeGraph.addVertex(stationName); + } + + public static void addDistanceEdgeWeight(String station1, String station2, int distance) { + timeGraph.setEdgeWeight(timeGraph.addEdge(station1, station2), distance); + } + + public static List getShortestRoute(String startStation, String arrivalStation) { + DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(timeGraph); + return dijkstraShortestPath.getPath(startStation, arrivalStation).getVertexList(); + } +} diff --git a/src/main/java/subway/util/enums/DefaultLines.java b/src/main/java/subway/util/enums/DefaultLines.java new file mode 100644 index 000000000..e1bf14376 --- /dev/null +++ b/src/main/java/subway/util/enums/DefaultLines.java @@ -0,0 +1,17 @@ +package subway.util.enums; + +public enum DefaultLines { + LINE2("2호선"), + LINE3("3호선"), + SINBUNDANG_LINE("신분당선"); + + private String name; + + private DefaultLines(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/subway/util/enums/DefaultStations.java b/src/main/java/subway/util/enums/DefaultStations.java new file mode 100644 index 000000000..e5eedc533 --- /dev/null +++ b/src/main/java/subway/util/enums/DefaultStations.java @@ -0,0 +1,21 @@ +package subway.util.enums; + +public enum DefaultStations { + KYODAE("교대역"), + GANGNAM("강남역"), + YEOKSAM("역삼역"), + NORTH_TERMINAL("남부터미널역"), + YANGJAE("양재역"), + YANGJAE_CITIZEN_FOREST("양재시민의숲역"), + MAEBONG("매봉역"); + + private String name; + + DefaultStations(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/subway/util/validator/MenuValidator.java b/src/main/java/subway/util/validator/MenuValidator.java new file mode 100644 index 000000000..2959cc417 --- /dev/null +++ b/src/main/java/subway/util/validator/MenuValidator.java @@ -0,0 +1,24 @@ +package subway.util.validator; + +public class MenuValidator { + private static final String ERROR = "[ ERROR ] "; + private static final String MENU_ERROR = "존재하지않는 메뉴입니다."; + + public static boolean isVailableMainMenu(String input) { + if (input.equals("1") || input.equals("Q")) { + return true; + } + System.out.println(ERROR + MENU_ERROR); + System.out.println(); + return false; + } + + public static boolean isVailableRouteSearchMenu(String input) { + if (input.equals("1") || input.equals("2") || input.equals("B")) { + return true; + } + System.out.println(ERROR + MENU_ERROR); + System.out.println(); + return false; + } +} diff --git a/src/main/java/subway/util/validator/StationValidator.java b/src/main/java/subway/util/validator/StationValidator.java new file mode 100644 index 000000000..d9f024d0b --- /dev/null +++ b/src/main/java/subway/util/validator/StationValidator.java @@ -0,0 +1,42 @@ +package subway.util.validator; + +import subway.domain.StationRepository; + +public class StationValidator { + private static final String ERROR = "[ERROR] "; + private static final String DUPLICATE_MESSAGE = "출발역과 도착역이 동일합니다."; + private static final String AVAILABLE_STATION_MESSAGE = "연결되어있지 않은 역입니다."; + private static final String NONEEXIST_STATION = "존재하지 않는 역입니다."; + + public static boolean checkVailableArrivalStation(String startStation, String arrivalStation) { + return isLinkedStation(startStation, arrivalStation) && ! duplicateStation(startStation, arrivalStation); + + } + + public static boolean haveStation(String name) { + if (StationRepository.findStationByName(name) != null) { + return true; + } + System.out.println(ERROR + NONEEXIST_STATION); + System.out.println(); + return false; + } + + public static boolean isLinkedStation(String startStation, String arrivalStation) { + if (StationRepository.findStationByName(arrivalStation) != null) { + return true; + } + System.out.println(ERROR + AVAILABLE_STATION_MESSAGE); + System.out.println(); + return false; + } + + public static boolean duplicateStation(String startStation, String arrivalStation) { + if (startStation.equals(arrivalStation)) { + System.out.println(ERROR + DUPLICATE_MESSAGE); + System.out.println(); + return true; + } + return false; + } +} diff --git a/src/main/java/subway/view/MainMenu.java b/src/main/java/subway/view/MainMenu.java new file mode 100644 index 000000000..f4f56aa67 --- /dev/null +++ b/src/main/java/subway/view/MainMenu.java @@ -0,0 +1,51 @@ +package subway.view; + +import subway.util.validator.MenuValidator; + +import java.util.Scanner; + +public class MainMenu { + private final String MAIN_TITLE = "## 메인 화면"; + private final String MAINMENU_1 = "1. 경로 조회"; + private final String QUIT_APP = "Q. 종료"; + private final String ENTER = "\n"; + private final String CHOICE_MENU = "## 원하는 기능을 선택하세요."; + private final String QUIT_MESSAGE = "프로그램을 종료합니다."; + + private Scanner scanner; + + public MainMenu(Scanner scanner) { + this.scanner = scanner; + } + + public void startMainMENU() { + inputMenu(); + } + + private void inputMenu() { + while (true) { + outputMainMenu(); + String input = scanner.nextLine(); + System.out.println(); + MenuValidator.isVailableMainMenu(input); + if (input.equals("1")) { + new RouteSearchMenu(scanner).startRouteSearchMenu(); + } + if (input.equals("Q")) { + System.out.println(QUIT_MESSAGE); + return; + } + } + } + + + private void outputMainMenu() { + StringBuilder sb = new StringBuilder(); + sb.append(MAIN_TITLE).append(ENTER) + .append(MAINMENU_1).append(ENTER) + .append(QUIT_APP).append(ENTER) + .append(ENTER) + .append(CHOICE_MENU); + System.out.println(sb); + } +} diff --git a/src/main/java/subway/view/RouteSearchMenu.java b/src/main/java/subway/view/RouteSearchMenu.java new file mode 100644 index 000000000..2f0e3223e --- /dev/null +++ b/src/main/java/subway/view/RouteSearchMenu.java @@ -0,0 +1,119 @@ +package subway.view; + +import subway.util.CalculatorRoute; +import subway.util.DistanceRouteSearch; +import subway.util.validator.MenuValidator; +import subway.util.validator.StationValidator; +import subway.util.TimeRouteSearch; + +import java.util.List; +import java.util.Scanner; + +public class RouteSearchMenu { + private final String ROUTE_SEARCH_TITLE = "## 경로 기준"; + private final String MENU1 = "1. 최단 거리"; + private final String MENU2 = "2. 최소 시간"; + private final String BACK = "B. 돌아가기"; + private final String ENTER = "\n"; + private final String CHOICE_MENU = "## 원하는 기능을 선택하세요."; + private final String REQUEST_START = "## 출발역을 입력하세요."; + private final String REQUEST_ARRIVAL = "## 도착역을 입력하세요."; + private final String LINE = "---"; + private final String INFO = "[ INFO ] "; + + private Scanner scanner; + private String input; + private String startStationName; + private String arrivalStationName; + + public RouteSearchMenu(Scanner scanner) { + this.scanner = scanner; + } + + public void startRouteSearchMenu() { + inputMenu(); + } + + private void inputMenu() { + while (true) { + outputRouteSearchMenu(); + this.input = scanner.nextLine(); + System.out.println(); + MenuValidator.isVailableRouteSearchMenu(input); + if (input.equals("1") || input.equals("2")) { + requestStartStation(); + } + if (input.equals("B")) { + return; + } + } + } + + private void requestStartStation() { + System.out.println(REQUEST_START); + this.startStationName = scanner.nextLine(); + System.out.println(); + if (StationValidator.haveStation(startStationName)) { + requestArrivalStation(); + } + } + + private void requestArrivalStation() { + System.out.println(REQUEST_ARRIVAL); + this.arrivalStationName = scanner.nextLine(); + System.out.println(); + if (StationValidator.checkVailableArrivalStation(startStationName, arrivalStationName)) { + checkSearchMethod(); + } + } + + private void checkSearchMethod() { + if (input.equals("1")) { + searchShortestDistanceRoute(); + } + if (input.equals("2")) { + searchShortestTimeRoute(); + } + } + + private void searchShortestDistanceRoute() { + List list = DistanceRouteSearch.getShortestRoute(startStationName, arrivalStationName); + int time = CalculatorRoute.calculatorRouteTime(list); + int distance = CalculatorRoute.calculatorRoutDistance(list); + System.out.println("## 조회결과"); + System.out.println(LINE); + System.out.println("총 거리: " + distance + "km"); + System.out.println("총 소요 시간 : " + time + "분"); + System.out.println(LINE); + for (String station : list) { + System.out.println(INFO + station); + } + System.out.println(); + } + + private void searchShortestTimeRoute() { + List list = TimeRouteSearch.getShortestRoute(startStationName, arrivalStationName); + int time = CalculatorRoute.calculatorRouteTime(list); + int distance = CalculatorRoute.calculatorRoutDistance(list); + System.out.println("## 조회결과"); + System.out.println(LINE); + System.out.println("총 거리: " + distance + "km"); + System.out.println("총 소요 시간 : " + time + "분"); + System.out.println(LINE); + for (String station : list) { + System.out.println(INFO + station); + } + System.out.println(); + } + + private void outputRouteSearchMenu() { + StringBuilder sb = new StringBuilder(); + sb.append(ROUTE_SEARCH_TITLE).append(ENTER) + .append(MENU1).append(ENTER) + .append(MENU2).append(ENTER) + .append(BACK).append(ENTER) + .append(ENTER) + .append(CHOICE_MENU); + System.out.println(sb); + } +} diff --git a/src/test/java/subway/DefaultSettingTest.java b/src/test/java/subway/DefaultSettingTest.java new file mode 100644 index 000000000..73a6beacd --- /dev/null +++ b/src/test/java/subway/DefaultSettingTest.java @@ -0,0 +1,48 @@ +package subway; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import subway.domain.Line; +import subway.domain.LineRepository; +import subway.domain.Station; +import subway.domain.StationRepository; +import subway.util.DefaultSetting; +import subway.util.enums.DefaultLines; +import subway.util.enums.DefaultStations; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class DefaultSettingTest { + + @BeforeAll + public static void 초기값_세팅() { + DefaultSetting.defaultSetting(); + } + + @Test + public void 이호선_만들어졌다() { + Line line2 = LineRepository.findLineByName(DefaultLines.LINE2.getName()); + assertEquals(line2.getName(), DefaultLines.LINE2.getName()); + } + + @Test + public void 노선들_만들어졌다() { + List lineList= LineRepository.lines(); + assertEquals(lineList.size(), 3); + } + + @Test + public void 역들_만들어졌다() { + List stationList = StationRepository.stations(); + assertEquals(stationList.size(), 7); + } + + @Test + public void 구간_적용됐다() { + Station station = StationRepository.findStationByName(DefaultStations.KYODAE.getName()); + assertEquals(station.sections().size(), 2); + } + +}