diff --git a/README.md b/README.md
index e6ed1056f..62bce37c6 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,18 @@
# 지하철 노선도 미션
+
- 지하철 역과 노선을 관리하는 지하철 노선도 기능을 구현한다.
## 🚀 기능 요구사항
-### 초기 설정
+### 초기 설정
+
- 프로그램 시작 시 역, 노선 등 필요한 정보를 미리 셋팅할 수 있다.
> 아래의 사전 등록 정보로 반드시 초기 설정을 하기
>
+
```
1. 지하철역으로 교대역, 강남역, 역삼역, 남부터미널역, 양재역, 양재시민의숲역, 매봉역이 등록되어 있다.
2. 지하철 노선으로 2호선, 3호선, 신분당선이 등록되어 있다.
@@ -20,23 +23,26 @@
```
-
+
### 지하철 역 관련 기능
+
- 지하철 역을 등록하고 삭제할 수 있다. (단, 노선에 등록된 역은 삭제할 수 없다)
- 중복된 지하철 역 이름이 등록될 수 없다.
- 지하철 역 이름은 2글자 이상이어야 한다.
- 지하철 역의 목록을 조회할 수 있다.
### 지하철 노선 관련 기능
+
- 지하철 노선을 등록하고 삭제할 수 있다.
- 중복된 지하철 노선 이름이 등록될 수 없다.
- 지하철 노선 이름은 2글자 이상이어야 한다.
-- 노선 등록 시 상행 종점역과 하행 종점역을 입력받는다.
+- 노선 등록 시 상행 종점역과 하행 종점역을 입력받는다.
- 지하철 노선의 목록을 조회할 수 있다.
### 지하철 구간 추가 기능
+
- 지하철 노선에 구간을 추가하는 기능은 노선에 역을 추가하는 기능이라고도 할 수 있다.
- - 역과 역사이를 구간이라 하고 이 구간들의 모음이 노선이다.
+ - 역과 역사이를 구간이라 하고 이 구간들의 모음이 노선이다.
- 하나의 역은 여러개의 노선에 추가될 수 있다.
- 역과 역 사이에 새로운 역이 추가 될 수 있다.
- 노선에서 갈래길은 생길 수 없다.
@@ -44,6 +50,7 @@
### 지하철 구간 삭제 기능
+
- 노선에 등록된 역을 제거할 수 있다.
- 종점을 제거할 경우 다음 역이 종점이 된다.
- 노선에 포함된 역이 두개 이하일 때는 역을 제거할 수 없다.
@@ -51,17 +58,21 @@
### 지하철 노선에 등록된 역 조회 기능
+
- 노선의 상행 종점부터 하행 종점까지 연결된 순서대로 역 목록을 조회할 수 있다.
## ✍🏻 입출력 요구사항
+
- `프로그래밍 실행 결과 예시`를 참고하여 입출력을 구현한다.
- 기대하는 출력 결과는 `[INFO]`를 붙여서 출력한다. 출력값의 형식은 예시와 동일하게 한다.
- 에러 발생 시 `[ERROR]`를 붙여서 출력한다. (에러의 문구는 자유롭게 작성한다.)
-### 💻 프로그래밍 실행 결과
+### 💻 프로그래밍 실행 결과
+
#### 역 관리
+
```
## 메인 화면
1. 역 관리
@@ -233,6 +244,7 @@ B. 돌아가기
```
### 구간 관리
+
- 순서는 1부터 시작한다.
```
@@ -379,25 +391,28 @@ B. 돌아가기
## 🎱 프로그래밍 요구사항
+
- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- - 기본적으로 [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html)을 원칙으로 한다.
- - 단, 들여쓰기는 '2 spaces'가 아닌 '4 spaces'로 한다.
+ - 기본적으로 [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html)을 원칙으로 한다.
+ - 단, 들여쓰기는 '2 spaces'가 아닌 '4 spaces'로 한다.
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
+ - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
+ - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
- 3항 연산자를 쓰지 않는다.
- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.
- - 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
+ - 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
- else 예약어를 쓰지 않는다.
- - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
+ - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
+ - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
- 프로그래밍 요구사항에서 별도로 변경 불가 안내가 없는 경우 파일 수정과 패키지 이동을 자유롭게 할 수 있다.
- 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 `[ERROR]` 로 시작해야 한다.
### 프로그래밍 요구사항 - Application
+
- Application 클래스를 활용해 구현해야 한다.
- Application의 패키지 구조는 변경하지 않는다.
-- 주석을 참고하여 구현할 수 있으며 주석대로 구현하지 않아도 되고 삭제해도 무관하다.
+- 주석을 참고하여 구현할 수 있으며 주석대로 구현하지 않아도 되고 삭제해도 무관하다.
+
```java
public class Application {
public static void main(String[] args) {
@@ -407,11 +422,12 @@ public class Application {
```
### 프로그래밍 요구사항 - Station, Line
+
- Station, Line 클래스를 활용하여 지하철역과 노선을 구현해야 한다.
- 제공하는 각 클래스의 기본 생성자를 추가할 수 없다.
- 필드(인스턴스 변수)인 name의 접근 제어자 private을 변경할 수 없다.
- 가능하면 setter 메소드(ex. setXXX)를 추가하지 않고 구현한다.
-
+
```java
public class Station {
private String name;
@@ -430,12 +446,13 @@ public class Station {
```
### 프로그래밍 요구사항 - StationRepository, LineRepository
+
- Station과 Line의 상태를 저장할 수 있는 StationRepository, LineRepository를 제공한다.
- 필요 시 StationRepository, LineRepository 이 외 추가로 Repository를 만들 수 있다.
- 추가로 생성되는 객체에 대해서 XXXRepository 네이밍으로 저장 클래스를 추가할 수 있다.
- 객체들의 상태를 관리하기 위해서 XXXRepository 클래스를 활용해 저장 로직을 구현해야 한다.
- 필요에 따라 자유롭게 수정이 가능하다.
-
+
```java
public class StationRepository {
private static final List stations = new ArrayList<>();
@@ -457,14 +474,61 @@ public class StationRepository {
## 📈 진행 요구사항
+
- 미션은 [java-subway-map-precourse 저장소](https://github.com/woowacourse/java-subway-map-precourse) 를 fork/clone해 시작한다.
- 기능을 구현하기 전에 java-subway-map-precourse/docs/README.md 파일에 구현할 기능 목록을 정리해 추가한다.
- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.
- - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다.
+ - [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) 문서를 참고하여 진행할 수 있다.
-
+ - [프리코스 과제 FAQ](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse/faq) 문서를 참고하여 진행할 수 있다.
+
## 📝 License
This project is [MIT](https://github.com/woowacourse/java-subway-map-precourse/blob/master/LICENSE.md) licensed.
+
+
+
+## ☑ 구현할 기능 목록
+
+- [x] 메인
+ - [x] 선택지에 없는 입력일 시 에러메세지를 출력한다.
+ - [x] 사용자 입력에 따라 하위 기능을 실행한다.
+ - [x] 사용자 입력이 Q인 경우 프로그램을 완전 종료한다.
+ - [x] 사전등록정보에 따라 지하철 노선도를 초기화한다.
+- [x] 역 관리
+ - [x] 선택지에 없는 입력일 시 에러메세지를 출력한다.
+ - [x] 사용자 입력에 따라 하위 기능을 실행한다.
+ - [x] 역 조회 시 모든 역의 이름을 중복 없이 출력한다.
+ - [x] 역 등록 시 이름 중복을 확인한다.
+ - [x] 역 등록 시 이름이 2글자 이상인지 확인한다.
+ - [x] 역 삭제 시 존재하는 역인지 확인한다.
+ - [x] 역 삭제 시 노선에 등록된 역인지 확인한다.
+- [x] 노선 관리
+ - [x] 하위 기능을 선택한다. (역 관리와 동일)
+ - [x] 노선 조회 시 모든 노선의 이름을 중복 없이 출력한다.
+ - [x] 노선 등록 시 이름 중복을 확인한다.
+ - [x] 노선 등록 시 이름이 2글자 이상인지 확인한다.
+ - [x] 노선 등록 시 상행 종점역이 존재하는 역인지 확인한다.
+ - [x] 노선 등록 시 하행 종점역이 존재하는 역인지 확인한다.
+ - [x] 노선 등록 시 상하행 종점역을 역 리스트에 추가한다.
+ - [x] 노선 삭제 시 존재하는 노선인지 확인한다.
+- [x] 구간 관리
+ - [x] 선택지에 없는 입력일 시 에러메세지를 출력한다.
+ - [x] 사용자 입력에 따라 하위 기능을 실행한다.
+ - [x] 구간 등록 시 존재하는 노선인지 확인한다.
+ - [x] 구간 등록 시 존재하는 역인지 확인한다.
+ - [x] 구간 등록 시 순서 입력이 숫자가 아니면 에러메세지를 출력한다.
+ - [x] 구간 등록 시 순서 입력이 범위 내 숫자가 아니면 에러메세지를 출력한다.
+ - [x] 구간 등록 시 상하행 종점역을 갱신한다.
+ - [x] 구간 삭제 시 존재하는 노선인지 확인한다.
+ - [x] 구간 삭제 시 존재하는 역인지 확인한다.
+ - [x] 구간 삭제 시 노선에 남은 역이 2개 초과인지 확인한다.
+ - [x] 구간 삭제 시 상하행 종점역을 갱신한다.
+- [x] 지하철 노선도
+ - [x] 모든 노선과 역을 순서대로 출력한다.
+- [x] 리팩토링
+ - [x] 비즈니스 로직과 UI 로직을 분리한다.
+ - [x] InputView를 통해 사용자 입력을 받는다.
+ - [x] OutputView를 통해 안내 문구와 결과를 출력한다.
\ No newline at end of file
diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java
index 0bcf786cc..bf14ff136 100644
--- a/src/main/java/subway/Application.java
+++ b/src/main/java/subway/Application.java
@@ -1,10 +1,14 @@
package subway;
+import subway.service.MainService;
+
import java.util.Scanner;
public class Application {
+
public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
- // TODO: 프로그램 구현
+ MainService mainService = new MainService(scanner);
+ mainService.run();
}
}
diff --git a/src/main/java/subway/constant/Constant.java b/src/main/java/subway/constant/Constant.java
new file mode 100644
index 000000000..9930e8aba
--- /dev/null
+++ b/src/main/java/subway/constant/Constant.java
@@ -0,0 +1,9 @@
+package subway.constant;
+
+public class Constant {
+
+ public static final int MIN_NAME_LENGTH = 2;
+ public static final int MIN_ORDER = 0;
+ public static final int MIN_STATIONS_IN_LINE = 3;
+ public static final String DIVIDER = "---";
+}
diff --git a/src/main/java/subway/constant/Function.java b/src/main/java/subway/constant/Function.java
new file mode 100644
index 000000000..cbb64da50
--- /dev/null
+++ b/src/main/java/subway/constant/Function.java
@@ -0,0 +1,40 @@
+package subway.constant;
+
+import subway.exception.InvalidInputException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum Function {
+
+ ADD("1"),
+ DELETE("2"),
+ SHOW("3"),
+ QUIT("B");
+
+ private static List functionCodes = new ArrayList<>();
+ private String code;
+
+ Function(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public static void validate(String input) throws InvalidInputException {
+ if (!isAvailable(input))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.INVALID_FUNCTION_CODE);
+ }
+
+ private static boolean isAvailable(String input) {
+ initFunctionCodes();
+ return functionCodes.contains(input);
+ }
+
+ private static void initFunctionCodes() {
+ for (Function function : Function.values())
+ functionCodes.add(function.code);
+ }
+}
diff --git a/src/main/java/subway/constant/Information.java b/src/main/java/subway/constant/Information.java
new file mode 100644
index 000000000..44005634d
--- /dev/null
+++ b/src/main/java/subway/constant/Information.java
@@ -0,0 +1,36 @@
+package subway.constant;
+
+public class Information {
+
+ public static final String INFO_HEADER = "[INFO] ";
+
+ public static final String CHOOSE_FUNCTION = "\n## 원하는 기능을 선택하세요.";
+ public static final String MAIN_INFO = "\n## 메인 화면\n1. 역 관리\n2. 노선 관리\n3. 구간 관리\n4. 지하철 노선도 출력\nQ. 종료\n" + CHOOSE_FUNCTION;
+
+ public static final String STATION_INFO = "\n## 역 관리 화면\n1. 역 등록\n2. 역 삭제\n3. 역 조회\nB. 돌아가기\n" + CHOOSE_FUNCTION;
+ public static final String ADD_STATION_INFO = "\n## 등록할 역 이름을 입력하세요.";
+ public static final String ADD_STATION_SUCCESS = INFO_HEADER + "지하철 역이 등록되었습니다.";
+ public static final String DELETE_STATION_INFO = "\n## 삭제할 역 이름을 입력하세요.";
+ public static final String DELETE_STATION_SUCCESS = INFO_HEADER + "지하철 역이 삭제되었습니다.";
+ public static final String SHOW_STATION_INFO = "\n## 역 목록";
+
+ public static final String LINE_INFO = "\n## 노선 관리 화면\n1. 노선 등록\n2. 노선 삭제\n3. 노선 조회\nB. 돌아가기\n" + CHOOSE_FUNCTION;
+ public static final String ADD_LINE_INFO = "\n## 등록할 노선 이름을 입력하세요.";
+ public static final String ADD_LINE_INFO_UP_END = "\n## 등록할 노선의 상행 종점역 이름을 입력하세요.";
+ public static final String ADD_LINE_INFO_DOWN_END = "\n## 등록할 노선의 하행 종점역 이름을 입력하세요.";
+ public static final String ADD_LINE_SUCCESS = INFO_HEADER + "지하철 노선이 등록되었습니다.";
+ public static final String DELETE_LINE_INFO = "\n## 삭제할 노선 이름을 입력하세요.";
+ public static final String DELETE_LINE_SUCCESS = INFO_HEADER + "지하철 노선이 삭제되었습니다.";
+ public static final String SHOW_LINE_INFO = "\n## 노선 목록";
+
+ public static final String LINK_INFO = "\n## 구간 관리 화면\n1. 구간 등록\n2. 구간 삭제\nB. 돌아가기\n" + CHOOSE_FUNCTION;
+ public static final String ADD_LINK_INFO_LINE = "\n## 노선 이름을 입력하세요.";
+ public static final String ADD_LINK_INFO_STATION = "\n## 역 이름을 입력하세요.";
+ public static final String ADD_LINK_INFO_ORDER = "\n## 순서를 입력하세요.";
+ public static final String ADD_LINK_SUCCESS = INFO_HEADER + "구간이 등록되었습니다.";
+ public static final String DELETE_LINK_INFO_LINE = "\n## 삭제할 구간의 노선 이름을 입력하세요.";
+ public static final String DELETE_LINK_INFO_STATION = "\n## 삭제할 구간의 역 이름을 입력하세요.";
+ public static final String DELETE_LINK_SUCCESS = INFO_HEADER + "구간이 삭제되었습니다.";
+
+ public static final String PRINT_INFO = "\n## 지하철 노선도";
+}
diff --git a/src/main/java/subway/constant/InitialData.java b/src/main/java/subway/constant/InitialData.java
new file mode 100644
index 000000000..15991cc21
--- /dev/null
+++ b/src/main/java/subway/constant/InitialData.java
@@ -0,0 +1,47 @@
+package subway.constant;
+
+import subway.domain.Line;
+import subway.domain.Station;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class InitialData {
+
+ public static final List stations = new ArrayList<>(Arrays.asList(
+ new Station("교대역"),
+ new Station("강남역"),
+ new Station("역삼역"),
+ new Station("남부터미널역"),
+ new Station("양재역"),
+ new Station("양재시민의숲역"),
+ new Station("매봉역")
+ ));
+
+
+ private static final List lineTwoStations = new ArrayList<>(Arrays.asList(
+ new Station("교대역"),
+ new Station("강남역"),
+ new Station("역삼역")
+ ));
+
+ private static final List lineThreeStations = new ArrayList<>(Arrays.asList(
+ new Station("교대역"),
+ new Station("남부터미널역"),
+ new Station("양재역"),
+ new Station("매봉역")
+ ));
+
+ private static final List lineSinbundangStations = new ArrayList<>(Arrays.asList(
+ new Station("강남역"),
+ new Station("양재역"),
+ new Station("양재시민의숲역")
+ ));
+
+ public static final List lines = new ArrayList<>(Arrays.asList(
+ new Line("2호선", lineTwoStations),
+ new Line("3호선", lineThreeStations),
+ new Line("신분당선", lineSinbundangStations)
+ ));
+}
diff --git a/src/main/java/subway/constant/Service.java b/src/main/java/subway/constant/Service.java
new file mode 100644
index 000000000..d0162f949
--- /dev/null
+++ b/src/main/java/subway/constant/Service.java
@@ -0,0 +1,41 @@
+package subway.constant;
+
+import subway.exception.InvalidInputException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum Service {
+
+ STATION("1"),
+ LINE("2"),
+ LINK("3"),
+ PRINT("4"),
+ QUIT("Q");
+
+ private static List serviceCodes = new ArrayList<>();
+ private String code;
+
+ Service(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public static void validate(String input) throws InvalidInputException {
+ if (!isAvailable(input))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.INVALID_SERVICE_CODE);
+ }
+
+ private static boolean isAvailable(String input) {
+ initServiceCodes();
+ return serviceCodes.contains(input);
+ }
+
+ private static void initServiceCodes() {
+ for (Service service : Service.values())
+ serviceCodes.add(service.code);
+ }
+}
diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java
index f4d738d5a..837aea7cf 100644
--- a/src/main/java/subway/domain/Line.java
+++ b/src/main/java/subway/domain/Line.java
@@ -1,15 +1,73 @@
package subway.domain;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
public class Line {
+
+ private static final int UP_END_INDEX = 0;
+
private String name;
+ private Station upEnd;
+ private Station downEnd;
+ private List stations = new ArrayList<>();
public Line(String name) {
this.name = name;
}
+ public Line(String name, List stations) {
+ this.name = name;
+ this.stations = stations;
+ setUpAndDownEndStations();
+ }
+
+ private void setUpAndDownEndStations() {
+ this.upEnd = stations.get(UP_END_INDEX);
+ this.downEnd = stations.get(stations.size() - 1);
+ }
+
public String getName() {
return name;
}
- // 추가 기능 구현
+ public Station getUpEnd() {
+ return upEnd;
+ }
+
+ public Station getDownEnd() {
+ return downEnd;
+ }
+
+ public List getStations() {
+ return stations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Line)) return false;
+ Line line = (Line) o;
+ return Objects.equals(name, line.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+
+
+ public void addNewLink(Link newLink) {
+ Station station = new Station(newLink.getStationName());
+ int targetIndex = newLink.getOrder();
+ stations.add(targetIndex, station);
+ setUpAndDownEndStations();
+ }
+
+ public void deleteTargetLink(Link targetLink) {
+ Station station = new Station(targetLink.getStationName());
+ stations.remove(station);
+ setUpAndDownEndStations();
+ }
}
diff --git a/src/main/java/subway/domain/Link.java b/src/main/java/subway/domain/Link.java
new file mode 100644
index 000000000..76e05f11c
--- /dev/null
+++ b/src/main/java/subway/domain/Link.java
@@ -0,0 +1,31 @@
+package subway.domain;
+
+public class Link {
+
+ private String lineName;
+ private String stationName;
+ private int order;
+
+ public Link(String lineName, String stationName, int order) {
+ this.lineName = lineName;
+ this.stationName = stationName;
+ this.order = order;
+ }
+
+ public Link(String targetLineName, String targetStationName) {
+ this.lineName = targetLineName;
+ this.stationName = targetStationName;
+ }
+
+ public String getLineName() {
+ return lineName;
+ }
+
+ public String getStationName() {
+ return stationName;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+}
diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java
index bdb142590..a572ebe5c 100644
--- a/src/main/java/subway/domain/Station.java
+++ b/src/main/java/subway/domain/Station.java
@@ -1,6 +1,9 @@
package subway.domain;
+import java.util.Objects;
+
public class Station {
+
private String name;
public Station(String name) {
@@ -11,5 +14,16 @@ public String getName() {
return name;
}
- // 추가 기능 구현
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Station)) return false;
+ Station station = (Station) o;
+ return Objects.equals(name, station.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
}
diff --git a/src/main/java/subway/exception/InvalidInputException.java b/src/main/java/subway/exception/InvalidInputException.java
new file mode 100644
index 000000000..ddba858f8
--- /dev/null
+++ b/src/main/java/subway/exception/InvalidInputException.java
@@ -0,0 +1,49 @@
+package subway.exception;
+
+public class InvalidInputException extends RuntimeException {
+
+ private final String ERROR_HEADER = "\n[ERROR] ";
+ private ExceptionCode exceptionCode;
+
+ public InvalidInputException(ExceptionCode exceptionCode) {
+ this.exceptionCode = exceptionCode;
+ }
+
+ public enum ExceptionCode {
+ INVALID_SERVICE_CODE,
+ INVALID_FUNCTION_CODE,
+ DUPLICATE_STATION,
+ DUPLICATE_LINE,
+ NO_SUCH_STATION,
+ NO_SUCH_LINE,
+ INVALID_NAME_LENGTH,
+ NON_NUMBER_INPUT,
+ OUT_OF_LINE_RANGE,
+ NO_LINK_AVAILABLE,
+ STATION_LINKED;
+ }
+
+ public String getMessage() {
+ if (exceptionCode.equals(ExceptionCode.INVALID_SERVICE_CODE) || exceptionCode.equals(ExceptionCode.INVALID_FUNCTION_CODE))
+ return ERROR_HEADER + "선택할 수 없는 기능입니다.";
+ if (exceptionCode.equals(ExceptionCode.DUPLICATE_STATION))
+ return ERROR_HEADER + "이미 등록된 역 이름입니다.";
+ if (exceptionCode.equals(ExceptionCode.DUPLICATE_LINE))
+ return ERROR_HEADER + "이미 등록된 노선 이름입니다.";
+ if (exceptionCode.equals(ExceptionCode.NO_SUCH_STATION))
+ return ERROR_HEADER + "존재하지 않는 역 이름입니다.";
+ if (exceptionCode.equals(ExceptionCode.NO_SUCH_LINE))
+ return ERROR_HEADER + "존재하지 않는 노선 이름입니다.";
+ if (exceptionCode.equals(ExceptionCode.INVALID_NAME_LENGTH))
+ return ERROR_HEADER + "이름은 두 글자 이상이어야 합니다.";
+ if (exceptionCode.equals(ExceptionCode.NON_NUMBER_INPUT))
+ return ERROR_HEADER + "순서는 숫자로 입력해야 합니다.";
+ if (exceptionCode.equals(ExceptionCode.OUT_OF_LINE_RANGE))
+ return ERROR_HEADER + "순서는 해당 노선의 범위 안에서 입력해야 합니다.";
+ if (exceptionCode.equals(ExceptionCode.NO_LINK_AVAILABLE))
+ return ERROR_HEADER + "역이 두 개 이하인 노선은 삭제할 수 없습니다";
+ if (exceptionCode.equals(ExceptionCode.STATION_LINKED))
+ return ERROR_HEADER + "노선에 연결된 역은 삭제할 수 없습니다";
+ return "";
+ }
+}
diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/repository/LineRepository.java
similarity index 62%
rename from src/main/java/subway/domain/LineRepository.java
rename to src/main/java/subway/repository/LineRepository.java
index 49132ddb6..9b4fb40d0 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;
@@ -6,6 +8,7 @@
import java.util.Objects;
public class LineRepository {
+
private static final List lines = new ArrayList<>();
public static List lines() {
@@ -16,7 +19,17 @@ public static void addLine(Line line) {
lines.add(line);
}
+ public static void addLine(List lines) {
+ for (Line line : lines)
+ addLine(line);
+ }
+
public static boolean deleteLineByName(String name) {
return lines.removeIf(line -> Objects.equals(line.getName(), name));
}
+
+ public static Line getLineByName(String name) {
+ int index = lines.indexOf(new Line(name));
+ return lines.get(index);
+ }
}
diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/repository/StationRepository.java
similarity index 65%
rename from src/main/java/subway/domain/StationRepository.java
rename to src/main/java/subway/repository/StationRepository.java
index b7245c0f3..720e98012 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;
@@ -6,6 +8,7 @@
import java.util.Objects;
public class StationRepository {
+
private static final List stations = new ArrayList<>();
public static List stations() {
@@ -16,7 +19,12 @@ public static void addStation(Station station) {
stations.add(station);
}
- public static boolean deleteStation(String name) {
+ public static void addStation(List stations) {
+ for (Station station : stations)
+ addStation(station);
+ }
+
+ public static boolean deleteStationByName(String name) {
return stations.removeIf(station -> Objects.equals(station.getName(), name));
}
}
diff --git a/src/main/java/subway/service/CrudService.java b/src/main/java/subway/service/CrudService.java
new file mode 100644
index 000000000..c10a01631
--- /dev/null
+++ b/src/main/java/subway/service/CrudService.java
@@ -0,0 +1,58 @@
+package subway.service;
+
+import subway.constant.Function;
+import subway.exception.InvalidInputException;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public abstract class CrudService implements MapService {
+
+ private InputView inputView;
+ private OutputView outputView;
+ private String functionInformation;
+
+ public CrudService(InputView inputView, OutputView outputView, String functionInformation) {
+ this.inputView = inputView;
+ this.outputView = outputView;
+ this.functionInformation = functionInformation;
+ }
+
+ public InputView getInputView() {
+ return inputView;
+ }
+
+ public OutputView getOutputView() {
+ return outputView;
+ }
+
+ @Override
+ public void run() {
+ try {
+ String selectedFunction = getSelectedFunction();
+ Function.validate(selectedFunction);
+ runSelectedFunction(selectedFunction);
+ } catch (InvalidInputException e) {
+ outputView.printErrorMessage(e.getMessage());
+ }
+ }
+
+ private String getSelectedFunction() {
+ outputView.printInformation(functionInformation);
+ return inputView.getSelectedFunctionInput();
+ }
+
+ private void runSelectedFunction(String selectedFunction) {
+ if (selectedFunction.equals(Function.ADD.getCode()))
+ add();
+ if (selectedFunction.equals(Function.DELETE.getCode()))
+ delete();
+ if (selectedFunction.equals(Function.SHOW.getCode()))
+ show();
+ }
+
+ public abstract void add();
+
+ public abstract void delete();
+
+ public abstract void show();
+}
diff --git a/src/main/java/subway/service/LineService.java b/src/main/java/subway/service/LineService.java
new file mode 100644
index 000000000..0cac4bd38
--- /dev/null
+++ b/src/main/java/subway/service/LineService.java
@@ -0,0 +1,113 @@
+package subway.service;
+
+import subway.constant.Constant;
+import subway.constant.InitialData;
+import subway.domain.Line;
+import subway.domain.Station;
+import subway.exception.InvalidInputException;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static subway.constant.Information.*;
+
+public class LineService extends CrudService {
+
+ public LineService(InputView inputView, OutputView outputView) {
+ super(inputView, outputView, LINE_INFO);
+ initLines();
+ }
+
+ private void initLines() {
+ LineRepository.addLine(InitialData.lines);
+ }
+
+
+ @Override
+ public void add() {
+ Line newLine = getNewLine();
+ validateNewLine(newLine);
+ LineRepository.addLine(newLine);
+ getOutputView().printInformation(ADD_LINE_SUCCESS);
+ }
+
+ private Line getNewLine() {
+ String name = getNewLineName();
+ List endStations = getNewLineEndStations();
+ return new Line(name, endStations);
+ }
+
+ private String getNewLineName() {
+ getOutputView().printInformation(ADD_LINE_INFO);
+ return getInputView().getLineName();
+ }
+
+ private List getNewLineEndStations() {
+ Station upEnd = getNewLineUpEnd();
+ Station downEnd = getNewLineDownEnd();
+ return new ArrayList<>(Arrays.asList(upEnd, downEnd));
+ }
+
+ private Station getNewLineUpEnd() {
+ getOutputView().printInformation(ADD_LINE_INFO_UP_END);
+ return new Station(getInputView().getStationName());
+ }
+
+ private Station getNewLineDownEnd() {
+ getOutputView().printInformation(ADD_LINE_INFO_DOWN_END);
+ return new Station(getInputView().getStationName());
+ }
+
+ private void validateNewLine(Line newLine) {
+ validateNameLength(newLine);
+ validateDuplicateLineExists(newLine);
+ validateEndStationsExist(newLine);
+ }
+
+ private void validateNameLength(Line newLine) {
+ if (newLine.getName().length() < Constant.MIN_NAME_LENGTH)
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.INVALID_NAME_LENGTH);
+ }
+
+ private void validateDuplicateLineExists(Line newLine) {
+ if (LineRepository.lines().contains(newLine))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.DUPLICATE_LINE);
+ }
+
+ private void validateEndStationsExist(Line newLine) {
+ if (!StationRepository.stations().contains(newLine.getUpEnd())
+ || !StationRepository.stations().contains(newLine.getDownEnd()))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NO_SUCH_STATION);
+ }
+
+
+ @Override
+ public void delete() {
+ String targetLineName = getTargetLineName();
+ validateTargetLine(targetLineName);
+ LineRepository.deleteLineByName(targetLineName);
+ getOutputView().printInformation(DELETE_LINE_SUCCESS);
+ }
+
+ private String getTargetLineName() {
+ getOutputView().printInformation(DELETE_STATION_INFO);
+ return getInputView().getLineName();
+ }
+
+ private void validateTargetLine(String targetLineName) {
+ Line targetLine = new Line(targetLineName);
+ if (!LineRepository.lines().contains(targetLine))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NO_SUCH_LINE);
+ }
+
+
+ @Override
+ public void show() {
+ getOutputView().printLineList();
+ }
+}
diff --git a/src/main/java/subway/service/LinkService.java b/src/main/java/subway/service/LinkService.java
new file mode 100644
index 000000000..978d8b786
--- /dev/null
+++ b/src/main/java/subway/service/LinkService.java
@@ -0,0 +1,136 @@
+package subway.service;
+
+import subway.domain.Line;
+import subway.domain.Link;
+import subway.domain.Station;
+import subway.exception.InvalidInputException;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+import static subway.constant.Constant.MIN_ORDER;
+import static subway.constant.Constant.MIN_STATIONS_IN_LINE;
+import static subway.constant.Information.*;
+
+public class LinkService extends CrudService {
+
+ public LinkService(InputView inputView, OutputView outputView) {
+ super(inputView, outputView, LINK_INFO);
+ }
+
+ @Override
+ public void add() {
+ try {
+ Link newLink = getNewLink();
+ validateNewLink(newLink);
+ addNewLink(newLink);
+ getOutputView().printInformation(ADD_LINK_SUCCESS);
+ } catch (NumberFormatException e) {
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NON_NUMBER_INPUT);
+ }
+ }
+
+ private Link getNewLink() throws NumberFormatException {
+ String lineName = getLineNameOnAdd();
+ String stationName = getStationNameOnAdd();
+ int order = getOrderOnAdd();
+ return new Link(lineName, stationName, order);
+ }
+
+ private String getLineNameOnAdd() {
+ getOutputView().printInformation(ADD_LINK_INFO_LINE);
+ return getInputView().getLineName();
+ }
+
+ private String getStationNameOnAdd() {
+ getOutputView().printInformation(ADD_LINK_INFO_STATION);
+ return getInputView().getStationName();
+ }
+
+ private int getOrderOnAdd() throws NumberFormatException {
+ getOutputView().printInformation(ADD_LINK_INFO_ORDER);
+ return getInputView().getOrder();
+ }
+
+ private void validateNewLink(Link newLink) {
+ validateLineExists(newLink.getLineName());
+ validateStationExists(newLink.getStationName());
+ validateOrderInRange(newLink);
+ }
+
+ private void validateLineExists(String lineName) {
+ Line line = new Line(lineName);
+ if (!LineRepository.lines().contains(line))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NO_SUCH_LINE);
+ }
+
+ private void validateStationExists(String stationName) {
+ Station station = new Station(stationName);
+ if (!StationRepository.stations().contains(station))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NO_SUCH_STATION);
+ }
+
+ private void validateOrderInRange(Link newLink) {
+ Line line = getTargetLineByName(newLink.getLineName());
+ if (newLink.getOrder() < MIN_ORDER || newLink.getOrder() > line.getStations().size())
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.OUT_OF_LINE_RANGE);
+ }
+
+ private Line getTargetLineByName(String lineName) {
+ return LineRepository.getLineByName(lineName);
+ }
+
+ private void addNewLink(Link newLink) {
+ Line line = getTargetLineByName(newLink.getLineName());
+ line.addNewLink(newLink);
+ }
+
+
+ @Override
+ public void delete() {
+ Link targetLink = getTargetLink();
+ validateTargetLink(targetLink);
+ deleteTargetLink(targetLink);
+ getOutputView().printInformation(DELETE_LINK_SUCCESS);
+ }
+
+ private Link getTargetLink() {
+ String targetLineName = getTargetLineNameOnDelete();
+ String targetStationName = getTargetStationNameOnDelete();
+ return new Link(targetLineName, targetStationName);
+ }
+
+ private String getTargetLineNameOnDelete() {
+ getOutputView().printInformation(DELETE_LINK_INFO_LINE);
+ return getInputView().getLineName();
+ }
+
+ private String getTargetStationNameOnDelete() {
+ getOutputView().printInformation(DELETE_LINK_INFO_STATION);
+ return getInputView().getStationName();
+ }
+
+ private void validateTargetLink(Link targetLink) {
+ validateLineExists(targetLink.getLineName());
+ validateStationExists(targetLink.getStationName());
+ validateLineLength(targetLink.getLineName());
+ }
+
+ private void validateLineLength(String lineName) {
+ Line targetLine = getTargetLineByName(lineName);
+ if (targetLine.getStations().size() < MIN_STATIONS_IN_LINE)
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NO_LINK_AVAILABLE);
+ }
+
+ private void deleteTargetLink(Link targetLink) {
+ Line targetLine = getTargetLineByName(targetLink.getLineName());
+ targetLine.deleteTargetLink(targetLink);
+ }
+
+
+ @Override
+ public void show() {
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.INVALID_FUNCTION_CODE);
+ }
+}
diff --git a/src/main/java/subway/service/MainService.java b/src/main/java/subway/service/MainService.java
new file mode 100644
index 000000000..149121d64
--- /dev/null
+++ b/src/main/java/subway/service/MainService.java
@@ -0,0 +1,75 @@
+package subway.service;
+
+import subway.constant.Service;
+import subway.exception.InvalidInputException;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+import java.util.Scanner;
+
+import static subway.constant.Information.MAIN_INFO;
+
+public class MainService implements MapService {
+
+ private boolean isContinue = true;
+
+ private InputView inputView;
+ private OutputView outputView;
+
+ private StationService stationService;
+ private LineService lineService;
+ private LinkService linkService;
+ private PrintService printService;
+
+ public MainService(Scanner scanner) {
+ initViews(scanner);
+ initServices();
+ }
+
+ private void initServices() {
+ stationService = new StationService(inputView, outputView);
+ lineService = new LineService(inputView, outputView);
+ linkService = new LinkService(inputView, outputView);
+ printService = new PrintService(outputView);
+ }
+
+ private void initViews(Scanner scanner) {
+ this.inputView = new InputView(scanner);
+ this.outputView = new OutputView();
+ }
+
+
+ @Override
+ public void run() {
+ while (isContinue)
+ runService();
+ }
+
+ private void runService() {
+ try {
+ String selectedService = getSelectedService();
+ Service.validate(selectedService);
+ runSelectedService(selectedService);
+ } catch (InvalidInputException e) {
+ outputView.printErrorMessage(e.getMessage());
+ }
+ }
+
+ private String getSelectedService() {
+ outputView.printInformation(MAIN_INFO);
+ return inputView.getSelectedServiceInput();
+ }
+
+ private void runSelectedService(String selectedService) {
+ if (selectedService.equals(Service.STATION.getCode()))
+ stationService.run();
+ if (selectedService.equals(Service.LINE.getCode()))
+ lineService.run();
+ if (selectedService.equals(Service.LINK.getCode()))
+ linkService.run();
+ if (selectedService.equals(Service.PRINT.getCode()))
+ printService.run();
+ if (selectedService.equals(Service.QUIT.getCode()))
+ isContinue = false;
+ }
+}
diff --git a/src/main/java/subway/service/MapService.java b/src/main/java/subway/service/MapService.java
new file mode 100644
index 000000000..f9266c8ae
--- /dev/null
+++ b/src/main/java/subway/service/MapService.java
@@ -0,0 +1,7 @@
+package subway.service;
+
+public interface MapService {
+
+ void run();
+
+}
diff --git a/src/main/java/subway/service/PrintService.java b/src/main/java/subway/service/PrintService.java
new file mode 100644
index 000000000..365565e93
--- /dev/null
+++ b/src/main/java/subway/service/PrintService.java
@@ -0,0 +1,17 @@
+package subway.service;
+
+import subway.view.OutputView;
+
+public class PrintService implements MapService {
+
+ private OutputView outputView;
+
+ public PrintService(OutputView outputView) {
+ this.outputView = outputView;
+ }
+
+ @Override
+ public void run() {
+ outputView.printMap();
+ }
+}
diff --git a/src/main/java/subway/service/StationService.java b/src/main/java/subway/service/StationService.java
new file mode 100644
index 000000000..01380b5c3
--- /dev/null
+++ b/src/main/java/subway/service/StationService.java
@@ -0,0 +1,95 @@
+package subway.service;
+
+import subway.constant.Constant;
+import subway.constant.Information;
+import subway.constant.InitialData;
+import subway.domain.Line;
+import subway.domain.Station;
+import subway.exception.InvalidInputException;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+import static subway.constant.Information.*;
+
+public class StationService extends CrudService {
+
+ public StationService(InputView inputView, OutputView outputView) {
+ super(inputView, outputView, Information.STATION_INFO);
+ initStations();
+ }
+
+ private void initStations() {
+ StationRepository.addStation(InitialData.stations);
+ }
+
+
+ @Override
+ public void add() {
+ Station newStation = getNewStation();
+ validateNewStation(newStation);
+ StationRepository.addStation(newStation);
+ getOutputView().printInformation(ADD_STATION_SUCCESS);
+ }
+
+ private Station getNewStation() {
+ getOutputView().printInformation(ADD_STATION_INFO);
+ return new Station(getInputView().getStationName());
+ }
+
+ private void validateNewStation(Station newStation) {
+ validateNameLength(newStation);
+ validateDuplicateStationExists(newStation);
+ }
+
+ private void validateNameLength(Station newStation) {
+ if (newStation.getName().length() < Constant.MIN_NAME_LENGTH)
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.INVALID_NAME_LENGTH);
+ }
+
+ private void validateDuplicateStationExists(Station newStation) {
+ if (StationRepository.stations().contains(newStation))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.DUPLICATE_STATION);
+ }
+
+
+ @Override
+ public void delete() {
+ Station targetStation = getTargetStation();
+ validateTargetStation(targetStation);
+ StationRepository.deleteStationByName(targetStation.getName());
+ getOutputView().printInformation(DELETE_STATION_SUCCESS);
+ }
+
+ private Station getTargetStation() {
+ getOutputView().printInformation(DELETE_STATION_INFO);
+ return new Station(getInputView().getStationName());
+ }
+
+ private void validateTargetStation(Station targetStation) {
+ validateTargetStationExists(targetStation);
+ validateTargetStationIsNotLinked(targetStation);
+ }
+
+ private void validateTargetStationExists(Station targetStation) {
+ if (!StationRepository.stations().contains(targetStation))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.NO_SUCH_STATION);
+ }
+
+ private void validateTargetStationIsNotLinked(Station targetStation) {
+ for (Line line : LineRepository.lines())
+ validateTargetStationIsNotInLine(line, targetStation);
+ }
+
+ private void validateTargetStationIsNotInLine(Line line, Station targetStation) {
+ if (line.getStations().contains(targetStation))
+ throw new InvalidInputException(InvalidInputException.ExceptionCode.STATION_LINKED);
+ }
+
+
+ @Override
+ public void show() {
+ getOutputView().printStationList();
+ }
+}
diff --git a/src/main/java/subway/view/InputView.java b/src/main/java/subway/view/InputView.java
new file mode 100644
index 000000000..847d7a2ec
--- /dev/null
+++ b/src/main/java/subway/view/InputView.java
@@ -0,0 +1,37 @@
+package subway.view;
+
+import java.util.Scanner;
+
+public class InputView {
+
+ private final Scanner scanner;
+
+ public InputView(Scanner scanner) {
+ this.scanner = scanner;
+ }
+
+
+ private String userInputString() {
+ return scanner.nextLine();
+ }
+
+ public String getSelectedServiceInput() {
+ return userInputString();
+ }
+
+ public String getSelectedFunctionInput() {
+ return userInputString();
+ }
+
+ public String getLineName() {
+ return userInputString();
+ }
+
+ public String getStationName() {
+ return userInputString();
+ }
+
+ public int getOrder() throws NumberFormatException {
+ return Integer.parseInt(scanner.nextLine()) - 1;
+ }
+}
diff --git a/src/main/java/subway/view/OutputView.java b/src/main/java/subway/view/OutputView.java
new file mode 100644
index 000000000..3345bb2ff
--- /dev/null
+++ b/src/main/java/subway/view/OutputView.java
@@ -0,0 +1,57 @@
+package subway.view;
+
+import subway.domain.Line;
+import subway.domain.Station;
+import subway.repository.LineRepository;
+import subway.repository.StationRepository;
+
+import static subway.constant.Constant.DIVIDER;
+import static subway.constant.Information.*;
+
+public class OutputView {
+
+ public void printErrorMessage(String message) {
+ print(message);
+ }
+
+ public void printInformation(String information) {
+ printEmptyLine();
+ print(information);
+ }
+
+ private void printEmptyLine() {
+ print("");
+ }
+
+ private void print(String message) {
+ System.out.println(message);
+ }
+
+ public void printStationList() {
+ print(SHOW_STATION_INFO);
+ for (Station station : StationRepository.stations())
+ print(INFO_HEADER + station.getName());
+ }
+
+ public void printLineList() {
+ print(SHOW_LINE_INFO);
+ for (Line line : LineRepository.lines())
+ print(INFO_HEADER + line.getName());
+ }
+
+
+ public void printMap() {
+ print(PRINT_INFO);
+ for (Line line : LineRepository.lines()) {
+ printLineAndItsStations(line);
+ printEmptyLine();
+ }
+ }
+
+ private void printLineAndItsStations(Line line) {
+ print(INFO_HEADER + line.getName());
+ print(INFO_HEADER + DIVIDER);
+ for (Station station : line.getStations())
+ print(INFO_HEADER + station.getName());
+ }
+}