diff --git a/README.md b/README.md
index 450e7753a..76f9126ef 100644
--- a/README.md
+++ b/README.md
@@ -1,469 +1,124 @@
# 지하철 노선도 미션
-- 지하철 역과 노선을 관리하는 지하철 노선도 기능을 구현한다.
-
-
-## 🚀 기능 요구사항
-
-### 초기 설정
-- 프로그램 시작 시 역, 노선 등 필요한 정보를 미리 셋팅할 수 있다.
-
-> 아래의 사전 등록 정보로 반드시 초기 설정을 하기
->
-```
- 1. 지하철역으로 교대역, 강남역, 역삼역, 남부터미널역, 양재역, 양재시민의숲역, 매봉역이 등록되어 있다.
- 2. 지하철 노선으로 2호선, 3호선, 신분당선이 등록되어 있다.
- 3. 노선에 역이 아래와 같이 등록되어 있다.(왼쪽 끝이 상행 종점)
- - 2호선: 교대역 - 강남역 - 역삼역
- - 3호선: 교대역 - 남부터미널역 - 양재역 - 매봉역
- - 신분당선: 강남역 - 양재역 - 양재시민의숲역
- ```
-
-
-
-### 지하철 역 관련 기능
-- 지하철 역을 등록하고 삭제할 수 있다. (단, 노선에 등록된 역은 삭제할 수 없다)
-- 중복된 지하철 역 이름이 등록될 수 없다.
-- 지하철 역 이름은 2글자 이상이어야 한다.
-- 지하철 역의 목록을 조회할 수 있다.
-
-### 지하철 노선 관련 기능
-- 지하철 노선을 등록하고 삭제할 수 있다.
-- 중복된 지하철 노선 이름이 등록될 수 없다.
-- 지하철 노선 이름은 2글자 이상이어야 한다.
-- 노선 등록 시 상행 종점역과 하행 종점역을 입력받는다.
-- 지하철 노선의 목록을 조회할 수 있다.
-
-### 지하철 구간 추가 기능
-- 지하철 노선에 구간을 추가하는 기능은 노선에 역을 추가하는 기능이라고도 할 수 있다.
- - 역과 역사이를 구간이라 하고 이 구간들의 모음이 노선이다.
-- 하나의 역은 여러개의 노선에 추가될 수 있다.
-- 역과 역 사이에 새로운 역이 추가 될 수 있다.
-- 노선에서 갈래길은 생길 수 없다.
-
-
-
-### 지하철 구간 삭제 기능
-- 노선에 등록된 역을 제거할 수 있다.
-- 종점을 제거할 경우 다음 역이 종점이 된다.
-- 노선에 포함된 역이 두개 이하일 때는 역을 제거할 수 없다.
-
-
-
-### 지하철 노선에 등록된 역 조회 기능
-- 노선의 상행 종점부터 하행 종점까지 연결된 순서대로 역 목록을 조회할 수 있다.
-
-
-
-## ✍🏻 입출력 요구사항
-- `프로그래밍 실행 결과 예시`를 참고하여 입출력을 구현한다.
-- 기대하는 출력 결과는 `[INFO]`를 붙여서 출력한다. 출력값의 형식은 예시와 동일하게 한다.
-- 에러 발생 시 `[ERROR]`를 붙여서 출력한다. (에러의 문구는 자유롭게 작성한다.)
-
-### 💻 프로그래밍 실행 결과
-#### 역 관리
-```
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-1
-
-## 역 관리 화면
-1. 역 등록
-2. 역 삭제
-3. 역 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-1
-
-## 등록할 역 이름을 입력하세요.
-잠실역
-
-[INFO] 지하철 역이 등록되었습니다.
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-1
-
-## 역 관리 화면
-1. 역 등록
-2. 역 삭제
-3. 역 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-3
-
-## 역 목록
-[INFO] 교대역
-[INFO] 강남역
-[INFO] 역삼역
-[INFO] 남부터미널역
-[INFO] 양재역
-[INFO] 양재시민의숲역
-[INFO] 매봉역
-[INFO] 잠실역
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-1
-
-## 역 관리 화면
-1. 역 등록
-2. 역 삭제
-3. 역 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-2
-
-## 삭제할 역 이름을 입력하세요.
-잠실역
-
-[INFO] 지하철 역이 삭제되었습니다.
-
-...
-```
-
-### 노선 관리
-
-```
-
-...
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-2
-
-## 노선 관리 화면
-1. 노선 등록
-2. 노선 삭제
-3. 노선 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-1
-
-## 등록할 노선 이름을 입력하세요.
-1호선
-
-## 등록할 노선의 상행 종점역 이름을 입력하세요.
-강남역
-
-## 등록할 노선의 하행 종점역 이름을 입력하세요.
-잠실역
-
-[INFO] 지하철 노선이 등록되었습니다.
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-2
-
-## 노선 관리 화면
-1. 노선 등록
-2. 노선 삭제
-3. 노선 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-3
-
-## 노선 목록
-[INFO] 2호선
-[INFO] 3호선
-[INFO] 신분당선
-[INFO] 1호선
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-2
-
-## 노선 관리 화면
-1. 노선 등록
-2. 노선 삭제
-3. 노선 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-2
-
-## 삭제할 노선 이름을 입력하세요.
-1호선
-
-[INFO] 지하철 노선이 삭제되었습니다.
-
-...
-
-```
-
-### 구간 관리
-- 순서는 1부터 시작한다.
-
-```
-...
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-3
-
-## 구간 관리 화면
-1. 구간 등록
-2. 구간 삭제
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-1
-
-## 노선을 입력하세요.
-2호선
-
-## 역이름을 입력하세요.
-잠실역
-
-## 순서를 입력하세요.
-2
-
-[INFO] 구간이 등록되었습니다.
-
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-3
-
-## 구간 관리 화면
-1. 구간 등록
-2. 구간 삭제
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-2
-
-## 삭제할 구간의 노선을 입력하세요.
-2호선
-
-## 삭제할 구간의 역을 입력하세요.
-잠실역
-
-[INFO] 구간이 삭제되었습니다.
-
-...
-
-```
-
-### 지하철 노선도 출력
-
-```
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-4
-
-## 지하철 노선도
-[INFO] 2호선
-[INFO] ---
-[INFO] 교대역
-[INFO] 강남역
-[INFO] 역삼역
-
-[INFO] 3호선
-[INFO] ---
-[INFO] 교대역
-[INFO] 남부터미널역
-[INFO] 양재역
-[INFO] 매봉역
-
-[INFO] 신분당선
-[INFO] ---
-[INFO] 강남역
-[INFO] 양재역
-[INFO] 양재시민의숲역
-
-```
-
-#### 에러 출력 예시
-
-```
-## 메인 화면
-1. 역 관리
-2. 노선 관리
-3. 구간 관리
-4. 지하철 노선도 출력
-Q. 종료
-
-## 원하는 기능을 선택하세요.
-5
-
-[ERROR] 선택할 수 없는 기능입니다.
-
-## 원하는 기능을 선택하세요.
-1
-
-## 역 관리 화면
-1. 역 등록
-2. 역 삭제
-3. 역 조회
-B. 돌아가기
-
-## 원하는 기능을 선택하세요.
-1
-
-## 등록할 역 이름을 입력하세요.
-강남역
-
-[ERROR] 이미 등록된 역 이름입니다.
-
-## 역 관리 화면
-1. 역 등록
-2. 역 삭제
-3. 역 조회
-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의 패키지 구조는 변경하지 않는다.
-```java
-public class Application {
- public static void main(String[] args) {
- ...
- }
-}
-```
-
-### 프로그래밍 요구사항 - 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));
- }
-}
-```
-
-
-
-## 📈 진행 요구사항
-- 미션은 [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를 남긴다.
-- [프리코스 과제 제출 문서](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-map-precourse/blob/master/LICENSE.md) licensed.
+## 기능 구현 목록
+> 기능 작동 순서대로 작성
+
+1. 초기 역 및 노선 등록
+2. 메인 화면 출력 및 기능 선택
+ - 예외 사항
+ 1. 1,2,3,4,Q가 아니면 예외 발생
+ - “선택할 수 없는 기능입니다.”
+3. 역 관리
+ 1. 역 관리 화면 출력 및 기능 선택
+ - 예외 사항
+ 1. 1,2,3,B가 아니면 예외 발생
+ - “선택할 수 없는 기능입니다.”
+ 2. 역 등록
+ 1. 등록할 역 이름 입력
+ - 예외 사항
+ - 이름 마지막 글자가 "역"이 아니면 예외 발생
+ - “역 이름은 "역"으로 끝나야 합니다.”
+ - ’역’을 포함해서 2글자 미만이면 예외 발생
+ - “역 이름은 2글자 이상이어야 합니다.”
+ - 기존에 이미 존재하는 역 이름이면 예외 발생
+ - “이미 등록된 역 이름입니다.”
+ 2. 역 등록 후 메시지 출력
+ - 지하철 역이 등록되었습니다.
+ 3. 역 삭제
+ 1. 삭제할 역 이름 입력
+ - 예외 사항
+ - 이름 마지막 글자가 "역"이 아니면 예외 발생
+ - “역 이름은 "역"으로 끝나야 합니다.”
+ - 존재하지 않는 역이면 예외 발생
+ - “존재하지 않는 역입니다.”
+ - 노선에 등록된 역이면 예외 발생
+ - “노선에 등록된 역은 삭제할 수 없습니다.”
+ 2. 역 삭제 후 메시지 출력
+ - 지하철 역이 삭제되었습니다.
+ 4. 역 조회
+ 1. 역 목록 출력
+ 5. 돌아가기
+ 1. 메인 화면으로 이동
+4. 노선 관리
+ 1. 노선 관리 화면 출력 및 기능 선택
+ - 예외 사항
+ 1. 1,2,3,B가 아니면 예외 발생
+ - “선택할 수 없는 기능입니다.”
+ 2. 노선 등록
+ 1. 등록할 노선 이름 입력
+ - 예외 사항
+ - 이름 마지막 글자가 "선"이 아니면 예외 발생
+ - “노선 이름은 "선"으로 끝나야 합니다.”
+ - ’선’을 포함해서 2글자 미만이면 예외 발생
+ - “노선 이름은 2글자 이상이어야 합니다.”
+ - 기존에 이미 존재하는 노선 이름이면 예외 발생
+ - “이미 등록된 노선 이름입니다.”
+ 2. 등록할 노선의 상행 종점역 이름 입력
+ - 예외 사항
+ - 존재하지 않는 역이면 예외 발생
+ - “존재하지 않는 역입니다.”
+ 3. 등록할 노선의 하행 종점역 이름 입력
+ - 예외 사항
+ - 존재하지 않는 역이면 예외 발생
+ - “존재하지 않는 역입니다.”
+ - 상행 종점역과 같은 역이면 예외 발생
+ - “해당 노선에 이미 존재하는 역입니다.”
+ 4. 노선 등록 후 메시지 출력
+ - 지하철 노선이 등록되었습니다.
+ 3. 노선 삭제
+ 1. 삭제할 노선 이름 입력
+ - 예외 사항
+ - 이름 마지막 글자가 "선"이 아니면 예외 발생
+ - “노선 이름은 "선"으로 끝나야 합니다.”
+ - 존재하지 않는 노선이면 예외 발생
+ - “존재하지 않는 노선입니다.”
+ 2. 노선 삭제 후 메시지 출력
+ - 지하철 노선이 삭제되었습니다.
+ 4. 노선 조회
+ 1. 노선 목록 출력
+ 5. 돌아가기
+ 1. 메인 화면으로 이동
+5. 구간 관리
+ 1. 구간 관리 화면 출력 및 기능 선택
+ - 예외 사항
+ 1. 1,2,B가 아니면 예외 발생
+ - “선택할 수 없는 기능입니다.”
+ 2. 구간 등록
+ 1. 노선 입력
+ - 예외 사항
+ - 존재하지 않는 노선이면 예외 발생
+ - “존재하지 않는 노선입니다.”
+ 2. 역 입력
+ - 예외 사항
+ - 존재하지 않는 역이면 예외 발생
+ - “존재하지 않는 역입니다.”
+ - 해당 노선에 이미 존재하는 역이면 예외 발생
+ - “해당 노선에 이미 존재하는 역입니다.”
+ 3. 순서 입력
+ - 예외 사항
+ - 0을 입력하거나 숫자가 아니면 예외 발생
+ - “1 이상의 숫자를 입력해야합니다.”
+ - `현재 노선의 길이 + 1` 보다 큰 순서이면 예외 발생
+ - “`현재 노선의 길이 + 1`이하의 순서를 입력해야합니다.”
+ 4. 구간 등록 후 메시지 출력
+ - 구간이 등록되었습니다.
+ 3. 구간 삭제
+ 1. 삭제할 구간의 노선 입력
+ - 예외 사항
+ - 존재하지 않는 노선이면 예외 발생
+ - “존재하지 않는 노선입니다.”
+ - 노선에 포함된 역이 두개 이하이면 예외 발생
+ - “해당 노선의 역은 더이상 삭제할 수 없습니다.”
+ 2. 삭제할 구간의 역 입력
+ - 예외 사항
+ - 존재하지 않는 역이면 예외 발생
+ - “존재하지 않는 역입니다.”
+ - 해당 노선에 존재하지 않는 역이면 예외 발생
+ - “해당 노선에 존재하지 않는 역입니다.”
+ 3. 구간 삭제 후 메시지 출력
+ - 구간이 삭제되었습니다.
+ 4. 돌아가기
+ 1. 메인 화면으로 이동
+6. 지하철 노선도 출력
+ - 노선 순서대로 역 목록 출력
+7. 종료
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d226001ca..3de213d33 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,3 +18,9 @@ dependencies {
test {
useJUnitPlatform()
}
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index dcf98e9d0..8c06312e5 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java
index 0bcf786cc..ed53c6558 100644
--- a/src/main/java/subway/Application.java
+++ b/src/main/java/subway/Application.java
@@ -1,10 +1,21 @@
package subway;
import java.util.Scanner;
+import subway.command.line.LineMenuCommandRegistry;
+import subway.command.main.MainMenuCommandRegistry;
+import subway.command.section.SectionMenuCommandRegistry;
+import subway.command.station.StationMenuCommandRegistry;
+import subway.controller.SubwayController;
+import subway.service.SubwayService;
public class Application {
public static void main(String[] args) {
- final Scanner scanner = new Scanner(System.in);
- // TODO: 프로그램 구현
+ SubwayService service = new SubwayService();
+ StationMenuCommandRegistry stationRegistry = StationMenuCommandRegistry.from(service);
+ LineMenuCommandRegistry lineRegistry = LineMenuCommandRegistry.from(service);
+ SectionMenuCommandRegistry sectionRegistry = SectionMenuCommandRegistry.from(service);
+ MainMenuCommandRegistry mainRegistry = MainMenuCommandRegistry.from(service, stationRegistry, lineRegistry, sectionRegistry);
+ SubwayController controller = new SubwayController(mainRegistry, service);
+ controller.run();
}
}
diff --git a/src/main/java/subway/command/Command.java b/src/main/java/subway/command/Command.java
new file mode 100644
index 000000000..a25f0a800
--- /dev/null
+++ b/src/main/java/subway/command/Command.java
@@ -0,0 +1,5 @@
+package subway.command;
+
+public interface Command {
+ void execute();
+}
diff --git a/src/main/java/subway/command/line/LineMenuCommandRegistry.java b/src/main/java/subway/command/line/LineMenuCommandRegistry.java
new file mode 100644
index 000000000..db17420fa
--- /dev/null
+++ b/src/main/java/subway/command/line/LineMenuCommandRegistry.java
@@ -0,0 +1,29 @@
+package subway.command.line;
+
+import java.util.EnumMap;
+import subway.command.Command;
+import subway.command.line.impl.LineDeleteCommand;
+import subway.command.line.impl.LineQueryCommand;
+import subway.command.line.impl.LineRegistrationCommand;
+import subway.service.SubwayService;
+
+public class LineMenuCommandRegistry {
+
+ private final EnumMap commands;
+
+ private LineMenuCommandRegistry(EnumMap commands) {
+ this.commands = commands;
+ }
+
+ public static LineMenuCommandRegistry from(SubwayService service) {
+ EnumMap map = new EnumMap<>(LineMenuOption.class);
+ map.put(LineMenuOption.A, new LineRegistrationCommand(service));
+ map.put(LineMenuOption.B, new LineDeleteCommand(service));
+ map.put(LineMenuOption.C, new LineQueryCommand(service));
+ return new LineMenuCommandRegistry(map);
+ }
+
+ public void execute(LineMenuOption option) {
+ commands.get(option).execute();
+ }
+}
diff --git a/src/main/java/subway/command/line/LineMenuOption.java b/src/main/java/subway/command/line/LineMenuOption.java
new file mode 100644
index 000000000..54a404d84
--- /dev/null
+++ b/src/main/java/subway/command/line/LineMenuOption.java
@@ -0,0 +1,25 @@
+package subway.command.line;
+
+import java.util.Arrays;
+import subway.constant.ErrorMessage;
+
+public enum LineMenuOption {
+ A("1"),
+ B("2"),
+ C("3"),
+ BACK("B");
+
+ private final String code;
+
+ LineMenuOption(String code) {
+ this.code = code;
+ }
+
+ public static LineMenuOption from(String command) {
+ String normalized = command.trim();
+ return Arrays.stream(values())
+ .filter(opt -> opt.code.equals(normalized))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.INVALID_OPTION.getErrorMessage()));
+ }
+}
diff --git a/src/main/java/subway/command/line/impl/LineDeleteCommand.java b/src/main/java/subway/command/line/impl/LineDeleteCommand.java
new file mode 100644
index 000000000..50ed32ee9
--- /dev/null
+++ b/src/main/java/subway/command/line/impl/LineDeleteCommand.java
@@ -0,0 +1,29 @@
+package subway.command.line.impl;
+
+import subway.command.Command;
+import subway.constant.Constant;
+import subway.service.SubwayService;
+import subway.util.InputParser;
+import subway.util.Retry;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public class LineDeleteCommand implements Command {
+
+ private final SubwayService service;
+
+ public LineDeleteCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Retry.retryUntilSuccess(() -> {
+ String readLine = InputView.readLineForDelete();
+ String lineName = InputParser.parseLine(readLine);
+ service.deleteLine(lineName);
+ });
+
+ OutputView.printDelete(Constant.LINE.getMessage());
+ }
+}
diff --git a/src/main/java/subway/command/line/impl/LineQueryCommand.java b/src/main/java/subway/command/line/impl/LineQueryCommand.java
new file mode 100644
index 000000000..dce4aea0f
--- /dev/null
+++ b/src/main/java/subway/command/line/impl/LineQueryCommand.java
@@ -0,0 +1,22 @@
+package subway.command.line.impl;
+
+import java.util.List;
+import subway.command.Command;
+import subway.service.SubwayService;
+import subway.view.OutputView;
+
+public class LineQueryCommand implements Command {
+
+ private final SubwayService service;
+
+ public LineQueryCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ List lines = service.getLines();
+
+ OutputView.printLines(lines);
+ }
+}
diff --git a/src/main/java/subway/command/line/impl/LineRegistrationCommand.java b/src/main/java/subway/command/line/impl/LineRegistrationCommand.java
new file mode 100644
index 000000000..83534c4ca
--- /dev/null
+++ b/src/main/java/subway/command/line/impl/LineRegistrationCommand.java
@@ -0,0 +1,53 @@
+package subway.command.line.impl;
+
+import subway.command.Command;
+import subway.constant.Constant;
+import subway.domain.Line;
+import subway.service.SubwayService;
+import subway.util.InputParser;
+import subway.util.Retry;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public class LineRegistrationCommand implements Command {
+
+ private final SubwayService service;
+
+ public LineRegistrationCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Line line = getLine();
+
+ registerFirstStationName(line);
+ registerLastStationName(line);
+
+ OutputView.printRegistration(Constant.LINE.getMessage());
+ }
+
+ private Line getLine() {
+ return Retry.retryUntilSuccess(() -> {
+ String readLine = InputView.readLineForRegistration();
+ String lineName = InputParser.parseLine(readLine);
+ return service.registerLine(lineName);
+ });
+ }
+
+ private void registerFirstStationName(Line line) {
+ Retry.retryUntilSuccess(() -> {
+ String readStation = InputView.readFirstStationForRegistration();
+ String firstStationName = InputParser.parseStation(readStation);
+ service.registerLineWithStation(line, firstStationName);
+ });
+ }
+
+ private void registerLastStationName(Line line) {
+ Retry.retryUntilSuccess(() -> {
+ String readStation = InputView.readLastStationForRegistration();
+ String lastStationName = InputParser.parseStation(readStation);
+ service.registerLineWithStation(line, lastStationName);
+ });
+ }
+}
diff --git a/src/main/java/subway/command/main/MainMenuCommandRegistry.java b/src/main/java/subway/command/main/MainMenuCommandRegistry.java
new file mode 100644
index 000000000..ad2923704
--- /dev/null
+++ b/src/main/java/subway/command/main/MainMenuCommandRegistry.java
@@ -0,0 +1,36 @@
+package subway.command.main;
+
+import java.util.EnumMap;
+import subway.command.Command;
+import subway.command.line.LineMenuCommandRegistry;
+import subway.command.main.impl.LineCommand;
+import subway.command.main.impl.RouteCommand;
+import subway.command.main.impl.SectionCommand;
+import subway.command.main.impl.StationCommand;
+import subway.command.section.SectionMenuCommandRegistry;
+import subway.command.station.StationMenuCommandRegistry;
+import subway.service.SubwayService;
+
+public class MainMenuCommandRegistry {
+
+ private final EnumMap commands;
+
+ private MainMenuCommandRegistry(EnumMap commands) {
+ this.commands = commands;
+ }
+
+ public static MainMenuCommandRegistry from(SubwayService service, StationMenuCommandRegistry stationRegistry,
+ LineMenuCommandRegistry lineRegistry,
+ SectionMenuCommandRegistry sectionRegistry) {
+ EnumMap map = new EnumMap<>(MainMenuOption.class);
+ map.put(MainMenuOption.A, new StationCommand(stationRegistry));
+ map.put(MainMenuOption.B, new LineCommand(lineRegistry));
+ map.put(MainMenuOption.C, new SectionCommand(sectionRegistry));
+ map.put(MainMenuOption.D, new RouteCommand(service));
+ return new MainMenuCommandRegistry(map);
+ }
+
+ public void execute(MainMenuOption option) {
+ commands.get(option).execute();
+ }
+}
diff --git a/src/main/java/subway/command/main/MainMenuOption.java b/src/main/java/subway/command/main/MainMenuOption.java
new file mode 100644
index 000000000..1eda65b28
--- /dev/null
+++ b/src/main/java/subway/command/main/MainMenuOption.java
@@ -0,0 +1,26 @@
+package subway.command.main;
+
+import java.util.Arrays;
+import subway.constant.ErrorMessage;
+
+public enum MainMenuOption {
+ A("1"),
+ B("2"),
+ C("3"),
+ D("4"),
+ QUIT("Q");
+
+ private final String code;
+
+ MainMenuOption(String code) {
+ this.code = code;
+ }
+
+ public static MainMenuOption from(String command) {
+ String normalized = command.trim();
+ return Arrays.stream(values())
+ .filter(opt -> opt.code.equals(normalized))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.INVALID_OPTION.getErrorMessage()));
+ }
+}
diff --git a/src/main/java/subway/command/main/impl/LineCommand.java b/src/main/java/subway/command/main/impl/LineCommand.java
new file mode 100644
index 000000000..9d3849215
--- /dev/null
+++ b/src/main/java/subway/command/main/impl/LineCommand.java
@@ -0,0 +1,34 @@
+package subway.command.main.impl;
+
+import subway.command.Command;
+import subway.command.line.LineMenuCommandRegistry;
+import subway.command.line.LineMenuOption;
+import subway.util.Retry;
+import subway.view.InputView;
+
+public class LineCommand implements Command {
+
+ private final LineMenuCommandRegistry lineRegistry;
+
+ public LineCommand(LineMenuCommandRegistry lineRegistry) {
+ this.lineRegistry = lineRegistry;
+ }
+
+ @Override
+ public void execute() {
+ LineMenuOption option = getOption();
+
+ if (option.equals(LineMenuOption.BACK)) {
+ return;
+ }
+
+ lineRegistry.execute(option);
+ }
+
+ private static LineMenuOption getOption() {
+ return Retry.retryUntilSuccess(() -> {
+ String selection = InputView.readLineMenuSelection();
+ return LineMenuOption.from(selection);
+ });
+ }
+}
diff --git a/src/main/java/subway/command/main/impl/RouteCommand.java b/src/main/java/subway/command/main/impl/RouteCommand.java
new file mode 100644
index 000000000..a4103eb98
--- /dev/null
+++ b/src/main/java/subway/command/main/impl/RouteCommand.java
@@ -0,0 +1,23 @@
+package subway.command.main.impl;
+
+import java.util.List;
+import java.util.Map;
+import subway.command.Command;
+import subway.service.SubwayService;
+import subway.view.OutputView;
+
+public class RouteCommand implements Command {
+
+ private final SubwayService service;
+
+ public RouteCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Map> routes = service.getRoutes();
+
+ OutputView.printRoutes(routes);
+ }
+}
diff --git a/src/main/java/subway/command/main/impl/SectionCommand.java b/src/main/java/subway/command/main/impl/SectionCommand.java
new file mode 100644
index 000000000..76b67c8da
--- /dev/null
+++ b/src/main/java/subway/command/main/impl/SectionCommand.java
@@ -0,0 +1,34 @@
+package subway.command.main.impl;
+
+import subway.command.Command;
+import subway.command.section.SectionMenuCommandRegistry;
+import subway.command.section.SectionMenuOption;
+import subway.util.Retry;
+import subway.view.InputView;
+
+public class SectionCommand implements Command {
+
+ private final SectionMenuCommandRegistry sectionRegistry;
+
+ public SectionCommand(SectionMenuCommandRegistry sectionRegistry) {
+ this.sectionRegistry = sectionRegistry;
+ }
+
+ @Override
+ public void execute() {
+ SectionMenuOption option = getOption();
+
+ if (option.equals(SectionMenuOption.BACK)) {
+ return;
+ }
+
+ sectionRegistry.execute(option);
+ }
+
+ private static SectionMenuOption getOption() {
+ return Retry.retryUntilSuccess(() -> {
+ String selection = InputView.readSectionMenuSelection();
+ return SectionMenuOption.from(selection);
+ });
+ }
+}
diff --git a/src/main/java/subway/command/main/impl/StationCommand.java b/src/main/java/subway/command/main/impl/StationCommand.java
new file mode 100644
index 000000000..aa5be3209
--- /dev/null
+++ b/src/main/java/subway/command/main/impl/StationCommand.java
@@ -0,0 +1,34 @@
+package subway.command.main.impl;
+
+import subway.command.Command;
+import subway.command.station.StationMenuCommandRegistry;
+import subway.command.station.StationMenuOption;
+import subway.util.Retry;
+import subway.view.InputView;
+
+public class StationCommand implements Command {
+
+ private final StationMenuCommandRegistry stationRegistry;
+
+ public StationCommand(StationMenuCommandRegistry stationRegistry) {
+ this.stationRegistry = stationRegistry;
+ }
+
+ @Override
+ public void execute() {
+ StationMenuOption option = getOption();
+
+ if (option.equals(StationMenuOption.BACK)) {
+ return;
+ }
+
+ stationRegistry.execute(option);
+ }
+
+ private static StationMenuOption getOption() {
+ return Retry.retryUntilSuccess(() -> {
+ String selection = InputView.readStationMenuSelection();
+ return StationMenuOption.from(selection);
+ });
+ }
+}
diff --git a/src/main/java/subway/command/section/SectionMenuCommandRegistry.java b/src/main/java/subway/command/section/SectionMenuCommandRegistry.java
new file mode 100644
index 000000000..178a38c30
--- /dev/null
+++ b/src/main/java/subway/command/section/SectionMenuCommandRegistry.java
@@ -0,0 +1,27 @@
+package subway.command.section;
+
+import java.util.EnumMap;
+import subway.command.Command;
+import subway.command.section.impl.SectionDeleteCommand;
+import subway.command.section.impl.SectionRegistrationCommand;
+import subway.service.SubwayService;
+
+public class SectionMenuCommandRegistry {
+
+ private final EnumMap commands;
+
+ private SectionMenuCommandRegistry(EnumMap commands) {
+ this.commands = commands;
+ }
+
+ public static SectionMenuCommandRegistry from(SubwayService service) {
+ EnumMap map = new EnumMap<>(SectionMenuOption.class);
+ map.put(SectionMenuOption.A, new SectionRegistrationCommand(service));
+ map.put(SectionMenuOption.B, new SectionDeleteCommand(service));
+ return new SectionMenuCommandRegistry(map);
+ }
+
+ public void execute(SectionMenuOption option) {
+ commands.get(option).execute();
+ }
+}
diff --git a/src/main/java/subway/command/section/SectionMenuOption.java b/src/main/java/subway/command/section/SectionMenuOption.java
new file mode 100644
index 000000000..caba82f08
--- /dev/null
+++ b/src/main/java/subway/command/section/SectionMenuOption.java
@@ -0,0 +1,24 @@
+package subway.command.section;
+
+import java.util.Arrays;
+import subway.constant.ErrorMessage;
+
+public enum SectionMenuOption {
+ A("1"),
+ B("2"),
+ BACK("B");
+
+ private final String code;
+
+ SectionMenuOption(String code) {
+ this.code = code;
+ }
+
+ public static SectionMenuOption from(String command) {
+ String normalized = command.trim();
+ return Arrays.stream(values())
+ .filter(opt -> opt.code.equals(normalized))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.INVALID_OPTION.getErrorMessage()));
+ }
+}
diff --git a/src/main/java/subway/command/section/impl/SectionDeleteCommand.java b/src/main/java/subway/command/section/impl/SectionDeleteCommand.java
new file mode 100644
index 000000000..38a5af9a9
--- /dev/null
+++ b/src/main/java/subway/command/section/impl/SectionDeleteCommand.java
@@ -0,0 +1,36 @@
+package subway.command.section.impl;
+
+import subway.command.Command;
+import subway.constant.Constant;
+import subway.domain.Line;
+import subway.service.SubwayService;
+import subway.util.InputParser;
+import subway.util.Retry;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public class SectionDeleteCommand implements Command {
+
+ private final SubwayService service;
+
+ public SectionDeleteCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Line line = Retry.retryUntilSuccess(() -> {
+ String readLine = InputView.readLineForSectionRegistration();
+ String lineName = InputParser.parseLine(readLine);
+ return service.deleteSectionWithLine(lineName);
+ });
+
+ Retry.retryUntilSuccess(() -> {
+ String readStation = InputView.readStationForSectionRegistration();
+ String stationName = InputParser.parseStation(readStation);
+ service.deleteSection(line, stationName);
+ });
+
+ OutputView.printDelete(Constant.SECTION.getMessage());
+ }
+}
diff --git a/src/main/java/subway/command/section/impl/SectionRegistrationCommand.java b/src/main/java/subway/command/section/impl/SectionRegistrationCommand.java
new file mode 100644
index 000000000..e23def1d7
--- /dev/null
+++ b/src/main/java/subway/command/section/impl/SectionRegistrationCommand.java
@@ -0,0 +1,43 @@
+package subway.command.section.impl;
+
+import subway.command.Command;
+import subway.constant.Constant;
+import subway.domain.Line;
+import subway.domain.Station;
+import subway.service.SubwayService;
+import subway.util.InputParser;
+import subway.util.Retry;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public class SectionRegistrationCommand implements Command {
+
+ private final SubwayService service;
+
+ public SectionRegistrationCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Line line = Retry.retryUntilSuccess(() -> {
+ String readLine = InputView.readLineForSectionRegistration();
+ String lineName = InputParser.parseLine(readLine);
+ return service.registerSectionWithLine(lineName);
+ });
+
+ Station station = Retry.retryUntilSuccess(() -> {
+ String readStation = InputView.readStationForSectionRegistration();
+ String stationName = InputParser.parseStation(readStation);
+ return service.validatePossibleAddStation(line, stationName);
+ });
+
+ Retry.retryUntilSuccess(() -> {
+ String readIndex = InputView.readIndexForSectionRegistration();
+ int index = InputParser.parseIndex(readIndex);
+ service.registerRoute(line, station, index);
+ });
+
+ OutputView.printRegistration(Constant.SECTION.getMessage());
+ }
+}
diff --git a/src/main/java/subway/command/station/StationMenuCommandRegistry.java b/src/main/java/subway/command/station/StationMenuCommandRegistry.java
new file mode 100644
index 000000000..67217a53b
--- /dev/null
+++ b/src/main/java/subway/command/station/StationMenuCommandRegistry.java
@@ -0,0 +1,30 @@
+package subway.command.station;
+
+import java.util.EnumMap;
+import subway.command.Command;
+import subway.command.main.impl.StationCommand;
+import subway.command.station.impl.StationDeleteCommand;
+import subway.command.station.impl.StationQueryCommand;
+import subway.command.station.impl.StationRegistrationCommand;
+import subway.service.SubwayService;
+
+public class StationMenuCommandRegistry {
+
+ private final EnumMap commands;
+
+ private StationMenuCommandRegistry(EnumMap commands) {
+ this.commands = commands;
+ }
+
+ public static StationMenuCommandRegistry from(SubwayService service) {
+ EnumMap map = new EnumMap<>(StationMenuOption.class);
+ map.put(StationMenuOption.A, new StationRegistrationCommand(service));
+ map.put(StationMenuOption.B, new StationDeleteCommand(service));
+ map.put(StationMenuOption.C, new StationQueryCommand(service));
+ return new StationMenuCommandRegistry(map);
+ }
+
+ public void execute(StationMenuOption option) {
+ commands.get(option).execute();
+ }
+}
diff --git a/src/main/java/subway/command/station/StationMenuOption.java b/src/main/java/subway/command/station/StationMenuOption.java
new file mode 100644
index 000000000..6c57dec78
--- /dev/null
+++ b/src/main/java/subway/command/station/StationMenuOption.java
@@ -0,0 +1,25 @@
+package subway.command.station;
+
+import java.util.Arrays;
+import subway.constant.ErrorMessage;
+
+public enum StationMenuOption {
+ A("1"),
+ B("2"),
+ C("3"),
+ BACK("B");
+
+ private final String code;
+
+ StationMenuOption(String code) {
+ this.code = code;
+ }
+
+ public static StationMenuOption from(String command) {
+ String normalized = command.trim();
+ return Arrays.stream(values())
+ .filter(opt -> opt.code.equals(normalized))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.INVALID_OPTION.getErrorMessage()));
+ }
+}
diff --git a/src/main/java/subway/command/station/impl/StationDeleteCommand.java b/src/main/java/subway/command/station/impl/StationDeleteCommand.java
new file mode 100644
index 000000000..0823bc178
--- /dev/null
+++ b/src/main/java/subway/command/station/impl/StationDeleteCommand.java
@@ -0,0 +1,29 @@
+package subway.command.station.impl;
+
+import subway.command.Command;
+import subway.constant.Constant;
+import subway.service.SubwayService;
+import subway.util.InputParser;
+import subway.util.Retry;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public class StationDeleteCommand implements Command {
+
+ private final SubwayService service;
+
+ public StationDeleteCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Retry.retryUntilSuccess(() -> {
+ String readStation = InputView.readStationForDelete();
+ String stationName = InputParser.parseStation(readStation);
+ service.deleteStation(stationName);
+ });
+
+ OutputView.printDelete(Constant.STATION.getMessage());
+ }
+}
diff --git a/src/main/java/subway/command/station/impl/StationQueryCommand.java b/src/main/java/subway/command/station/impl/StationQueryCommand.java
new file mode 100644
index 000000000..ebcc30e09
--- /dev/null
+++ b/src/main/java/subway/command/station/impl/StationQueryCommand.java
@@ -0,0 +1,22 @@
+package subway.command.station.impl;
+
+import java.util.List;
+import subway.command.Command;
+import subway.service.SubwayService;
+import subway.view.OutputView;
+
+public class StationQueryCommand implements Command {
+
+ private final SubwayService service;
+
+ public StationQueryCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ List stations = service.getStations();
+
+ OutputView.printStations(stations);
+ }
+}
diff --git a/src/main/java/subway/command/station/impl/StationRegistrationCommand.java b/src/main/java/subway/command/station/impl/StationRegistrationCommand.java
new file mode 100644
index 000000000..bc4ee9108
--- /dev/null
+++ b/src/main/java/subway/command/station/impl/StationRegistrationCommand.java
@@ -0,0 +1,29 @@
+package subway.command.station.impl;
+
+import subway.command.Command;
+import subway.constant.Constant;
+import subway.service.SubwayService;
+import subway.util.InputParser;
+import subway.util.Retry;
+import subway.view.InputView;
+import subway.view.OutputView;
+
+public class StationRegistrationCommand implements Command {
+
+ private final SubwayService service;
+
+ public StationRegistrationCommand(SubwayService service) {
+ this.service = service;
+ }
+
+ @Override
+ public void execute() {
+ Retry.retryUntilSuccess(() -> {
+ String readStation = InputView.readStationForRegistration();
+ String stationName = InputParser.parseStation(readStation);
+ service.registerStation(stationName);
+ });
+
+ OutputView.printRegistration(Constant.STATION.getMessage());
+ }
+}
diff --git a/src/main/java/subway/constant/Constant.java b/src/main/java/subway/constant/Constant.java
new file mode 100644
index 000000000..60d3fbc69
--- /dev/null
+++ b/src/main/java/subway/constant/Constant.java
@@ -0,0 +1,19 @@
+package subway.constant;
+
+public enum Constant {
+
+ STATION("지하철 역"),
+ LINE("지하철 노선"),
+ SECTION("구간"),
+ ;
+
+ private final String message;
+
+ Constant(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/subway/constant/ErrorMessage.java b/src/main/java/subway/constant/ErrorMessage.java
new file mode 100644
index 000000000..0ac47de46
--- /dev/null
+++ b/src/main/java/subway/constant/ErrorMessage.java
@@ -0,0 +1,36 @@
+package subway.constant;
+
+public enum ErrorMessage {
+
+ NUMBER_FORMAT_ERROR("숫자를 입려해야 합니다."),
+
+ INVALID_OPTION("선택할 수 없는 기능입니다."),
+
+ STATION_NAME_SIZE_ERROR("역 이름은 2글자 이상이어야 합니다."),
+ ALREADY_EXIST_STATION("이미 등록된 역 이름입니다."),
+ NO_EXIST_STATION("존재하지 않는 역입니다."),
+ STATION_FORMAT_ERROR("역 이름은 \"역\"으로 끝나야 합니다."),
+ IMPOSSIBLE_DELETE_STATION("노선에 등록된 역은 삭제할 수 없습니다."),
+
+ LINE_NAME_SIZE_ERROR("노선 이름은 2글자 이상이어야 합니다."),
+ ALREADY_EXIST_LINE("이미 등록된 노선 이름입니다"),
+ NO_EXIST_LINE("존재하지 않는 노선입니다."),
+ LINE_FORMAT_ERROR("노선 이름은 \"선\"으로 끝나야 합니다."),
+ ALREADY_EXIST_STATION_IN_LINE("해당 노선에 이미 존재하는 역입니다."),
+
+ INDEX_FORMAT_ERROR("1 이상의 숫자를 입력해야합니다."),
+ MAX_INDEX_ERROR("%d 이하의 순서를 입력해야합니다."),
+ IMPOSSIBLE_DELETE_SECTION("해당 노선의 역은 더이상 삭제할 수 없습니다."),
+ NO_EXIST_STATION_IN_LINE("해당 노선에 존재하지 않는 역입니다.");
+
+ private static final String ERROR_MESSAGE_PREFIX = "[ERROR] ";
+ private final String errorMessage;
+
+ ErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getErrorMessage(Object... args) {
+ return ERROR_MESSAGE_PREFIX + String.format(errorMessage, args);
+ }
+}
diff --git a/src/main/java/subway/controller/SubwayController.java b/src/main/java/subway/controller/SubwayController.java
new file mode 100644
index 000000000..2c3ff08fb
--- /dev/null
+++ b/src/main/java/subway/controller/SubwayController.java
@@ -0,0 +1,53 @@
+package subway.controller;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import subway.command.main.MainMenuCommandRegistry;
+import subway.command.main.MainMenuOption;
+import subway.service.SubwayService;
+import subway.util.Retry;
+import subway.view.InputView;
+
+public class SubwayController {
+
+ private final MainMenuCommandRegistry mainRegistry;
+ private final SubwayService service;
+
+ public SubwayController(MainMenuCommandRegistry mainRegistry, SubwayService service) {
+ this.mainRegistry = mainRegistry;
+ this.service = service;
+ }
+
+ public void run() {
+ setInit();
+
+ while (true) {
+ MainMenuOption option = readMainOption();
+
+ if (option.equals(MainMenuOption.QUIT)) {
+ return;
+ }
+
+ mainRegistry.execute(option);
+ }
+ }
+
+ private MainMenuOption readMainOption() {
+ return Retry.retryUntilSuccess(() -> {
+ String selection = InputView.readMainMenuSelection();
+ return MainMenuOption.from(selection);
+ });
+ }
+
+ private void setInit() {
+ List stations = List.of("교대역", "강남역", "역삼역", "남부터미널역", "양재역", "양재시민의숲역", "매봉역");
+ Map> lines = new LinkedHashMap<>();
+ lines.put("2호선", List.of("교대역", "강남역", "역삼역"));
+ lines.put("3호선", List.of("교대역", "남부터미널역", "양재역", "매봉역"));
+ lines.put("신분당선", List.of("강남역", "양재역", "양재시민의숲역"));
+
+ service.setStations(stations);
+ service.setLines(lines);
+ }
+}
diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java
index f4d738d5a..d091dbb02 100644
--- a/src/main/java/subway/domain/Line.java
+++ b/src/main/java/subway/domain/Line.java
@@ -1,15 +1,28 @@
package subway.domain;
-public class Line {
- private String name;
+import java.util.Objects;
+import subway.constant.ErrorMessage;
- public Line(String name) {
- this.name = name;
+public record Line(String name) {
+
+ public static Line from(String name) {
+ validate(name);
+ return new Line(name);
+ }
+
+ private static void validate(String name) {
+ if (name.length() < 2) {
+ throw new IllegalArgumentException(ErrorMessage.LINE_NAME_SIZE_ERROR.getErrorMessage());
+ }
}
- public String getName() {
- return name;
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ Line line = (Line) object;
+ return Objects.equals(name, line.name);
}
- // 추가 기능 구현
}
diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/domain/LineRepository.java
index 49132ddb6..14de1bce5 100644
--- a/src/main/java/subway/domain/LineRepository.java
+++ b/src/main/java/subway/domain/LineRepository.java
@@ -4,6 +4,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import subway.constant.ErrorMessage;
public class LineRepository {
private static final List lines = new ArrayList<>();
@@ -13,10 +14,23 @@ public static List lines() {
}
public static void addLine(Line line) {
+ if (lines().contains(line)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_LINE.getErrorMessage());
+ }
lines.add(line);
}
- public static boolean deleteLineByName(String name) {
- return lines.removeIf(line -> Objects.equals(line.getName(), name));
+ public static void deleteLine(Line deletedLine) {
+ if (!lines().contains(deletedLine)) {
+ throw new IllegalArgumentException(ErrorMessage.NO_EXIST_LINE.getErrorMessage());
+ }
+ lines.remove(deletedLine);
+ }
+
+ public static Line getLine(Line deletedLine) {
+ return lines.stream()
+ .filter(line -> line.equals(deletedLine))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.NO_EXIST_LINE.getErrorMessage()));
}
}
diff --git a/src/main/java/subway/domain/RouteRepository.java b/src/main/java/subway/domain/RouteRepository.java
new file mode 100644
index 000000000..460e5842b
--- /dev/null
+++ b/src/main/java/subway/domain/RouteRepository.java
@@ -0,0 +1,100 @@
+package subway.domain;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import subway.constant.ErrorMessage;
+
+public class RouteRepository {
+
+ private static final Map> routes = new LinkedHashMap<>();
+
+ public static void addStations(Line line, List stations) {
+ if (routes.containsKey(line)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_LINE.getErrorMessage());
+ }
+ routes.put(line, stations);
+ }
+
+ public static void addLine(Line line) {
+ if (routes.containsKey(line)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_LINE.getErrorMessage());
+ }
+ routes.put(line, new ArrayList<>());
+ }
+
+ public static boolean contains(Station station) {
+ for (List value : routes.values()) {
+ if (value.contains(station)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(Line line) {
+ return routes.containsKey(line);
+ }
+
+ public static boolean contains(Line line, Station station) {
+ return routes.get(line).contains(station);
+ }
+
+ public static void addStation(Line line, Station station) {
+ List stations = routes.get(line);
+ if (stations.contains(station)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_STATION_IN_LINE.getErrorMessage());
+ }
+ stations.add(station);
+ }
+
+ public static void addStation(Line line, Station station, int index) {
+ List stations = routes.get(line);
+
+ if (index > stations.size() + 1) {
+ throw new IllegalArgumentException(ErrorMessage.MAX_INDEX_ERROR.getErrorMessage(stations.size() + 1));
+ }
+
+ if (stations.contains(station)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_STATION_IN_LINE.getErrorMessage());
+ }
+
+ stations.add(index - 1, station);
+ }
+
+ public static void deleteLine(Line line) {
+ Set lines = routes.keySet();
+ if (!lines.contains(line)) {
+ throw new IllegalArgumentException(ErrorMessage.NO_EXIST_LINE.getErrorMessage());
+ }
+ routes.remove(line);
+ }
+
+ public static void validateDeleteLine(Line line) {
+ List stations = routes.get(line);
+ if (stations.size()<=2) {
+ throw new IllegalArgumentException(ErrorMessage.IMPOSSIBLE_DELETE_SECTION.getErrorMessage());
+ }
+ }
+
+ public static void deleteSection(Line line, Station station) {
+ List stations = routes.get(line);
+ if (!stations.contains(station)) {
+ throw new IllegalArgumentException(ErrorMessage.NO_EXIST_STATION_IN_LINE.getErrorMessage());
+ }
+ stations.remove(station);
+ }
+
+ public static Map> getRoutes() {
+ Map> resultRoutes = new LinkedHashMap<>();
+ for (Line line : routes.keySet()) {
+ List stations = routes.get(line).stream()
+ .map(Station::name)
+ .toList();
+ resultRoutes.put(line.name(), stations);
+ }
+ return resultRoutes;
+ }
+}
diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java
index bdb142590..5695c43b9 100644
--- a/src/main/java/subway/domain/Station.java
+++ b/src/main/java/subway/domain/Station.java
@@ -1,15 +1,30 @@
package subway.domain;
-public class Station {
- private String name;
+import java.util.Objects;
+import subway.constant.ErrorMessage;
- public Station(String name) {
- this.name = name;
+public record Station(String name) {
+
+ public static Station from(String name) {
+ validate(name);
+ return new Station(name);
}
- public String getName() {
- return name;
+ private static void validate(String name) {
+ if (name.length() < 2) {
+ throw new IllegalArgumentException(ErrorMessage.STATION_NAME_SIZE_ERROR.getErrorMessage());
+ }
}
// 추가 기능 구현
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ Station station = (Station) object;
+ return Objects.equals(name, station.name);
+ }
+
}
diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java
index b7245c0f3..197c6943f 100644
--- a/src/main/java/subway/domain/StationRepository.java
+++ b/src/main/java/subway/domain/StationRepository.java
@@ -3,7 +3,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
+import subway.constant.ErrorMessage;
public class StationRepository {
private static final List stations = new ArrayList<>();
@@ -13,10 +13,24 @@ public static List stations() {
}
public static void addStation(Station station) {
+ if (stations.contains(station)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_STATION.getErrorMessage());
+ }
stations.add(station);
}
- public static boolean deleteStation(String name) {
- return stations.removeIf(station -> Objects.equals(station.getName(), name));
+ public static void deleteStation(Station deleteStation) {
+ if (RouteRepository.contains(deleteStation)) {
+ throw new IllegalArgumentException(ErrorMessage.IMPOSSIBLE_DELETE_STATION.getErrorMessage());
+ }
+
+ stations.removeIf(station -> station.equals(deleteStation));
+ }
+
+ public static Station getStation(String stationName) {
+ return stations.stream()
+ .filter(station -> station.name().equals(stationName))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.NO_EXIST_STATION.getErrorMessage()));
}
}
diff --git a/src/main/java/subway/service/SubwayService.java b/src/main/java/subway/service/SubwayService.java
new file mode 100644
index 000000000..a840412ae
--- /dev/null
+++ b/src/main/java/subway/service/SubwayService.java
@@ -0,0 +1,109 @@
+package subway.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import subway.constant.ErrorMessage;
+import subway.domain.Line;
+import subway.domain.LineRepository;
+import subway.domain.RouteRepository;
+import subway.domain.Station;
+import subway.domain.StationRepository;
+
+public class SubwayService {
+
+ public void setStations(List stations) {
+ for (String stationName : stations) {
+ StationRepository.addStation(Station.from(stationName));
+ }
+ }
+
+ public void setLines(Map> lines) {
+ for (String lineName : lines.keySet()) {
+ Line line = Line.from(lineName);
+ LineRepository.addLine(line);
+
+ List stations = new ArrayList<>();
+ for (String stationName : lines.get(lineName)) {
+ stations.add(StationRepository.getStation(stationName));
+ }
+ RouteRepository.addStations(line, stations);
+ }
+ }
+
+ public void registerStation(String stationName) {
+ StationRepository.addStation(Station.from(stationName));
+ }
+
+ public void deleteStation(String stationName) {
+ Station station = StationRepository.getStation(stationName);
+ StationRepository.deleteStation(station);
+ }
+
+ public Line registerLine(String lineName) {
+ Line line = Line.from(lineName);
+
+ LineRepository.addLine(line);
+ RouteRepository.addLine(line);
+
+ return line;
+ }
+
+ public void registerLineWithStation(Line line, String stationName) {
+ Station station = Station.from(stationName);
+ RouteRepository.addStation(line, station);
+ }
+
+ public void deleteLine(String lineName) {
+ Line line = Line.from(lineName);
+ LineRepository.deleteLine(line);
+ RouteRepository.deleteLine(line);
+ }
+
+ public List getStations() {
+ return StationRepository.stations().stream()
+ .map(Station::name)
+ .toList();
+ }
+
+ public List getLines() {
+ return LineRepository.lines().stream()
+ .map(Line::name)
+ .toList();
+ }
+
+ public Line registerSectionWithLine(String lineName) {
+ Line line = Line.from(lineName);
+ if (!RouteRepository.contains(line)) {
+ throw new IllegalArgumentException(ErrorMessage.NO_EXIST_LINE.getErrorMessage());
+ }
+ return line;
+ }
+
+ public Station validatePossibleAddStation(Line line, String stationName) {
+ Station station = StationRepository.getStation(stationName);
+ if (RouteRepository.contains(line, station)) {
+ throw new IllegalArgumentException(ErrorMessage.ALREADY_EXIST_STATION_IN_LINE.getErrorMessage());
+ }
+ return station;
+ }
+
+ public void registerRoute(Line line, Station station, int index) {
+ RouteRepository.addStation(line, station, index);
+ }
+
+ public Line deleteSectionWithLine(String lineName) {
+ Line line = LineRepository.getLine(Line.from(lineName));
+ RouteRepository.validateDeleteLine(line);
+ return line;
+ }
+
+ public void deleteSection(Line line, String stationName) {
+ Station station = StationRepository.getStation(stationName);
+ RouteRepository.deleteSection(line, station);
+ }
+
+ public Map> getRoutes() {
+ return RouteRepository.getRoutes();
+ }
+}
diff --git a/src/main/java/subway/util/InputParser.java b/src/main/java/subway/util/InputParser.java
new file mode 100644
index 000000000..e488add04
--- /dev/null
+++ b/src/main/java/subway/util/InputParser.java
@@ -0,0 +1,29 @@
+package subway.util;
+
+public final class InputParser {
+
+ private InputParser() {
+ }
+
+ public static String parseStation(String station) {
+ station = station.strip();
+
+ Validator.validateStationFormat(station);
+ return station;
+ }
+
+ public static String parseLine(String line) {
+ line = line.strip();
+
+ Validator.validateLineFormat(line);
+ return line;
+ }
+
+ public static int parseIndex(String readIndex) {
+ readIndex = readIndex.strip();
+
+ Validator.validateIndexFormat(readIndex);
+
+ return NumberConvertor.convertToNumber(readIndex);
+ }
+}
diff --git a/src/main/java/subway/util/NumberConvertor.java b/src/main/java/subway/util/NumberConvertor.java
new file mode 100644
index 000000000..2a40d6d4a
--- /dev/null
+++ b/src/main/java/subway/util/NumberConvertor.java
@@ -0,0 +1,14 @@
+package subway.util;
+
+import subway.constant.ErrorMessage;
+
+public final class NumberConvertor {
+
+ public static Integer convertToNumber(String input) {
+ try {
+ return Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(ErrorMessage.NUMBER_FORMAT_ERROR.getErrorMessage());
+ }
+ }
+}
diff --git a/src/main/java/subway/util/Retry.java b/src/main/java/subway/util/Retry.java
new file mode 100644
index 000000000..a20c019af
--- /dev/null
+++ b/src/main/java/subway/util/Retry.java
@@ -0,0 +1,30 @@
+package subway.util;
+
+import java.util.function.Supplier;
+import subway.view.OutputView;
+
+public final class Retry {
+
+ private Retry() {}
+
+ public static T retryUntilSuccess(Supplier action) {
+ while (true) {
+ try {
+ return action.get();
+ } catch (IllegalArgumentException e) {
+ OutputView.printErrorMessage(e);
+ }
+ }
+ }
+
+ public static void retryUntilSuccess(Runnable action) {
+ while (true) {
+ try {
+ action.run();
+ return;
+ } catch (IllegalArgumentException e) {
+ OutputView.printErrorMessage(e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/subway/util/Validator.java b/src/main/java/subway/util/Validator.java
new file mode 100644
index 000000000..0a4c43eb4
--- /dev/null
+++ b/src/main/java/subway/util/Validator.java
@@ -0,0 +1,31 @@
+package subway.util;
+
+import subway.constant.ErrorMessage;
+
+public final class Validator {
+
+ private static final String NUMBER_FORMAT = "\\d+";
+ private static final String STATION_FORMAT = ".*역$";
+ private static final String LINE_FORMAT = ".*선$";
+
+ private Validator() {
+ }
+
+ public static void validateStationFormat(String station) {
+ if (!station.matches(STATION_FORMAT)) {
+ throw new IllegalArgumentException(ErrorMessage.STATION_FORMAT_ERROR.getErrorMessage());
+ }
+ }
+
+ public static void validateLineFormat(String line) {
+ if (!line.matches(LINE_FORMAT)) {
+ throw new IllegalArgumentException(ErrorMessage.LINE_FORMAT_ERROR.getErrorMessage());
+ }
+ }
+
+ public static void validateIndexFormat(String readIndex) {
+ if (readIndex.matches("0") || !readIndex.matches(NUMBER_FORMAT)) {
+ throw new IllegalArgumentException(ErrorMessage.INDEX_FORMAT_ERROR.getErrorMessage());
+ }
+ }
+}
diff --git a/src/main/java/subway/view/InputView.java b/src/main/java/subway/view/InputView.java
new file mode 100644
index 000000000..091c192d5
--- /dev/null
+++ b/src/main/java/subway/view/InputView.java
@@ -0,0 +1,93 @@
+package subway.view;
+
+import java.util.Scanner;
+
+public class InputView {
+
+ private static final Scanner scanner = new Scanner(System.in);
+
+ public static String readMainMenuSelection() {
+ System.out.println("\n## 메인 화면\n"
+ + "1. 역 관리\n"
+ + "2. 노선 관리\n"
+ + "3. 구간 관리\n"
+ + "4. 지하철 노선도 출력\n"
+ + "Q. 종료");
+ System.out.println("\n## 원하는 기능을 선택하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readStationMenuSelection() {
+ System.out.println("\n## 역 관리 화면\n"
+ + "1. 역 등록\n"
+ + "2. 역 삭제\n"
+ + "3. 역 조회\n"
+ + "B. 돌아가기");
+ System.out.println("\n## 원하는 기능을 선택하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readLineMenuSelection() {
+ System.out.println("\n## 노선 관리 화면\n"
+ + "1. 노선 등록\n"
+ + "2. 노선 삭제\n"
+ + "3. 노선 조회\n"
+ + "B. 돌아가기");
+ System.out.println("\n## 원하는 기능을 선택하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readSectionMenuSelection() {
+ System.out.println("\n## 구간 관리 화면\n"
+ + "1. 구간 등록\n"
+ + "2. 구간 삭제\n"
+ + "B. 돌아가기");
+ System.out.println("\n## 원하는 기능을 선택하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readStationForRegistration() {
+ System.out.println("\n## 등록할 역 이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readStationForDelete() {
+ System.out.println("\n## 삭제할 역 이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readLineForRegistration() {
+ System.out.println("\n## 등록할 노선 이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readFirstStationForRegistration() {
+ System.out.println("\n## 등록할 노선의 상행 종점역 이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readLastStationForRegistration() {
+ System.out.println("\n## 등록할 노선의 하행 종점역 이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readLineForDelete() {
+ System.out.println("\n## 삭제할 노선 이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readLineForSectionRegistration() {
+ System.out.println("\n## 노선을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readStationForSectionRegistration() {
+ System.out.println("\n## 역이름을 입력하세요.");
+ return scanner.nextLine();
+ }
+
+ public static String readIndexForSectionRegistration() {
+ System.out.println("\n## 순서를 입력하세요.");
+ 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..547d06d6d
--- /dev/null
+++ b/src/main/java/subway/view/OutputView.java
@@ -0,0 +1,48 @@
+package subway.view;
+
+import java.util.List;
+import java.util.Map;
+
+public class OutputView {
+
+ private OutputView() {
+ }
+
+ public static void printRegistration(String name) {
+ System.out.printf("\n[INFO] %s이 등록되었습니다.\n", name);
+ }
+
+ public static void printDelete(String name) {
+ System.out.printf("\n[INFO] %s이 삭제되었습니다.\n", name);
+ }
+
+ public static void printStations(List stations) {
+ System.out.println("\n## 역 목록");
+ for (String station : stations) {
+ System.out.println("[INFO] " + station);
+ }
+ }
+
+ public static void printLines(List lines) {
+ System.out.println("\n## 노선 목록");
+ for (String line : lines) {
+ System.out.println("[INFO] " + line);
+ }
+ }
+
+ public static void printRoutes(Map> routes) {
+ System.out.println("\n## 지하철 노선도");
+ for (String line : routes.keySet()) {
+ System.out.println("[INFO] " + line);
+ System.out.println("[INFO] ---");
+ for (String station : routes.get(line)) {
+ System.out.println("[INFO] " + station);
+ }
+ System.out.println();
+ }
+ }
+
+ public static void printErrorMessage(IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ }
+}