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()); + } +}