diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..ca51f58a5 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,25 @@ +# 지하철 노선도 경로 조회 미션 +: 두 지하철 역 사이의 최단 경로 / 최단 시간의 경로를 조회한다. + +본 프로젝트는 우아한 테크코스 3기 프리코스 백엔드 과정의 2차 코딩테스트 미션을 구현한 것입니다. + +## 구현 기능 목록 +#### 기능 선택 +- [X] 화면을 출력한다. +- [X] 사용자가 원하는 기능을 기호로 입력받는다. + - [X] 사용자가 입력한 기호가 기능목록에 존재하지 않는 경우 에러를 출력한다. +- [X] 종료(Q)를 선택하면 프로그램을 종료한다. +- [X] 돌아가기(B)를 선택하면 메인 화면으로 돌아간다. + +#### 경로 조회 +- [X] 초기 노선 정보로 초기화한다. +- [X] 출발역과 도착역을 입력한다. +- [X] 출발역과 도착역 입력이 유효한지 검사한다. + - [X] 출발역과 도착역이 기존에 존재하는 역인지 확인한다. + - [X] 출발역과 도착역이 서로 다른지 확인한다. +- [ ] 두 역 사이의 (기준에 따른) 경로를 계산한다. + - [ ] 두 역이 연결되어 있지 않은 경우 에러를 출력한다. +- [ ] 조회 결과를 출력한다. + - [ ] 이동 거리를 출력한다. + - [ ] 소요 시간을 출력한다. + - [ ] 경로를 출력한다. diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java index 0bcf786cc..19dd1b63b 100644 --- a/src/main/java/subway/Application.java +++ b/src/main/java/subway/Application.java @@ -1,10 +1,29 @@ package subway; +import subway.controller.Initializer; +import subway.controller.MenuScanner; +import subway.controller.Navigator; +import subway.domain.Menus; +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: 프로그램 구현 + InputView inputView = new InputView(scanner); + run(inputView); + } + + private static void run(InputView inputView) { + Navigator navigator = Initializer.set(); + MenuScanner menuScanner = new MenuScanner(); + String selectedMenus = menuScanner.scanMenus(inputView); + boolean quit = Menus.isQuit(selectedMenus); + while (!quit) { + Menus.run(inputView, selectedMenus, navigator); + selectedMenus = menuScanner.scanMenus(inputView); + quit = Menus.isQuit(selectedMenus); + } } } diff --git a/src/main/java/subway/controller/Initializer.java b/src/main/java/subway/controller/Initializer.java new file mode 100644 index 000000000..511c2e682 --- /dev/null +++ b/src/main/java/subway/controller/Initializer.java @@ -0,0 +1,110 @@ +package subway.controller; + +import subway.controller.Navigator; +import subway.domain.Edge; +import subway.domain.Station; +import subway.domain.StationRepository; +import subway.domain.Line; +import subway.domain.LineRepository; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class Initializer { + private static final int NEXT_CONSTANT = 1; + private static final int ORDER_CONSTANT = 1; + + private static final String station1 = "교대역"; + private static final String station2 = "강남역"; + private static final String station3 = "역삼역"; + private static final String station4 = "남부터미널역"; + private static final String station5 = "양재역"; + private static final String station6 = "양재시민의숲역"; + private static final String station7 = "매봉역"; + + private static final String line1 = "2호선"; + private static final String line2 = "3호선"; + private static final String line3 = "신분당선"; + + private static final List initStations = Arrays + .asList(station1, station2, station3, station4, station5, station6, station7); + private static final List line1Stations = Arrays.asList(station1, station2, station3); + private static final List line2Stations = Arrays.asList(station1, station4, station5, station7); + private static final List line3Stations = Arrays.asList(station2, station5, station6); + + private static final HashMap> initLines = new HashMap>() {{ + put(line1, line1Stations); + put(line2, line2Stations); + put(line3, line3Stations); + }}; + + private Initializer() { + } + + public static Navigator set() { + Navigator navigator = new Navigator(); + HashMap stations = makeStations(navigator); + List lineNameList = new ArrayList(initLines.keySet()); + for (int i = 0; i < lineNameList.size(); i++) { + String lineName = lineNameList.get(i); + makeLine(lineName, navigator); + } + return navigator; + } + + private static HashMap makeStations(Navigator navigator) { + HashMap stations = new HashMap(); + for (int i = 0; i < initStations.size(); i++) { + String stationName = initStations.get(i); + Station station = new Station(stationName); + StationRepository.addStation(station); + stations.put(stationName, station); + navigator.addStation(stationName); + } + return stations; + } + + private static void makeLine(String lineName, Navigator navigator) { + Line line = new Line(lineName); + List stationList = initLines.get(lineName); + for (int i = 0; i < stationList.size(); i++) { + String stationName = stationList.get(i); + int order = i + ORDER_CONSTANT; + line.addStationByName(stationName, order); + makeEdgeInLine(line, navigator); + } + LineRepository.addLine(line); + } + + private static void makeEdgeInLine(Line line, Navigator navigator) { + List edgeList = makeEdge(line); + List stationNameList = line.getStationNameList(); + for (int i = 0; i < edgeList.size(); i++) { + Edge edge = edgeList.get(i); + //line.addEdge(edge,i); + //navigator.addEdge(stationNameList.get(i), stationNameList.get(i+NEXT_CONSTANT),edge); + } + } + + //자료형 사용해보자, 수정 필요 + private static List makeEdge(Line line) { + String lineName = line.getName(); + List edgeList = new ArrayList(); + if (lineName.equals(line1)) { + edgeList.add(new Edge(2,3)); + edgeList.add(new Edge(2,3)); + } + if (lineName.equals(line2)) { + edgeList.add(new Edge(3,2)); + edgeList.add(new Edge(6,5)); + edgeList.add(new Edge(1,1)); + } + if (lineName.equals(line3)) { + edgeList.add(new Edge(2,8)); + edgeList.add(new Edge(10,3)); + } + return edgeList; + } +} \ No newline at end of file diff --git a/src/main/java/subway/controller/MenuScanner.java b/src/main/java/subway/controller/MenuScanner.java new file mode 100644 index 000000000..c1198db5b --- /dev/null +++ b/src/main/java/subway/controller/MenuScanner.java @@ -0,0 +1,44 @@ +package subway.controller; + +import subway.domain.Criterions; +import subway.domain.Functions; +import subway.domain.Menus; +import subway.view.InputView; +import subway.view.OutputView; + +public class MenuScanner { + + private static final Functions functions = new Functions(); + private static final Criterions criterions = new Criterions(); + + public MenuScanner() { + } + + public String scanMenus(InputView inputView) { + String selectedFunction = scanFunctions(inputView); + boolean quit = Menus.isQuit(selectedFunction); + if (quit) { + return selectedFunction; + } + String selectedCriterion = scanCriterions(inputView); + return selectedCriterion; + } + + public String scanFunctions(InputView inputView) { + OutputView.printMainScreen(); + String selectedFunction; + do { + selectedFunction = inputView.scanMenu(); + } while(!functions.isValid(selectedFunction)); + return selectedFunction; + } + + public String scanCriterions(InputView inputView) { + OutputView.printSubScreen(); + String selectedCriterion; + do { + selectedCriterion = inputView.scanMenu(); + } while(!criterions.isValid(selectedCriterion)); + return selectedCriterion; + } +} diff --git a/src/main/java/subway/controller/Navigator.java b/src/main/java/subway/controller/Navigator.java new file mode 100644 index 000000000..8d50815b8 --- /dev/null +++ b/src/main/java/subway/controller/Navigator.java @@ -0,0 +1,36 @@ +package subway.controller; + +import subway.domain.Edge; + +import java.util.List; +import org.jgrapht.graph.WeightedMultigraph; +import org.jgrapht.graph.DefaultWeightedEdge; + +public class Navigator { + + public WeightedMultigraph distanceGraph; + public WeightedMultigraph timeGraph; + + public Navigator() { + this.distanceGraph = new WeightedMultigraph(DefaultWeightedEdge.class); + this.timeGraph = new WeightedMultigraph(DefaultWeightedEdge.class); + } + + public void addStation(String stationName) { + distanceGraph.addVertex(stationName); + timeGraph.addVertex(stationName); + } + + public void addEdge(String startStation, String endStation, Edge edge) { + distanceGraph.setEdgeWeight(distanceGraph.addEdge(startStation, endStation), edge.distance); + timeGraph.setEdgeWeight(timeGraph.addEdge(startStation, endStation), edge.time); + } + + public List searchShortestDistance(List stations) { + return stations; + } + + public List searchShortestTime(List stations) { + return stations; + } +} diff --git a/src/main/java/subway/domain/Criterions.java b/src/main/java/subway/domain/Criterions.java new file mode 100644 index 000000000..7ac5887d6 --- /dev/null +++ b/src/main/java/subway/domain/Criterions.java @@ -0,0 +1,42 @@ +package subway.domain; + +import java.util.Arrays; +import java.util.List; + +public class Criterions extends Menus { + + private static final String distanceSign = "1"; + private static final String timeSign = "2"; + private static final String backSign = "B"; + private static final List signs = Arrays + .asList(new String[]{distanceSign, timeSign, backSign}); + + public Criterions() { + } + + @Override + public List getSigns() { + return signs; + } + + public static boolean isBack(String sign) { + if (sign.equals(backSign)) { + return true; + } + return false; + } + + public boolean isDistanceSign(String sign) { + if (sign.equals(distanceSign)) { + return true; + } + return false; + } + + public boolean isTimeSign(String sign) { + if (sign.equals(timeSign)) { + return true; + } + return false; + } +} diff --git a/src/main/java/subway/domain/Edge.java b/src/main/java/subway/domain/Edge.java new file mode 100644 index 000000000..5da628161 --- /dev/null +++ b/src/main/java/subway/domain/Edge.java @@ -0,0 +1,14 @@ +package subway.domain; + +public class Edge { + + public int distance; + public int time; + public final String distanceUnit = "km"; + public final String timeUnit = "분"; + + public Edge(int distance, int time) { + this.distance = distance; + this.time = time; + } +} diff --git a/src/main/java/subway/domain/Functions.java b/src/main/java/subway/domain/Functions.java new file mode 100644 index 000000000..50c517a01 --- /dev/null +++ b/src/main/java/subway/domain/Functions.java @@ -0,0 +1,27 @@ +package subway.domain; + +import java.util.Arrays; +import java.util.List; + +public class Functions extends Menus { + + private static final String searchSign = "1"; + private static final String quitSign = "Q"; + private static List signs = Arrays + .asList(new String[]{searchSign, quitSign}); + + public Functions() { + } + + @Override + public List getSigns() { + return signs; + } + + public static boolean isQuit(String menu) { + if (menu.equals(quitSign)) { + return true; + } + return false; + } +} diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java index f4d738d5a..417a988a5 100644 --- a/src/main/java/subway/domain/Line.java +++ b/src/main/java/subway/domain/Line.java @@ -1,7 +1,14 @@ package subway.domain; +import java.util.ArrayList; +import java.util.List; + public class Line { + private static final int ORDER_CONSTANT = 1; + private String name; + private List stations = new ArrayList<>(); + private List edges = new ArrayList<>(); public Line(String name) { this.name = name; @@ -11,5 +18,29 @@ public String getName() { return name; } - // 추가 기능 구현 + private void addStation(Station station, int order) { + int indexOrder = order - ORDER_CONSTANT; + stations.add(indexOrder,station); + } + + public void addEdge(Edge edge, int order) { + edges.add(order,edge); + } + + public void addStationByName(String stationName, int order) { + for (int i = 0; i < StationRepository.stations().size(); i++) { + Station station = StationRepository.stations().get(i); + if (station.getName().equals(stationName)) { + addStation(station, order); + } + } + } + + public List getStationNameList() { + List stationList = new ArrayList(); + for (int i = 0; i < stations.size(); i++) { + stationList.add(stations.get(i).getName()); + } + return stationList; + } } diff --git a/src/main/java/subway/domain/Menus.java b/src/main/java/subway/domain/Menus.java new file mode 100644 index 000000000..46400cc79 --- /dev/null +++ b/src/main/java/subway/domain/Menus.java @@ -0,0 +1,102 @@ +package subway.domain; + +import subway.controller.Navigator; +import subway.domain.exception.NonExistentMenuException; +import subway.domain.exception.NonExistentNameException; +import subway.domain.exception.SamePointsException; +import subway.view.InputView; + +import java.util.Arrays; +import java.util.List; + +public abstract class Menus { + + public static final Functions functions = new Functions(); + public static final Criterions criterions = new Criterions(); + private static final List signs = Arrays.asList(new String[]{}); + + public static void run(InputView inputView, String selectedCriterions, Navigator navigator) { + if (isBack(selectedCriterions)) { + return; + } + search(inputView, selectedCriterions, navigator); + } + + public static void search(InputView inputView, String criterion, Navigator navigator) { + List stations = scanValidStations(inputView); + if (criterions.isDistanceSign(criterion)) { + navigator.searchShortestDistance(stations); + } + if (criterions.isTimeSign(criterion)) { + navigator.searchShortestTime(stations); + } + } + + private static List scanValidStations(InputView inputView) { + String start; + String end; + boolean validation = false; + do { + start = inputView.scanStartStation(); + end = inputView.scanEndStation(); + validation = isValidStations(start, end); + } while (!validation); + return Arrays.asList(start, end); + } + + private static boolean isValidStations(String start, String end) { + try { + checkValidationOfStations(start, end); + return true; + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + return false; + } + } + + private static void checkValidationOfStations(String start, String end) { + if (Station.isSameStation(start,end)) { + throw new SamePointsException(); + } + if (!StationRepository.isExistStationName(start)) { + throw new NonExistentNameException(); + } + if (!StationRepository.isExistStationName(end)) { + throw new NonExistentNameException(); + } + } + + public static boolean isQuit(String sign) { + return functions.isQuit(sign); + } + + public static boolean isBack(String sign) { + return criterions.isBack(sign); + } + + public boolean isValid(String sign) { + try { + checkValidationOf(sign); + return true; + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + return false; + } + } + private void checkValidationOf(String sign) { + if (!isInSignList(sign)) { + throw new NonExistentMenuException(); + } + } + + private boolean isInSignList(String sign) { + if (getSigns().contains(sign)) { + return true; + } + return false; + } + + public List getSigns() { + return signs; + } +} diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java index bdb142590..31fa50f0c 100644 --- a/src/main/java/subway/domain/Station.java +++ b/src/main/java/subway/domain/Station.java @@ -11,5 +11,10 @@ public String getName() { return name; } - // 추가 기능 구현 + public static boolean isSameStation(String start, String end) { + if (start.equals(end)) { + return true; + } + return false; + } } diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java index 8ed9d103f..9aae9cdbc 100644 --- a/src/main/java/subway/domain/StationRepository.java +++ b/src/main/java/subway/domain/StationRepository.java @@ -23,4 +23,13 @@ public static boolean deleteStation(String name) { public static void deleteAll() { stations.clear(); } + + public static boolean isExistStationName(String name) { + for (int i = 0; i < stations.size(); i++) { + if (stations.get(i).getName().equals(name)) { + return true; + } + } + return false; + } } diff --git a/src/main/java/subway/domain/exception/NonExistentMenuException.java b/src/main/java/subway/domain/exception/NonExistentMenuException.java new file mode 100644 index 000000000..4cf03bb94 --- /dev/null +++ b/src/main/java/subway/domain/exception/NonExistentMenuException.java @@ -0,0 +1,10 @@ +package subway.domain.exception; + +public class NonExistentMenuException extends RuntimeException { + + private static final String NON_EXISTENT_MENU_ERROR_MESSAGE = "\n[ERROR] 선택할 수 없는 기능입니다."; + + public NonExistentMenuException() { + super(NON_EXISTENT_MENU_ERROR_MESSAGE); + } +} diff --git a/src/main/java/subway/domain/exception/NonExistentNameException.java b/src/main/java/subway/domain/exception/NonExistentNameException.java new file mode 100644 index 000000000..592fa847c --- /dev/null +++ b/src/main/java/subway/domain/exception/NonExistentNameException.java @@ -0,0 +1,9 @@ +package subway.domain.exception; + +public class NonExistentNameException extends RuntimeException { + private static final String NON_EXISTENT_NAME_ERROR_MESSAGE_START = "\n[ERROR] 존재하지 않는 역 이름입니다."; + + public NonExistentNameException() { + super(NON_EXISTENT_NAME_ERROR_MESSAGE_START); + } +} \ No newline at end of file diff --git a/src/main/java/subway/domain/exception/SamePointsException.java b/src/main/java/subway/domain/exception/SamePointsException.java new file mode 100644 index 000000000..fae0211d9 --- /dev/null +++ b/src/main/java/subway/domain/exception/SamePointsException.java @@ -0,0 +1,9 @@ +package subway.domain.exception; + +public class SamePointsException extends RuntimeException { + private static final String SAME_POINTS_ERROR_MESSAGE = "\n[ERROR] 출발역과 도착역이 동일합니다."; + + public SamePointsException() { + super(SAME_POINTS_ERROR_MESSAGE); + } +} \ No newline at end of file diff --git a/src/main/java/subway/view/InputView.java b/src/main/java/subway/view/InputView.java new file mode 100644 index 000000000..5c181bd90 --- /dev/null +++ b/src/main/java/subway/view/InputView.java @@ -0,0 +1,32 @@ +package subway.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private final Scanner scanner; + + public InputView(Scanner scanner) { + this.scanner = scanner; + } + + public String scanMenu() { + OutputView.printSelectMessage(); + return getInput(); + } + + public String scanStartStation() { + OutputView.printStartInputMessage(); + return getInput(); + } + + public String scanEndStation() { + OutputView.printEndInputMessage(); + return getInput(); + } + + private String getInput() { + return scanner.nextLine(); + } +} diff --git a/src/main/java/subway/view/OutputView.java b/src/main/java/subway/view/OutputView.java new file mode 100644 index 000000000..4ff208ed2 --- /dev/null +++ b/src/main/java/subway/view/OutputView.java @@ -0,0 +1,30 @@ +package subway.view; + +public class OutputView { + + private static final String MAIN_SCREEN = "\n## 메인 화면\n1. 경로 조회\nQ. 종료"; + private static final String SUB_SCREEN = "\n## 경로 기준\n1. 최단 거리\n2. 최소 시간\nB. 돌아가기"; + private static final String SELECT_MESSAGE = "\n## 원하는 기능을 선택하세요."; + private static final String START_STATION_INPUT_MESSAGE = "\n## 출발역을 입력하세요."; + private static final String END_STATION_INPUT_MESSAGE = "\n## 도착역을 입력하세요."; + + public static void printMainScreen() { + System.out.println(MAIN_SCREEN); + } + + public static void printSubScreen() { + System.out.println(SUB_SCREEN); + } + + public static void printSelectMessage() { + System.out.println(SELECT_MESSAGE); + } + + public static void printStartInputMessage() { + System.out.println(START_STATION_INPUT_MESSAGE); + } + + public static void printEndInputMessage() { + System.out.println(END_STATION_INPUT_MESSAGE); + } +}