diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..2551edaa2
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,109 @@
+# 지하철 노선도 경로 조회 미션
+
+*작성자: 황진성(eddy5360@naver.com)*
+
+우아한 테크코스 3기 웹 백엔드 선발 과정 최종 코딩테스트입니다.
+
+
+
+## 📃 구현할 기능 목록
+
+
+
+* 초기 설정
+```
+- 노선, 역, 거리, 시간 정보들을 초기화한다.
+- 방향이 정해져있지 않으므로 경로를 양방향으로 설정한다.
+```
+
+* 경로 조회 기능
+```
+- 출발역과 도착역을 입력받는다.
+- 최단거리 또는 최소시간 기준으로 경로를 조회할 수 있다.
+[예외상황]
+ - 출발역과 도착역이 동일하다.
+ - 출발역에서 도착역으로 갈 수 없다(연결되어있지 않다.)
+```
+
+
+
+## ✍ 클래스 설계 목록
+
+
+
+## domain
+
+* Line
+```
+- 노선의 이름을 저장한다.
+```
+
+* LineRepository
+```
+- 노선들의 목록을 저장한다.
+```
+
+* Station
+```
+- 역의 이름을 저장한다.
+```
+
+* StationRepository
+```
+- 역들의 목록을 저장한다.
+```
+
+* Connection
+```
+- 연결된 역 사이의 시간과 거리를 저장한다.
+```
+
+* ConnectionRepository
+```
+- 모든 Connection 객체들을 일급 컬렉션 형태로 저장한다.
+```
+
+
+## view
+
+* InputView
+```
+- 기능 선택
+- 출발, 도착 역 입력
+```
+
+* OutputView
+```
+- 메인 화면 출력
+ - 1. 경로 조회
+ - Q. 종료
+- 경로 조회 화면 출력
+ - 1. 최단거리
+ - 2. 최소시간
+ - B. 돌아가기
+- 조회 결과 출력
+```
+
+
+## controller
+
+* MainController
+```
+- 메인 화면을 출력하고, 입력받은 기능 값에 의존해 다음 기능을 수행한다.
+```
+
+* InquirePathController
+```
+- 경로 기준 화면을 출력하고, 입력받은 기능 값에 의존해 경로를 조회한다.
+```
+
+
+## util
+
+* Validator
+```
+- 현재 화면에서 선택할 수 있는 기능인지 검증한다.
+- 출발역과 도착역이 같은지 검증한다.
+- 존재하는 역을 입력했는지 검증한다.
+- 서로 연결된 역인지 검증한다.
+```
\ No newline at end of file
diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java
index 0bcf786cc..7494ec7d2 100644
--- a/src/main/java/subway/Application.java
+++ b/src/main/java/subway/Application.java
@@ -1,10 +1,19 @@
package subway;
+import subway.controller.MainController;
+import subway.util.Initialization;
+import subway.view.InputView;
+
import java.util.Scanner;
public class Application {
public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
- // TODO: 프로그램 구현
+ Initialization.set();
+ InputView inputView = new InputView(scanner);
+ MainController mainController = new MainController(inputView);
+ mainController.run();
+
+ scanner.close();
}
}
diff --git a/src/main/java/subway/controller/MainButton.java b/src/main/java/subway/controller/MainButton.java
new file mode 100644
index 000000000..be981d38e
--- /dev/null
+++ b/src/main/java/subway/controller/MainButton.java
@@ -0,0 +1,15 @@
+package subway.controller;
+
+public enum MainButton {
+ INQUIRY("1"), EXIT("Q");
+
+ private String symbol;
+
+ MainButton(String symbol) {
+ this.symbol = symbol;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+}
diff --git a/src/main/java/subway/controller/MainController.java b/src/main/java/subway/controller/MainController.java
new file mode 100644
index 000000000..78aa09e07
--- /dev/null
+++ b/src/main/java/subway/controller/MainController.java
@@ -0,0 +1,37 @@
+package subway.controller;
+
+import subway.view.InputView;
+import subway.view.OutputView;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class MainController {
+
+ private final InputView inputView;
+ private final PathController pathController;
+
+ public MainController(InputView inputView) {
+ this.inputView = inputView;
+ pathController = new PathController(inputView);
+ }
+
+ private final List buttons = Arrays.asList(
+ MainButton.INQUIRY.getSymbol(),
+ MainButton.EXIT.getSymbol()
+ );
+
+ public void run() {
+ OutputView.printMain();
+ String selectedButton = inputView.getFunctionSelect(buttons);
+ nextProcedure(selectedButton);
+ }
+
+ private void nextProcedure(final String button) {
+ if (button.equals(MainButton.INQUIRY.getSymbol())) {
+ pathController.run();
+ run();
+ }
+ }
+
+}
diff --git a/src/main/java/subway/controller/PathButton.java b/src/main/java/subway/controller/PathButton.java
new file mode 100644
index 000000000..c808de410
--- /dev/null
+++ b/src/main/java/subway/controller/PathButton.java
@@ -0,0 +1,15 @@
+package subway.controller;
+
+public enum PathButton {
+ SHORTEST_PATH("1"), LEAST_TIME("2"), BACK("B");
+
+ private String symbol;
+
+ PathButton(String symbol) {
+ this.symbol = symbol;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+}
diff --git a/src/main/java/subway/controller/PathController.java b/src/main/java/subway/controller/PathController.java
new file mode 100644
index 000000000..4b9887f11
--- /dev/null
+++ b/src/main/java/subway/controller/PathController.java
@@ -0,0 +1,54 @@
+package subway.controller;
+
+import subway.graph.DistanceWeightedGraph;
+import subway.graph.TimeWeightedGraph;
+import subway.util.Validator;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class PathController {
+
+ private final InputView inputView;
+
+ public PathController(InputView inputView) {
+ this.inputView = inputView;
+ }
+
+ private final List buttons = Arrays.asList(
+ PathButton.SHORTEST_PATH.getSymbol(),
+ PathButton.LEAST_TIME.getSymbol(),
+ PathButton.BACK.getSymbol()
+ );
+
+ public void run() {
+ OutputView.printInquiry();
+ String selectedButton = inputView.getFunctionSelect(buttons);
+ nextProcedure(selectedButton);
+ }
+
+ private void nextProcedure(final String button) {
+ if (button.equals(PathButton.BACK.getSymbol())) {
+ return;
+ }
+ String source = inputView.getSourceStation();
+ String destination = inputView.getDestinationStation();
+ if (Validator.sameStation(source, destination)) {
+ nextProcedure(button);
+ return;
+ }
+ if (Validator.unconnected(source, destination)) {
+ nextProcedure(button);
+ return;
+ }
+ // 총 시간과 거리를 계산하는 함수를 구현하지 못했습니다.
+ if (button.equals(PathButton.SHORTEST_PATH.getSymbol())) {
+ OutputView.printInquiryGraph(DistanceWeightedGraph.getOptimalGraph(source, destination), -1, -1);
+ } else if (button.equals(PathButton.LEAST_TIME.getSymbol())) {
+ OutputView.printInquiryGraph(TimeWeightedGraph.getOptimalGraph(source, destination), -1, -1);
+ }
+ }
+
+}
diff --git a/src/main/java/subway/domain/Connection.java b/src/main/java/subway/domain/Connection.java
new file mode 100644
index 000000000..c15c461c2
--- /dev/null
+++ b/src/main/java/subway/domain/Connection.java
@@ -0,0 +1,36 @@
+package subway.domain;
+
+public class Connection {
+
+ private final String source;
+ private final String destination;
+ private final int distance; // (km) 단위
+ private final int time; // (분) 단위
+
+ public Connection(final String source, final String destination, final int distance, final int time) {
+ this.source = source;
+ this.destination = destination;
+ this.distance = distance;
+ this.time = time;
+ }
+
+ public Connection getReverse() {
+ return new Connection(destination, source, distance, time);
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public String getDestination() {
+ return destination;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ public int getTime() {
+ return time;
+ }
+}
diff --git a/src/main/java/subway/domain/ConnectionRepository.java b/src/main/java/subway/domain/ConnectionRepository.java
new file mode 100644
index 000000000..cbe065d9c
--- /dev/null
+++ b/src/main/java/subway/domain/ConnectionRepository.java
@@ -0,0 +1,18 @@
+package subway.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConnectionRepository {
+
+ private static List connections = new ArrayList<>();
+
+ public static void addConnection(Connection connection) {
+ connections.add(connection);
+ connections.add(connection.getReverse());
+ }
+
+ public static List connections() {
+ return connections;
+ }
+}
diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java
index 8ed9d103f..a6dd579dd 100644
--- a/src/main/java/subway/domain/StationRepository.java
+++ b/src/main/java/subway/domain/StationRepository.java
@@ -23,4 +23,14 @@ public static boolean deleteStation(String name) {
public static void deleteAll() {
stations.clear();
}
+
+ public static boolean isExist(String stationName) {
+ for (Station station : stations) {
+ if (station.getName().equals(stationName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
diff --git a/src/main/java/subway/graph/DistanceWeightedGraph.java b/src/main/java/subway/graph/DistanceWeightedGraph.java
new file mode 100644
index 000000000..23f471b89
--- /dev/null
+++ b/src/main/java/subway/graph/DistanceWeightedGraph.java
@@ -0,0 +1,31 @@
+package subway.graph;
+
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.WeightedMultigraph;
+
+import java.util.List;
+
+public class DistanceWeightedGraph {
+
+ private static final WeightedMultigraph graph = new WeightedMultigraph(DefaultWeightedEdge.class);
+
+ public static void addVertex(String vertex) {
+ graph.addVertex(vertex);
+ }
+
+ public static DefaultWeightedEdge addEdge(String source, String destination) {
+ return graph.addEdge(source, destination);
+ }
+
+ public static void setEdgeWeight(DefaultWeightedEdge DWE, double weight) {
+ graph.setEdgeWeight(DWE, weight);
+ }
+
+ public static List getOptimalGraph(String source, String destination) {
+ DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(graph);
+ List optimalGraph = dijkstraShortestPath.getPath(source, destination).getVertexList();
+ return optimalGraph;
+ }
+
+}
diff --git a/src/main/java/subway/graph/TimeWeightedGraph.java b/src/main/java/subway/graph/TimeWeightedGraph.java
new file mode 100644
index 000000000..78ffd9d31
--- /dev/null
+++ b/src/main/java/subway/graph/TimeWeightedGraph.java
@@ -0,0 +1,31 @@
+package subway.graph;
+
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.WeightedMultigraph;
+
+import java.util.List;
+
+public class TimeWeightedGraph {
+
+ private static final WeightedMultigraph graph = new WeightedMultigraph(DefaultWeightedEdge.class);
+
+ public static void addVertex(String vertex) {
+ graph.addVertex(vertex);
+ }
+
+ public static DefaultWeightedEdge addEdge(String source, String destination) {
+ return graph.addEdge(source, destination);
+ }
+
+ public static void setEdgeWeight(DefaultWeightedEdge DWE, double weight) {
+ graph.setEdgeWeight(DWE, weight);
+ }
+
+ public static List getOptimalGraph(String source, String destination) {
+ DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(graph);
+ List optimalGraph = dijkstraShortestPath.getPath(source, destination).getVertexList();
+ return optimalGraph;
+ }
+
+}
diff --git a/src/main/java/subway/legacy/Lines.java b/src/main/java/subway/legacy/Lines.java
new file mode 100644
index 000000000..71bb4a687
--- /dev/null
+++ b/src/main/java/subway/legacy/Lines.java
@@ -0,0 +1,17 @@
+package subway.legacy;
+
+public enum Lines {
+
+ LINE_2("2호선"),
+ LINE_3("3호선"),
+ LINE_SHINBUNDANG("신분당선");
+
+ private String name;
+ Lines(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/subway/legacy/Stations.java b/src/main/java/subway/legacy/Stations.java
new file mode 100644
index 000000000..83002f195
--- /dev/null
+++ b/src/main/java/subway/legacy/Stations.java
@@ -0,0 +1,22 @@
+package subway.legacy;
+
+public enum Stations {
+
+ GYODAE("교대역"),
+ GANGNAM("강남역"),
+ YEOKSAM("역삼역"),
+ NAMBU_BUS_TERMINAL("남부터미널역"),
+ YANGJAE("양재역"),
+ YANGJAE_CITIZEN_FOREST("양재시민의숲역"),
+ MAEBONG("매봉역");
+
+ private String name;
+ Stations(String name){
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+}
diff --git a/src/main/java/subway/util/Initialization.java b/src/main/java/subway/util/Initialization.java
new file mode 100644
index 000000000..4139b8e3a
--- /dev/null
+++ b/src/main/java/subway/util/Initialization.java
@@ -0,0 +1,55 @@
+package subway.util;
+
+import subway.graph.DistanceWeightedGraph;
+import subway.graph.TimeWeightedGraph;
+import subway.legacy.Lines;
+import subway.legacy.Stations;
+import subway.domain.*;
+
+public class Initialization {
+
+ public static void set() {
+ registerStations();
+ registerLines();
+ registerConnections();
+ setGraph();
+ }
+
+ private static void registerStations() {
+ StationRepository.addStation(new Station(Stations.GYODAE.getName()));
+ StationRepository.addStation(new Station(Stations.GANGNAM.getName()));
+ StationRepository.addStation(new Station(Stations.YEOKSAM.getName()));
+ StationRepository.addStation(new Station(Stations.NAMBU_BUS_TERMINAL.getName()));
+ StationRepository.addStation(new Station(Stations.YANGJAE.getName()));
+ StationRepository.addStation(new Station(Stations.YANGJAE_CITIZEN_FOREST.getName()));
+ StationRepository.addStation(new Station(Stations.MAEBONG.getName()));
+ }
+
+ private static void registerLines() {
+ LineRepository.addLine(new Line(Lines.LINE_2.getName()));
+ LineRepository.addLine(new Line(Lines.LINE_3.getName()));
+ LineRepository.addLine(new Line(Lines.LINE_SHINBUNDANG.getName()));
+ }
+
+ private static void registerConnections() {
+ ConnectionRepository.addConnection(new Connection(Stations.GYODAE.getName(), Stations.GANGNAM.getName(), 2, 3));
+ ConnectionRepository.addConnection(new Connection(Stations.GANGNAM.getName(), Stations.YEOKSAM.getName(), 2, 3));
+ ConnectionRepository.addConnection(new Connection(Stations.GYODAE.getName(), Stations.NAMBU_BUS_TERMINAL.getName(), 3,2));
+ ConnectionRepository.addConnection(new Connection(Stations.NAMBU_BUS_TERMINAL.getName(), Stations.YANGJAE.getName(), 6,5));
+ ConnectionRepository.addConnection(new Connection(Stations.YANGJAE.getName(), Stations.MAEBONG.getName(), 1,1));
+ ConnectionRepository.addConnection(new Connection(Stations.GANGNAM.getName(), Stations.YANGJAE.getName(), 2,8));
+ ConnectionRepository.addConnection(new Connection(Stations.YANGJAE.getName(), Stations.YANGJAE_CITIZEN_FOREST.getName(), 10,3));
+ }
+
+ private static void setGraph() {
+ for (Station station : StationRepository.stations()) {
+ DistanceWeightedGraph.addVertex(station.getName());
+ TimeWeightedGraph.addVertex(station.getName());
+ }
+ for (Connection connection : ConnectionRepository.connections()) {
+ DistanceWeightedGraph.setEdgeWeight(DistanceWeightedGraph.addEdge(connection.getSource(), connection.getDestination()), connection.getDistance());
+ TimeWeightedGraph.setEdgeWeight(TimeWeightedGraph.addEdge(connection.getSource(), connection.getDestination()), connection.getTime());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/subway/util/Validator.java b/src/main/java/subway/util/Validator.java
new file mode 100644
index 000000000..f92c52ba8
--- /dev/null
+++ b/src/main/java/subway/util/Validator.java
@@ -0,0 +1,37 @@
+package subway.util;
+
+import subway.domain.StationRepository;
+import subway.view.OutputView;
+
+import java.util.List;
+
+public class Validator {
+
+ public static void functionSelect(final List choices, final String command) {
+ if (!choices.contains(command)) {
+ OutputView.printError(OutputView.ERROR_INVALID_SELECT);
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static void existStation(final String stationName) {
+ if (!StationRepository.isExist(stationName)){
+ OutputView.printError(OutputView.ERROR_INVALID_STATION);
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static boolean sameStation(final String source, final String destination) {
+ if (source.equals(destination)) {
+ OutputView.printError(OutputView.ERROR_SAME_STATION);
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean unconnected(final String source, final String destination) {
+ // 구현 못했습니다.
+ return false;
+ }
+
+}
diff --git a/src/main/java/subway/view/InputView.java b/src/main/java/subway/view/InputView.java
new file mode 100644
index 000000000..343d04a18
--- /dev/null
+++ b/src/main/java/subway/view/InputView.java
@@ -0,0 +1,51 @@
+package subway.view;
+
+import subway.util.Validator;
+
+import java.util.List;
+import java.util.Scanner;
+
+public class InputView {
+
+ private final Scanner scanner;
+
+ public InputView(Scanner scanner) {
+ this.scanner = scanner;
+ }
+
+ public String getFunctionSelect(List buttons) {
+ OutputView.printFunctionSelectQuery();
+ try {
+ String input = scanner.nextLine();
+ System.out.println();
+ Validator.functionSelect(buttons, input);
+ return input;
+ } catch (IllegalArgumentException IAE) {
+ return getFunctionSelect(buttons);
+ }
+ }
+
+ public String getSourceStation() {
+ OutputView.printSourceStationQuery();
+ try {
+ String input = scanner.nextLine();
+ OutputView.printEmptyLine();
+ Validator.existStation(input);
+ return input;
+ } catch (IllegalArgumentException IAE) {
+ return getSourceStation();
+ }
+ }
+
+ public String getDestinationStation() {
+ OutputView.printDestinationStationQuery();
+ try {
+ String input = scanner.nextLine();
+ OutputView.printEmptyLine();
+ Validator.existStation(input);
+ return input;
+ } catch (IllegalArgumentException IAE) {
+ return getDestinationStation();
+ }
+ }
+}
diff --git a/src/main/java/subway/view/OutputView.java b/src/main/java/subway/view/OutputView.java
new file mode 100644
index 000000000..0ec91a0c7
--- /dev/null
+++ b/src/main/java/subway/view/OutputView.java
@@ -0,0 +1,83 @@
+package subway.view;
+
+import java.util.List;
+
+public class OutputView {
+
+ private static final String MAIN_TITLE = "## 메인 화면";
+ private static final String MAIN_OPTION_INQUIRY = "1. 경로 조회";
+ private static final String MAIN_OPTION_EXIT = "Q. 종료";
+
+ private static final String INQUIRY_TITLE = "## 경로 기준";
+ private static final String INQUIRY_SHORTEST_PATH = "1. 최단 거리";
+ private static final String INQUIRY_LEAST_TIME = "2. 최소 시간";
+ private static final String INQUIRY_BACK = "B. 돌아가기";
+
+ private static final String RESULT_TITLE = "## 조회 결과";
+ private static final String RESULT_LINE = "---";
+ private static final String RESULT_DISTANCE_FORMAT = "총 거리: %dkm";
+ private static final String RESULT_TIME_FORMAT = "총 소요 시간: %d분";
+
+ private static final String QUERY_FUNCTION_SELECT = "## 원하는 기능을 선택하세요.";
+ private static final String QUERY_SOURCE_STATION = "## 출발역을 입력하세요.";
+ private static final String QUERY_DESTINATION_STATION = "## 도착역을 입력하세요.";
+
+ private static final String TRY_AGAIN = " 다시 입력해주세요.";
+ public static final String ERROR_INVALID_SELECT = "선택할 수 없는 기능입니다." + TRY_AGAIN;
+ public static final String ERROR_INVALID_STATION = "존재하지 않는 역입니다." + TRY_AGAIN;
+ public static final String ERROR_SAME_STATION = "출발역과 도착역이 동일합니다." + TRY_AGAIN;
+ public static final String ERROR_UNREACHABLE = "죄송합니다. 두 역은 연결되어 있지 않습니다.";
+
+ public static void printMain() {
+ System.out.println(MAIN_TITLE);
+ System.out.println(MAIN_OPTION_INQUIRY);
+ System.out.println(MAIN_OPTION_EXIT);
+ printEmptyLine();
+ }
+
+ public static void printInquiry() {
+ System.out.println(INQUIRY_TITLE);
+ System.out.println(INQUIRY_SHORTEST_PATH);
+ System.out.println(INQUIRY_LEAST_TIME);
+ System.out.println(INQUIRY_BACK);
+ printEmptyLine();
+ }
+
+ public static void printFunctionSelectQuery() {
+ System.out.println(QUERY_FUNCTION_SELECT);
+ }
+
+ public static void printSourceStationQuery() {
+ System.out.println(QUERY_SOURCE_STATION);
+ }
+
+ public static void printDestinationStationQuery() {
+ System.out.println(QUERY_DESTINATION_STATION);
+ }
+
+ public static void printInquiryGraph(final List graph, final int totalDistance, final int totalTime) {
+ System.out.println(RESULT_TITLE);
+ printInformation(RESULT_LINE);
+ printInformation(String.format(RESULT_DISTANCE_FORMAT, totalDistance));
+ printInformation(String.format(RESULT_TIME_FORMAT, totalTime));
+ printInformation(RESULT_LINE);
+ for (String stationName : graph) {
+ printInformation(stationName);
+ }
+ printEmptyLine();
+ }
+
+ public static void printInformation(final String message) {
+ System.out.println("[INFO] " + message);
+ }
+
+ public static void printError(final String message) {
+ System.out.println("[ERROR] " + message);
+ printEmptyLine();
+ }
+
+ public static void printEmptyLine() {
+ System.out.println();
+ }
+
+}