diff --git a/README.md b/README.md
index d6299154c..b7b7c8acb 100644
--- a/README.md
+++ b/README.md
@@ -1,68 +1,46 @@
# 지하철 노선도 경로 조회 미션
-- 등록된 지하철 노선도에서 경로를 조회하는 기능을 구현한다.
-
+등록된 지하철 노선도에서 최단 거리/최소 시간으로 경로를 조회하는 프로그램
-## 🚀 기능 요구사항
+## 기능 목록
-> 프리코스 3주차 미션에서 사용한 코드를 참고해도 무관하다.
+ - [x] 프로그램 시작 시 역, 노선, 구간 정보를 초기 설정한다.
-### 초기 설정
-- 프로그램 시작 시 역, 노선, 구간 정보를 초기 설정 해야 한다.
-- 거리와 소요 시간은 양의 정수이며 단위는 km와 분을 의미한다.
-- 아래의 사전 등록 정보로 반드시 초기 설정을 한다.
+ - [x] 메인 메뉴를 출력하고 사용자의 선택을 입력을 받는다.
-```
- 1. 지하철역으로 교대역, 강남역, 역삼역, 남부터미널역, 양재역, 양재시민의숲역, 매봉역이 등록되어 있다.
- 2. 지하철 노선으로 2호선, 3호선, 신분당선이 등록되어 있다.
- 3. 노선에 역이 아래와 같이 등록되어 있다.(왼쪽 끝이 상행 종점)
- - 2호선: 교대역 - ( 2km / 3분 ) - 강남역 - ( 2km / 3분 ) - 역삼역
- - 3호선: 교대역 - ( 3km / 2분 ) - 남부터미널역 - ( 6km / 5분 ) - 양재역 - ( 1km / 1분 ) - 매봉역
- - 신분당선: 강남역 - ( 2km / 8분 ) - 양재역 - ( 10km / 3분 ) - 양재시민의숲역
- ```
-
-### 경로 조회 기능
-- 출발역과 도착역을 입력받아 경로를 조회한다.
-- 경로 조회 시 총 거리, 총 소요 시간도 함께 출력한다.
-- 경로 조회 기준은 `최단 거리` `최소 시간`이 있다.
-
-### 예외 처리
-- 경로 조회 시 출발역과 도착역이 같으면 에러를 출력한다.
-- 경로 조회 시 출발역과 도착역이 연결되어 있지 않으면 에러를 출력한다.
-- 그 외 정상적으로 프로그램이 수행되지 않은 경우 에러를 출력한다.
+ - [x] 메인 메뉴를 출력한다.
+ - [x] 사용자의 입력을 받는다.
+ - [x] 1 을 입력받으면 경로 조회 화면으로 전환한다.
+ - [x] Q 를 입력받으면 프로그램을 종료한다.
+ - [x] [예외 처리] 메뉴에 없는 값을 입력받으면 에러를 출력한다.
-
+ - [x] 경로를 조회할 기준을 출력하고 사용자의 선택을 입력받는다.
-## ✍🏻 입출력 요구사항
-- `프로그래밍 실행 결과 예시`와 동일하게 입출력을 구현한다.
-- 기대하는 출력 결과는 `[INFO]`를 붙여서 출력한다. 출력값의 형식은 예시와 동일하게 한다.
-- 에러 발생 시 `[ERROR]`를 붙여서 출력한다. 에러의 문구는 자유롭게 작성한다.
+ - [x] 경로를 조회할 기준 메뉴를 출력한다.
+ - [x] 1 을 입력받으면 최단 거리를 기준으로 경로를 조회한다.
+ - [x] 2 를 입력받으면 최소 시간을 기준으로 경로를 조회한다.
+ - [x] B 를 입력받으면 메인 메뉴로 돌아간다.
+ - [x] [예외 처리] 메뉴에 없는 값을 입력받으면 에러를 출력한다.
-### 💻 프로그래밍 실행 결과 예시
-#### 경로 조회
-```
-## 메인 화면
-1. 경로 조회
-Q. 종료
+ - [x] 경로를 조회하는 기능에서는 출발역과 도착역을 설정한다.
+
+ - [x] 출발역을 입력받는다.
+ - [x] 도착역을 입력받는다.
+ - [x] [예외 처리] 등록되지 않은 역을 입력받으면 에러를 출력한다.
+ - [x] [예외 처리] 입력받은 출발역과 도착역이 같으면 에러를 출력한다.
-## 원하는 기능을 선택하세요.
-1
+ - [x] 출발역에서 도착역까지 가는 최단 거리 경로를 구한다.
-## 경로 기준
-1. 최단 거리
-2. 최소 시간
-B. 돌아가기
+ - [x] [예외 처리] 출발 역과 도착역이 연결되어 있지 않으면 에러를 출력한다.
-## 원하는 기능을 선택하세요.
-1
+ - [x] 출발역에서 도착역까지 가는 최단 시간 경로를 구한다.
-## 출발역을 입력하세요.
-교대역
+ - [x] [예외 처리] 출발 역과 도착역이 연결되어 있지 않으면 에러를 출력한다.
-## 도착역을 입력하세요.
-양재역
+ - [x] 출발역에서 도착역 까지 가는 경로를 포함하여 총 거리와 총 소요 시간을 아래와 같이 출력한다.
-## 조회 결과
+```
+##조회 결과
[INFO] ---
[INFO] 총 거리: 6km
[INFO] 총 소요 시간: 14분
@@ -70,184 +48,14 @@ B. 돌아가기
[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, 시간은 분 단위로 계산한다.
+
+ - 기대하는 출력 결과는 [INFO]를 붙인다.
+
+ - 에러 출력은 [ERROR]를 붙인다.
+
+ - jgrapht 라이브러리를 활용한다.
\ No newline at end of file
diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java
index 0bcf786cc..e2e9fce0b 100644
--- a/src/main/java/subway/Application.java
+++ b/src/main/java/subway/Application.java
@@ -1,10 +1,16 @@
package subway;
+import subway.controller.MainController;
+
import java.util.Scanner;
public class Application {
public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
- // TODO: 프로그램 구현
+
+ InitialSetting.settingInitialSubways();
+
+ MainController mainController = MainController.getInstance();
+ mainController.run(scanner);
}
}
diff --git a/src/main/java/subway/InitialSetting.java b/src/main/java/subway/InitialSetting.java
new file mode 100644
index 000000000..196128780
--- /dev/null
+++ b/src/main/java/subway/InitialSetting.java
@@ -0,0 +1,89 @@
+package subway;
+
+import subway.domain.Line;
+import subway.domain.RequiredResources;
+import subway.domain.Section;
+import subway.domain.Station;
+import subway.repository.LineRepository;
+import subway.repository.SectionRepository;
+import subway.repository.StationRepository;
+
+public class InitialSetting {
+ private static final String GYODAE = "교대역";
+ private static final String GANGNAM = "강남역";
+ private static final String YUCKSAM = "역삼역";
+ private static final String NAMBUTERMINAL = "남부터미널역";
+ private static final String YANGGAE = "양재역";
+ private static final String YANGGAEFORREST = "양재시민의숲역";
+ private static final String MAEBONG = "매봉역";
+ private static final String LINE_TWO = "2호선";
+ private static final String LINE_THREE = "3호선";
+ private static final String LINE_SINBUNDANG = "신분당선";
+ private static final String[] LINE_TWO_STATIONS = {"교대역", "강남역", "역삼역"};
+ private static final String[] LINE_THREE_STATIONS = {"교대역", "남부터미널역", "양재역", "매봉역"};
+ private static final String[] LINE_SINBUNDANG_STATIONS = {"강남역", "양재역", "양재시민의숲역"};
+
+ public static void settingInitialSubways() {
+ deleteAllRepository();
+
+ settingInitialStations();
+
+ settingInitialLines(LINE_TWO, LINE_TWO_STATIONS);
+ settingInitialLines(LINE_THREE, LINE_THREE_STATIONS);
+ settingInitialLines(LINE_SINBUNDANG, LINE_SINBUNDANG_STATIONS);
+
+ settingInitialSection();
+ }
+
+ private static void deleteAllRepository() {
+ StationRepository.deleteAll();
+ LineRepository.deleteAll();
+ SectionRepository.deleteAll();
+ }
+
+ private static void settingInitialStations() {
+ StationRepository.addStation(new Station(GYODAE));
+ StationRepository.addStation(new Station(GANGNAM));
+ StationRepository.addStation(new Station(YUCKSAM));
+ StationRepository.addStation(new Station(NAMBUTERMINAL));
+ StationRepository.addStation(new Station(YANGGAE));
+ StationRepository.addStation(new Station(YANGGAEFORREST));
+ StationRepository.addStation(new Station(MAEBONG));
+ }
+
+ private static void settingInitialLines(String lineName, String[] stationNames) {
+ Line line = new Line(lineName);
+ addStationInLine(line, stationNames);
+ LineRepository.addLine(line);
+ }
+
+ private static void addStationInLine(Line line, String[] stationNames) {
+ for (String stationName : stationNames) {
+ line.addLineStation(new Station(stationName));
+ }
+ }
+
+ private static void settingInitialSection() {
+ Section section = new Section(new Station(GYODAE), new Station(GANGNAM));
+ RequiredResources requiredResources = new RequiredResources(2, 3);
+ SectionRepository.addSection(section, requiredResources);
+ section = new Section(new Station(GANGNAM), new Station(YUCKSAM));
+ requiredResources = new RequiredResources(2, 3);
+ SectionRepository.addSection(section, requiredResources);
+ section = new Section(new Station(GYODAE), new Station(NAMBUTERMINAL));
+ requiredResources = new RequiredResources(3, 2);
+ SectionRepository.addSection(section, requiredResources);
+ section = new Section(new Station(NAMBUTERMINAL), new Station(YANGGAE));
+ requiredResources = new RequiredResources(6, 5);
+ SectionRepository.addSection(section, requiredResources);
+ section = new Section(new Station(YANGGAE), new Station(MAEBONG));
+ requiredResources = new RequiredResources(1, 1);
+ SectionRepository.addSection(section, requiredResources);
+ section = new Section(new Station(GANGNAM), new Station(YANGGAE));
+ requiredResources = new RequiredResources(2, 8);
+ SectionRepository.addSection(section, requiredResources);
+ section = new Section(new Station(YANGGAE), new Station(YANGGAEFORREST));
+ requiredResources = new RequiredResources(10, 3);
+ SectionRepository.addSection(section, requiredResources);
+ }
+}
diff --git a/src/main/java/subway/controller/Controller.java b/src/main/java/subway/controller/Controller.java
new file mode 100644
index 000000000..9ceb375b9
--- /dev/null
+++ b/src/main/java/subway/controller/Controller.java
@@ -0,0 +1,7 @@
+package subway.controller;
+
+import java.util.Scanner;
+
+public interface Controller {
+ String mappingMenu(Scanner scanner);
+}
diff --git a/src/main/java/subway/controller/MainController.java b/src/main/java/subway/controller/MainController.java
new file mode 100644
index 000000000..bcec3d531
--- /dev/null
+++ b/src/main/java/subway/controller/MainController.java
@@ -0,0 +1,45 @@
+package subway.controller;
+
+import subway.menus.MainMenu;
+import subway.views.mainviews.MainInputView;
+import subway.views.mainviews.MainOutputView;
+
+import java.util.Scanner;
+
+public class MainController implements Controller{
+ private static final String END_CODE = "Q";
+ private static final MainController MAIN_CONTROLLER = new MainController();
+
+ private MainController() {
+ }
+
+ public static MainController getInstance() {
+ return MAIN_CONTROLLER;
+ }
+
+ public void run(Scanner scanner) {
+ String selectedOption;
+ do {
+ selectedOption = mappingMenu(scanner);
+ } while (!selectedOption.equals(END_CODE));
+ }
+
+ public String mappingMenu(Scanner scanner) {
+ try {
+ MainOutputView.printMainMenu();
+ String selectedOption = MainInputView.inputMainOption(scanner);
+ branchBySelectedOption(selectedOption, scanner);
+ return selectedOption;
+ } catch (IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ return mappingMenu(scanner);
+ }
+ }
+
+ private void branchBySelectedOption(String selectedOption, Scanner scanner) {
+ if (selectedOption.equals(END_CODE)) {
+ return;
+ }
+ MainMenu.execute(selectedOption, scanner);
+ }
+}
diff --git a/src/main/java/subway/controller/RouteController.java b/src/main/java/subway/controller/RouteController.java
new file mode 100644
index 000000000..35d3c59f5
--- /dev/null
+++ b/src/main/java/subway/controller/RouteController.java
@@ -0,0 +1,38 @@
+package subway.controller;
+
+import subway.menus.RouteMenu;
+import subway.views.routeviews.RouteInputView;
+import subway.views.routeviews.RouteOutputView;
+
+import java.util.Scanner;
+
+public class RouteController implements Controller{
+ private static final String GO_BACK_CODE = "B";
+ private static final RouteController ROUTE_CONTROLLER = new RouteController();
+
+ private RouteController() {
+ }
+
+ public static RouteController getInstance() {
+ return ROUTE_CONTROLLER;
+ }
+
+ public String mappingMenu(Scanner scanner) {
+ try {
+ RouteOutputView.printRouteMenu();
+ String selectedOption = RouteInputView.inputRouteOption(scanner);
+ branchBySelectedOption(selectedOption, scanner);
+ return selectedOption;
+ } catch (IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ return mappingMenu(scanner);
+ }
+ }
+
+ private void branchBySelectedOption(String selectedOption, Scanner scanner) {
+ if (selectedOption.equals(GO_BACK_CODE)) {
+ return;
+ }
+ RouteMenu.execute(selectedOption, scanner);
+ }
+}
diff --git a/src/main/java/subway/domain/Distance.java b/src/main/java/subway/domain/Distance.java
new file mode 100644
index 000000000..848b34566
--- /dev/null
+++ b/src/main/java/subway/domain/Distance.java
@@ -0,0 +1,13 @@
+package subway.domain;
+
+public class Distance {
+ private int distance;
+
+ public Distance(int distance) {
+ this.distance = distance;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+}
diff --git a/src/main/java/subway/domain/DistanceMap.java b/src/main/java/subway/domain/DistanceMap.java
new file mode 100644
index 000000000..d035535e2
--- /dev/null
+++ b/src/main/java/subway/domain/DistanceMap.java
@@ -0,0 +1,22 @@
+package subway.domain;
+
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.WeightedMultigraph;
+import subway.repository.SectionRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public class DistanceMap implements SubwayGraph{
+ public void addWeight() {
+ Map sections = SectionRepository.sections();
+ sections.forEach((key, value)
+ -> subwayGraph.setEdgeWeight(subwayGraph.addEdge(key.getFirstStation(), key.getSecondStation()), value.getDistanceResource().getDistance()));
+ }
+
+ public List getShortestRoute(WeightedMultigraph graph, Station first, Station second) {
+ DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(graph);
+ isConnectedMap(dijkstraShortestPath.getPath(first, second));
+ return dijkstraShortestPath.getPath(first, second).getVertexList();
+ }
+}
diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java
index f4d738d5a..c816c1751 100644
--- a/src/main/java/subway/domain/Line.java
+++ b/src/main/java/subway/domain/Line.java
@@ -1,15 +1,40 @@
package subway.domain;
public class Line {
- private String name;
+ private final String name;
+ private final LineStations stations;
public Line(String name) {
this.name = name;
+ this.stations = new LineStations();
}
public String getName() {
return name;
}
- // 추가 기능 구현
+ public void addLineStation(Station station) {
+ stations.addLineStation(station);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (getClass() != object.getClass()) {
+ return false;
+ }
+ boolean isEqualObject = false;
+ Line line = (Line) object;
+ if (getName().equals(line.getName())) {
+ isEqualObject = true;
+ }
+ return isEqualObject;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int hashCode = 1;
+ hashCode = prime * hashCode + getName().hashCode();
+ return hashCode;
+ }
}
diff --git a/src/main/java/subway/domain/LineStations.java b/src/main/java/subway/domain/LineStations.java
new file mode 100644
index 000000000..78e073213
--- /dev/null
+++ b/src/main/java/subway/domain/LineStations.java
@@ -0,0 +1,23 @@
+package subway.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LineStations {
+ private final List stations;
+
+ public LineStations() {
+ this.stations = new ArrayList<>();
+ }
+
+ public void addLineStation(Station station) {
+ stations.add(station);
+ }
+
+ public Boolean isContain(Station station) {
+ if (stations.contains(station)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/subway/domain/RequiredResources.java b/src/main/java/subway/domain/RequiredResources.java
new file mode 100644
index 000000000..d1dc835b8
--- /dev/null
+++ b/src/main/java/subway/domain/RequiredResources.java
@@ -0,0 +1,19 @@
+package subway.domain;
+
+public class RequiredResources {
+ private final Distance distance;
+ private final Time time;
+
+ public RequiredResources(int distance, int time) {
+ this.distance = new Distance(distance);
+ this.time = new Time(time);
+ }
+
+ public Distance getDistanceResource() {
+ return distance;
+ }
+
+ public Time getTimeResource() {
+ return time;
+ }
+}
diff --git a/src/main/java/subway/domain/Section.java b/src/main/java/subway/domain/Section.java
new file mode 100644
index 000000000..92874ef74
--- /dev/null
+++ b/src/main/java/subway/domain/Section.java
@@ -0,0 +1,42 @@
+package subway.domain;
+
+public class Section {
+ private final Station firstStation;
+ private final Station secondStation;
+
+ public Section(Station firstStation, Station secondStation) {
+ this.firstStation = firstStation;
+ this.secondStation = secondStation;
+ }
+
+ public Station getFirstStation() {
+ return firstStation;
+ }
+
+ public Station getSecondStation() {
+ return secondStation;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (getClass() != object.getClass()) {
+ return false;
+ }
+ boolean isEqualObject = false;
+ Section section = (Section) object;
+ if (firstStation.getName().equals(section.firstStation.getName())
+ && secondStation.getName().equals(section.secondStation.getName())) {
+ isEqualObject = true;
+ }
+ return isEqualObject;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int hashCode = 1;
+ hashCode = prime * hashCode + firstStation.getName().hashCode()
+ + secondStation.getName().hashCode();
+ return hashCode;
+ }
+}
diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java
index bdb142590..dba8be823 100644
--- a/src/main/java/subway/domain/Station.java
+++ b/src/main/java/subway/domain/Station.java
@@ -11,5 +11,24 @@ public String getName() {
return name;
}
- // 추가 기능 구현
+ @Override
+ public boolean equals(Object object) {
+ if (getClass() != object.getClass()) {
+ return false;
+ }
+ boolean isEqualObject = false;
+ Station station = (Station) object;
+ if (getName().equals(station.getName())) {
+ isEqualObject = true;
+ }
+ return isEqualObject;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int hashCode = 1;
+ hashCode = prime * hashCode + getName().hashCode();
+ return hashCode;
+ }
}
diff --git a/src/main/java/subway/domain/SubwayGraph.java b/src/main/java/subway/domain/SubwayGraph.java
new file mode 100644
index 000000000..3b37980a3
--- /dev/null
+++ b/src/main/java/subway/domain/SubwayGraph.java
@@ -0,0 +1,33 @@
+package subway.domain;
+
+import org.jgrapht.GraphPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.WeightedMultigraph;
+import subway.repository.StationRepository;
+
+import java.util.List;
+
+public interface SubwayGraph {
+ String NOT_CONNECTED_STATION_MESSAGE = "\n[ERROR] 두 역은 연결되지 않았습니다.\n";
+
+ WeightedMultigraph subwayGraph
+ = new WeightedMultigraph(DefaultWeightedEdge.class);
+
+ default void addStationVertex() {
+ StationRepository.stations()
+ .forEach(subwayGraph::addVertex);
+ }
+
+ default WeightedMultigraph getGraph() {
+ return subwayGraph;
+ }
+
+ default void isConnectedMap(GraphPath shortestPath) {
+ if (shortestPath == null) {
+ throw new IllegalArgumentException(NOT_CONNECTED_STATION_MESSAGE);
+ }
+ }
+
+ void addWeight();
+ List getShortestRoute(WeightedMultigraph graph, Station firstStation, Station secondStation);
+}
diff --git a/src/main/java/subway/domain/Time.java b/src/main/java/subway/domain/Time.java
new file mode 100644
index 000000000..2d847f344
--- /dev/null
+++ b/src/main/java/subway/domain/Time.java
@@ -0,0 +1,13 @@
+package subway.domain;
+
+public class Time {
+ private int time;
+
+ public Time(int time) {
+ this.time = time;
+ }
+
+ public int getTime() {
+ return time;
+ }
+}
diff --git a/src/main/java/subway/domain/TimeMap.java b/src/main/java/subway/domain/TimeMap.java
new file mode 100644
index 000000000..4af5a65b5
--- /dev/null
+++ b/src/main/java/subway/domain/TimeMap.java
@@ -0,0 +1,22 @@
+package subway.domain;
+
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.WeightedMultigraph;
+import subway.repository.SectionRepository;
+
+import java.util.List;
+import java.util.Map;
+
+public class TimeMap implements SubwayGraph{
+ public void addWeight() {
+ Map sections = SectionRepository.sections();
+ sections.forEach((key, value)
+ -> subwayGraph.setEdgeWeight(subwayGraph.addEdge(key.getFirstStation(), key.getSecondStation()), value.getTimeResource().getTime()));
+ }
+
+ public List getShortestRoute(WeightedMultigraph graph, Station first, Station second) {
+ DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(graph);
+ isConnectedMap(dijkstraShortestPath.getPath(first, second));
+ return dijkstraShortestPath.getPath(first, second).getVertexList();
+ }
+}
diff --git a/src/main/java/subway/menus/MainMenu.java b/src/main/java/subway/menus/MainMenu.java
new file mode 100644
index 000000000..3e985732a
--- /dev/null
+++ b/src/main/java/subway/menus/MainMenu.java
@@ -0,0 +1,38 @@
+package subway.menus;
+
+import subway.controller.RouteController;
+import subway.views.OutputConstant;
+
+import java.util.Arrays;
+import java.util.Scanner;
+import java.util.function.Supplier;
+
+public enum MainMenu {
+ ROUTE_LOOKUP("1", "경로 조회", RouteController::getInstance),
+ EXIT_PROGRAM("Q", "종료", () -> null);
+
+ private final String option;
+ private final String description;
+ private final Supplier routeController;
+
+ MainMenu(String option, String description, Supplier routeController) {
+ this.option = option;
+ this.description = description;
+ this.routeController = routeController;
+ }
+
+ public static void execute(String selectedOption, Scanner scanner) {
+ Arrays.stream(values())
+ .filter(mainMenu -> mainMenu.option.equals(selectedOption))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(OutputConstant.NOT_EXIST_OPTION_ERROR))
+ .routeController
+ .get()
+ .mappingMenu(scanner);
+ }
+
+ @Override
+ public String toString() {
+ return option + OutputConstant.OPTION_SEPARATOR + description;
+ }
+}
diff --git a/src/main/java/subway/menus/RouteMenu.java b/src/main/java/subway/menus/RouteMenu.java
new file mode 100644
index 000000000..3f9062ca3
--- /dev/null
+++ b/src/main/java/subway/menus/RouteMenu.java
@@ -0,0 +1,41 @@
+package subway.menus;
+
+import subway.service.DistanceRouteService;
+import subway.service.RouteService;
+import subway.service.TimeRouteService;
+import subway.views.OutputConstant;
+
+import java.util.Arrays;
+import java.util.Scanner;
+import java.util.function.Supplier;
+
+public enum RouteMenu {
+ SHORTEST_DISTANCE("1", "최단 거리", DistanceRouteService::getInstance),
+ SHORTEST_TIME("2", "최소 시간", TimeRouteService::getInstance),
+ GO_BACK_TO_MAIN_MENU("B", "돌아가기", () -> null);
+
+ private final String option;
+ private final String description;
+ private final Supplier routeService;
+
+ RouteMenu(String option, String description, Supplier routeService) {
+ this.option = option;
+ this.description = description;
+ this.routeService = routeService;
+ }
+
+ public static void execute(String selectedOption, Scanner scanner) {
+ Arrays.stream(values())
+ .filter(routeMenu -> routeMenu.option.equals(selectedOption))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(OutputConstant.NOT_EXIST_OPTION_ERROR))
+ .routeService
+ .get()
+ .routingService(scanner);
+ }
+
+ @Override
+ public String toString() {
+ return option + OutputConstant.OPTION_SEPARATOR + description;
+ }
+}
diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/repository/LineRepository.java
similarity index 91%
rename from src/main/java/subway/domain/LineRepository.java
rename to src/main/java/subway/repository/LineRepository.java
index 2c4a723c9..84101c3e0 100644
--- a/src/main/java/subway/domain/LineRepository.java
+++ b/src/main/java/subway/repository/LineRepository.java
@@ -1,4 +1,6 @@
-package subway.domain;
+package subway.repository;
+
+import subway.domain.Line;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/main/java/subway/repository/SectionRepository.java b/src/main/java/subway/repository/SectionRepository.java
new file mode 100644
index 000000000..0b967961b
--- /dev/null
+++ b/src/main/java/subway/repository/SectionRepository.java
@@ -0,0 +1,24 @@
+package subway.repository;
+
+import subway.domain.RequiredResources;
+import subway.domain.Section;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SectionRepository {
+ private static Map sections = new HashMap<>();
+
+ public static Map sections() {
+ return Collections.unmodifiableMap(sections);
+ }
+
+ public static void addSection(Section section, RequiredResources requiredResources) {
+ sections.put(section, requiredResources);
+ }
+
+ public static void deleteAll() {
+ sections.clear();
+ }
+}
diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/repository/StationRepository.java
similarity index 91%
rename from src/main/java/subway/domain/StationRepository.java
rename to src/main/java/subway/repository/StationRepository.java
index 8ed9d103f..5fbadba4a 100644
--- a/src/main/java/subway/domain/StationRepository.java
+++ b/src/main/java/subway/repository/StationRepository.java
@@ -1,4 +1,6 @@
-package subway.domain;
+package subway.repository;
+
+import subway.domain.Station;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/main/java/subway/service/DistanceRouteService.java b/src/main/java/subway/service/DistanceRouteService.java
new file mode 100644
index 000000000..3a73e5619
--- /dev/null
+++ b/src/main/java/subway/service/DistanceRouteService.java
@@ -0,0 +1,34 @@
+package subway.service;
+
+import subway.domain.DistanceMap;
+import subway.domain.Station;
+
+import java.util.List;
+import java.util.Scanner;
+
+public class DistanceRouteService implements RouteService {
+ private static final DistanceRouteService distanceRouteService = new DistanceRouteService();
+
+ private DistanceRouteService() {
+ }
+
+ public static DistanceRouteService getInstance() {
+ return distanceRouteService;
+ }
+
+ @Override
+ public void routingService(Scanner scanner) {
+ try {
+ Station startStation = inputStartStations(scanner);
+ Station endStation = inputEndStation(scanner);
+ isSameName(startStation, endStation);
+ DistanceMap distanceMap = new DistanceMap();
+ distanceMap.addStationVertex();
+ distanceMap.addWeight();
+ List stationList = distanceMap.getShortestRoute(distanceMap.getGraph(), startStation, endStation);
+ calculateTotalDistanceAndTime(stationList);
+ } catch (IllegalArgumentException e) {
+ goToMenu(e, scanner);
+ }
+ }
+}
diff --git a/src/main/java/subway/service/RouteService.java b/src/main/java/subway/service/RouteService.java
new file mode 100644
index 000000000..f3e40cbac
--- /dev/null
+++ b/src/main/java/subway/service/RouteService.java
@@ -0,0 +1,73 @@
+package subway.service;
+
+import subway.controller.RouteController;
+import subway.domain.Section;
+import subway.domain.Station;
+import subway.repository.SectionRepository;
+import subway.repository.StationRepository;
+import subway.views.routeviews.RouteInputView;
+import subway.views.routeviews.RouteOutputView;
+
+import java.util.List;
+import java.util.Scanner;
+
+public interface RouteService {
+ String NOT_EXIST_STATION_ERROR = "\n[ERROR] 존재하지 않는 역입니다.\n";
+ String SAME_STATION_ERROR = "\n[ERROR] 같은 역은 입력할 수 없습니다.\n";
+
+ void routingService(Scanner scanner);
+
+ default Station inputStartStations(Scanner scanner) {
+ Station startStation = new Station(RouteInputView.inputStartStation(scanner));
+ isExistStation(startStation);
+ return startStation;
+ }
+
+ default Station inputEndStation(Scanner scanner) {
+ Station endStation = new Station(RouteInputView.inputEndStation(scanner));
+ isExistStation(endStation);
+ return endStation;
+ }
+
+ default void goToMenu(IllegalArgumentException e, Scanner scanner) {
+ System.out.println(e.getMessage());
+ RouteController routeController = RouteController.getInstance();
+ routeController.mappingMenu(scanner);
+ }
+
+ static void isExistStation(Station station) {
+ if (!StationRepository.stations().contains(station)) {
+ throw new IllegalArgumentException(NOT_EXIST_STATION_ERROR);
+ }
+ }
+
+ default void isSameName(Station startStation, Station endStation) {
+ if (startStation.getName().equals(endStation.getName())) {
+ throw new IllegalArgumentException(SAME_STATION_ERROR);
+ }
+ }
+
+ default void calculateTotalDistanceAndTime(List shortestRouteStationList) {
+ int totalDistance = calculateTotalDistance(shortestRouteStationList);
+ int totalTime = calculateTotalTime(shortestRouteStationList);
+ RouteOutputView.printRoutedMapWithResources(shortestRouteStationList, totalDistance, totalTime);
+ }
+
+ default int calculateTotalDistance(List shortestRouteStationList) {
+ int totalDistance = 0;
+ for (int i = 0; i < shortestRouteStationList.size()-1 ; i++) {
+ Section section = new Section(shortestRouteStationList.get(i), shortestRouteStationList.get(i + 1));
+ totalDistance += SectionRepository.sections().get(section).getDistanceResource().getDistance();
+ }
+ return totalDistance;
+ }
+
+ default int calculateTotalTime(List shortestRouteStationList) {
+ int totalTime = 0;
+ for (int i = 0; i < shortestRouteStationList.size()-1 ; i++) {
+ Section section = new Section(shortestRouteStationList.get(i), shortestRouteStationList.get(i + 1));
+ totalTime += SectionRepository.sections().get(section).getTimeResource().getTime();
+ }
+ return totalTime;
+ }
+}
diff --git a/src/main/java/subway/service/TimeRouteService.java b/src/main/java/subway/service/TimeRouteService.java
new file mode 100644
index 000000000..142d0e582
--- /dev/null
+++ b/src/main/java/subway/service/TimeRouteService.java
@@ -0,0 +1,34 @@
+package subway.service;
+
+import subway.domain.Station;
+import subway.domain.TimeMap;
+
+import java.util.List;
+import java.util.Scanner;
+
+public class TimeRouteService implements RouteService {
+ private static final TimeRouteService timeRouteService = new TimeRouteService();
+
+ private TimeRouteService() {
+ }
+
+ public static TimeRouteService getInstance() {
+ return timeRouteService;
+ }
+
+ @Override
+ public void routingService(Scanner scanner) {
+ try {
+ Station startStation = inputStartStations(scanner);
+ Station endStation = inputEndStation(scanner);
+ isSameName(startStation, endStation);
+ TimeMap timeMap = new TimeMap();
+ timeMap.addStationVertex();
+ timeMap.addWeight();
+ List stationList = timeMap.getShortestRoute(timeMap.getGraph(), startStation, endStation);
+ calculateTotalDistanceAndTime(stationList);
+ } catch (IllegalArgumentException e) {
+ goToMenu(e, scanner);
+ }
+ }
+}
diff --git a/src/main/java/subway/views/OutputConstant.java b/src/main/java/subway/views/OutputConstant.java
new file mode 100644
index 000000000..860da1a41
--- /dev/null
+++ b/src/main/java/subway/views/OutputConstant.java
@@ -0,0 +1,9 @@
+package subway.views;
+
+public class OutputConstant {
+ public static final String OPTION_SEPARATOR = ". ";
+ public static final String NOT_EXIST_OPTION_ERROR = "[ERROR] 존재하지 않는 옵션입니다.";
+
+ private OutputConstant() {
+ }
+}
diff --git a/src/main/java/subway/views/OutputView.java b/src/main/java/subway/views/OutputView.java
new file mode 100644
index 000000000..1bfdedda3
--- /dev/null
+++ b/src/main/java/subway/views/OutputView.java
@@ -0,0 +1,10 @@
+package subway.views;
+
+public interface OutputView {
+ String LINE_WRAP = "\n";
+ String SELECT_FEATURE_MESSAGE = "## 원하는 기능을 선택하세요.";
+
+ static void printSelectOptionMessage() {
+ System.out.println(LINE_WRAP + SELECT_FEATURE_MESSAGE);
+ }
+}
diff --git a/src/main/java/subway/views/mainviews/MainInputView.java b/src/main/java/subway/views/mainviews/MainInputView.java
new file mode 100644
index 000000000..d13353b40
--- /dev/null
+++ b/src/main/java/subway/views/mainviews/MainInputView.java
@@ -0,0 +1,15 @@
+package subway.views.mainviews;
+
+import subway.views.OutputView;
+
+import java.util.Scanner;
+
+public class MainInputView {
+ private MainInputView() {
+ }
+
+ public static String inputMainOption(Scanner scanner) {
+ OutputView.printSelectOptionMessage();
+ return scanner.nextLine();
+ }
+}
diff --git a/src/main/java/subway/views/mainviews/MainOutputView.java b/src/main/java/subway/views/mainviews/MainOutputView.java
new file mode 100644
index 000000000..c96975dec
--- /dev/null
+++ b/src/main/java/subway/views/mainviews/MainOutputView.java
@@ -0,0 +1,19 @@
+package subway.views.mainviews;
+
+import subway.menus.MainMenu;
+import subway.views.OutputView;
+
+import java.util.Arrays;
+
+public class MainOutputView implements OutputView {
+ private static final String MAIN_SCREEN_MESSAGE = "## 메인 화면";
+
+ private MainOutputView() {
+ }
+
+ public static void printMainMenu() {
+ System.out.println(MAIN_SCREEN_MESSAGE);
+ Arrays.stream(MainMenu.values())
+ .forEach(System.out::println);
+ }
+}
diff --git a/src/main/java/subway/views/routeviews/RouteInputView.java b/src/main/java/subway/views/routeviews/RouteInputView.java
new file mode 100644
index 000000000..a056ba892
--- /dev/null
+++ b/src/main/java/subway/views/routeviews/RouteInputView.java
@@ -0,0 +1,25 @@
+package subway.views.routeviews;
+
+import subway.views.OutputView;
+
+import java.util.Scanner;
+
+public class RouteInputView {
+ private RouteInputView() {
+ }
+
+ public static String inputRouteOption(Scanner scanner) {
+ OutputView.printSelectOptionMessage();
+ return scanner.nextLine();
+ }
+
+ public static String inputStartStation(Scanner scanner) {
+ RouteOutputView.printStartStationMessage();
+ return scanner.nextLine();
+ }
+
+ public static String inputEndStation(Scanner scanner) {
+ RouteOutputView.printEndStationMessage();
+ return scanner.nextLine();
+ }
+}
diff --git a/src/main/java/subway/views/routeviews/RouteOutputView.java b/src/main/java/subway/views/routeviews/RouteOutputView.java
new file mode 100644
index 000000000..b245c3629
--- /dev/null
+++ b/src/main/java/subway/views/routeviews/RouteOutputView.java
@@ -0,0 +1,54 @@
+package subway.views.routeviews;
+
+import subway.domain.Station;
+import subway.menus.RouteMenu;
+import subway.views.OutputView;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class RouteOutputView implements OutputView {
+ private static final String ROUTE_SCREEN_MESSAGE = "## 경로 기준";
+ private static final String INPUT_START_STATION_MESSAGE = "## 출발역을 입력하세요.";
+ private static final String INPUT_END_STATION_MESSAGE = "## 도착역을 입력하세요.";
+ private static final String ROUTE_RESULT_MESSAGE = "## 조회 결과";
+ private static final String INFORMATION = "[INFO] ";
+ private static final String SEPARATOR = "---";
+ private static final String TOTAL_DISTANCE_MESSAGE = "총 거리: ";
+ private static final String TOTAL_TIME_MESSAGE = "총 소요 시간: ";
+ private static final String DISTANCE_UNIT = "km";
+ private static final String TIME_UNIT = "분";
+
+ private RouteOutputView() {
+ }
+
+ public static void printRouteMenu() {
+ System.out.println(LINE_WRAP + ROUTE_SCREEN_MESSAGE);
+ Arrays.stream(RouteMenu.values())
+ .forEach(System.out::println);
+ }
+
+ public static void printStartStationMessage() {
+ System.out.println(LINE_WRAP + INPUT_START_STATION_MESSAGE);
+ }
+
+ public static void printEndStationMessage() {
+ System.out.println(LINE_WRAP + INPUT_END_STATION_MESSAGE);
+ }
+
+ public static void printRoutedMapWithResources(List stationList, int distance, int time) {
+ System.out.println(LINE_WRAP + ROUTE_RESULT_MESSAGE);
+ System.out.println(INFORMATION + SEPARATOR);
+ System.out.println(INFORMATION + TOTAL_DISTANCE_MESSAGE + distance + DISTANCE_UNIT);
+ System.out.println(INFORMATION + TOTAL_TIME_MESSAGE + time + TIME_UNIT);
+ System.out.println(INFORMATION + SEPARATOR);
+ printRoute(stationList);
+ System.out.println();
+ }
+
+ private static void printRoute(List stationList) {
+ stationList.stream()
+ .map(Station::getName)
+ .forEach(stationName -> System.out.println(INFORMATION + stationName));
+ }
+}