diff --git a/src/main/java/docs/README.md b/src/main/java/docs/README.md new file mode 100644 index 000000000..b98faba7f --- /dev/null +++ b/src/main/java/docs/README.md @@ -0,0 +1,33 @@ +# 기능정의 + +## 도메인 + +### 지하철(Staion) + +- [x] 지하철역 등록 기능 + - [x] [ERROR] 지하철역은 두글자 이상이여야 한다. + - [x] [ERROR] 지하철역은 한국어만 가능합니다. + - [x] [ERROR] 중복된 역 이름은 등록할 수 없습니다. +- [x] 지하철역 삭제 기능 + - [x] [ERROR] 존재하는 지하철역만 삭제 가능합니다. + - [x] [ERROR] 노선에 등록된 역은 삭제가 안됩니다. +- [x] 지하철역 전체 조회 기능 + +### 노선(Line) + +- [x] 노선 등록 기능 + - [x] 등록시 상행 종점과 하행 종점을 입력받는다. + - [x] [ERROR] 노선의 이름은 두글자 이상이여야 한다. +- [x] 노선 삭제 기능 + - [x] [ERROR] 존재하는 노선만 삭제 가능합니다. +- [x] 노선 목록 조회 기능 +- [x] 노선에 등록된 역 조회 기능 + - [x] 노선의 상행 종점부터 하행 종점까지 순서대로 목록을 조회한다. + +### 구간 + +- [x] 존재하는 노선에 역을 추가한다. + - [x] [ERROR] 등록된 역만 노선에 추가가 가능하다. +- [x] 노선에 역을 삭제한다. + - [x] [ERROR] 노선에 역이 2개 이하면 삭제할 수 없다. + - [x] [ERROR] 노선에 존재하는 역만 삭제할 수 있다. \ No newline at end of file diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java index 0bcf786cc..0b5e6cdba 100644 --- a/src/main/java/subway/Application.java +++ b/src/main/java/subway/Application.java @@ -1,10 +1,10 @@ package subway; -import java.util.Scanner; +import subway.common.AppConfig; public class Application { public static void main(String[] args) { - final Scanner scanner = new Scanner(System.in); - // TODO: 프로그램 구현 + + AppConfig.getInstance().subwayController().start(); } } diff --git a/src/main/java/subway/common/AppConfig.java b/src/main/java/subway/common/AppConfig.java new file mode 100644 index 000000000..505ff4838 --- /dev/null +++ b/src/main/java/subway/common/AppConfig.java @@ -0,0 +1,51 @@ +package subway.common; + +import subway.controller.SubwayController; +import subway.domain.LineRepository; +import subway.domain.StationRepository; +import subway.domain.Subway; +import subway.service.SubwayService; +import subway.view.InputView; +import subway.view.OutputView; + +public class AppConfig { + + private static AppConfig instance; + + private AppConfig() { + } + + public static AppConfig getInstance() { + if (instance == null) { + instance = new AppConfig(); + } + return instance; + } + + public SubwayController subwayController() { + return new SubwayController(outputView(), inputView(), subwayService()); + } + + private OutputView outputView() { + return new OutputView(); + } + private InputView inputView() { + return new InputView(); + } + + private SubwayService subwayService() { + return new SubwayService(subway(), lineRepository(), stationRepository()); + } + + private Subway subway() { + return new Subway(); + } + + private LineRepository lineRepository() { + return new LineRepository(); + } + + private StationRepository stationRepository() { + return new StationRepository(); + } +} diff --git a/src/main/java/subway/common/ExceptionHandler.java b/src/main/java/subway/common/ExceptionHandler.java new file mode 100644 index 000000000..b22d66718 --- /dev/null +++ b/src/main/java/subway/common/ExceptionHandler.java @@ -0,0 +1,13 @@ +package subway.common; + +public class ExceptionHandler { + + public static void retryOnException(final Runnable runnable) { + try { + runnable.run(); + } catch (final IllegalArgumentException | IllegalStateException e) { + System.out.println(e.getMessage()); + retryOnException(runnable); + } + } +} diff --git a/src/main/java/subway/constant/LineCommand.java b/src/main/java/subway/constant/LineCommand.java new file mode 100644 index 000000000..eb124567e --- /dev/null +++ b/src/main/java/subway/constant/LineCommand.java @@ -0,0 +1,25 @@ +package subway.constant; + +import java.util.Arrays; +import subway.constant.message.ErrorMessage; + +public enum LineCommand { + + SAVE("1"), + DELETE("2"), + SELECT("3"), + BACK("B"); + + private final String command; + + LineCommand(final String command) { + this.command = command; + } + + public static LineCommand from(final String input) { + return Arrays.stream(LineCommand.values()) + .filter(e -> e.command.equals(input)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 버튼입니다.")); + } +} diff --git a/src/main/java/subway/constant/MainCommand.java b/src/main/java/subway/constant/MainCommand.java new file mode 100644 index 000000000..fddd95d19 --- /dev/null +++ b/src/main/java/subway/constant/MainCommand.java @@ -0,0 +1,25 @@ +package subway.constant; + +import java.util.Arrays; +import subway.constant.message.ErrorMessage; + +public enum MainCommand { + STATION_MANAGE("1"), + LINE_MANAGE("2"), + SECTION_MANAGE("3"), + PRINT_SUBWAY("4"), + QUIT("Q"); + + private final String command; + + MainCommand(final String command) { + this.command = command; + } + + public static MainCommand from(final String input) { + return Arrays.stream(MainCommand.values()) + .filter(e -> e.command.equalsIgnoreCase(input)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 버튼입니다.")); + } +} diff --git a/src/main/java/subway/constant/SectionCommand.java b/src/main/java/subway/constant/SectionCommand.java new file mode 100644 index 000000000..43eb5e251 --- /dev/null +++ b/src/main/java/subway/constant/SectionCommand.java @@ -0,0 +1,23 @@ +package subway.constant; + +import java.util.Arrays; +import subway.constant.message.ErrorMessage; + +public enum SectionCommand { + SAVE("1"), + DELETE("2"), + BACK("B"); + + private final String command; + + SectionCommand(final String command) { + this.command = command; + } + + public static SectionCommand from(final String input) { + return Arrays.stream(SectionCommand.values()) + .filter(e -> e.command.equals(input)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 버튼입니다.")); + } +} diff --git a/src/main/java/subway/constant/StationCommand.java b/src/main/java/subway/constant/StationCommand.java new file mode 100644 index 000000000..dcf75ef7b --- /dev/null +++ b/src/main/java/subway/constant/StationCommand.java @@ -0,0 +1,24 @@ +package subway.constant; + +import java.util.Arrays; +import subway.constant.message.ErrorMessage; + +public enum StationCommand { + SAVE("1"), + DELETE("2"), + SELECT("3"), + BACK("B"); + + private final String command; + + StationCommand(final String command) { + this.command = command; + } + + public static StationCommand from(final String input) { + return Arrays.stream(StationCommand.values()) + .filter(e -> e.command.equals(input)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 버튼입니다.")); + } +} diff --git a/src/main/java/subway/constant/message/ErrorMessage.java b/src/main/java/subway/constant/message/ErrorMessage.java new file mode 100644 index 000000000..f7b5a6639 --- /dev/null +++ b/src/main/java/subway/constant/message/ErrorMessage.java @@ -0,0 +1,15 @@ +package subway.constant.message; + +public enum ErrorMessage { + ERROR_PREFIX("[ERROR] "); + + private final String message; + + ErrorMessage(final String message) { + this.message = message; + } + + public String toMessage() { + return message; + } +} diff --git a/src/main/java/subway/constant/message/Processmessage.java b/src/main/java/subway/constant/message/Processmessage.java new file mode 100644 index 000000000..aed9eb789 --- /dev/null +++ b/src/main/java/subway/constant/message/Processmessage.java @@ -0,0 +1,16 @@ +package subway.constant.message; + +public enum Processmessage { + SUCCESS_PREFIX("[INFO] "), + ; + + private final String message; + + Processmessage(final String message) { + this.message = message; + } + + public String toMessage() { + return message; + } +} diff --git a/src/main/java/subway/controller/SubwayController.java b/src/main/java/subway/controller/SubwayController.java new file mode 100644 index 000000000..2bd1747cc --- /dev/null +++ b/src/main/java/subway/controller/SubwayController.java @@ -0,0 +1,127 @@ +package subway.controller; + +import java.util.List; +import subway.common.ExceptionHandler; +import subway.constant.LineCommand; +import subway.constant.MainCommand; +import subway.constant.SectionCommand; +import subway.constant.StationCommand; +import subway.domain.Subway; +import subway.service.SubwayService; +import subway.view.InputView; +import subway.view.OutputView; + +public class SubwayController { + + private final OutputView outputView; + private final InputView inputView; + + public SubwayController( + final OutputView outputView, + final InputView inputView, + final SubwayService subwayService + ) { + this.outputView = outputView; + this.inputView = inputView; + this.subwayService = subwayService; + } + + private final SubwayService subwayService; + + public void start() { + subwayService.initializeSubway(); + ExceptionHandler.retryOnException(this::startSubway); + } + + private void startSubway() { + while (true) { + outputView.printMainView(); + final String command = inputView.readCommand(); + final MainCommand mainCommand = MainCommand.from(command); + if (mainCommand.equals(MainCommand.STATION_MANAGE)) { + manageStation(); + } + if (mainCommand.equals(MainCommand.LINE_MANAGE)) { + manageLine(); + } + if (mainCommand.equals(MainCommand.SECTION_MANAGE)) { + manageSection(); + } + if (mainCommand.equals(MainCommand.PRINT_SUBWAY)) { + final Subway subway = subwayService.getSubway(); + outputView.printSubway(subway); + } + } + } + + private void manageSection() { + while (true) { + outputView.printSectionManageView(); + final String sectionManageCommand = inputView.readCommand(); + final SectionCommand sectionCommand = SectionCommand.from(sectionManageCommand); + if (sectionCommand.equals(SectionCommand.SAVE)) { + final String inputLine = inputView.readLineName(); + final String inputStation = inputView.readStationName(); + final String inputIndex = inputView.readStationInputIndex(); + subwayService.addSubwayStation(inputLine, inputStation, inputIndex); + } + if (sectionCommand.equals(SectionCommand.DELETE)) { + final String inputLine = inputView.readDeleteLineName(); + subwayService.deleteSubwayLine(inputLine); + } + if (sectionCommand.equals(SectionCommand.BACK)) { + break; + } + } + } + + private void manageLine() { + while (true) { + outputView.printLineManageView(); + final String lineManageCommand = inputView.readCommand(); + final LineCommand lineCommand = LineCommand.from(lineManageCommand); + if (lineCommand.equals(LineCommand.SAVE)) { + final String inputName = inputView.readSaveLineName(); + final String inputLineStartStationName = inputView.readLineStartStationName(); + final String inputLineEndStationName = inputView.readLineEndStatationName(); + subwayService.saveLine(inputName, inputLineStartStationName, inputLineEndStationName); + outputView.printSubwayAddSuccess(); + } + if (lineCommand.equals(LineCommand.DELETE)) { + final String inputLineName = inputView.readDeleteLineName(); + subwayService.deleteLine(inputLineName); + outputView.printSubwayDeleteSuccess(); + } + if (lineCommand.equals(LineCommand.SELECT)) { + List lineNames = subwayService.getAllLine(); + outputView.printLines(lineNames); + } + if (lineCommand.equals(LineCommand.BACK)) { + break; + } + } + } + + private void manageStation() { + while (true) { + outputView.printStationManageView(); + final String stationManageCommand = inputView.readCommand(); + final StationCommand stationCommand = StationCommand.from(stationManageCommand); + if (stationCommand.equals(StationCommand.SAVE)) { + final String input = inputView.readSaveStationName(); + subwayService.saveStation(input); + } + if (stationCommand.equals(StationCommand.DELETE)) { + final String input = inputView.readDeleteStationName(); + subwayService.deleteStation(input); + } + if (stationCommand.equals(StationCommand.SELECT)) { + final List stationsName = subwayService.getAllStations(); + outputView.printStations(stationsName); + } + if (stationCommand.equals(StationCommand.BACK)) { + break; + } + } + } +} diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java index f4d738d5a..2a808c4aa 100644 --- a/src/main/java/subway/domain/Line.java +++ b/src/main/java/subway/domain/Line.java @@ -1,15 +1,54 @@ package subway.domain; +import java.util.Objects; +import subway.constant.message.ErrorMessage; + public class Line { - private String name; - public Line(String name) { + private static final int MIN_NAME_LENGTH = 2; + private static final String NAME_SUFFIX = "선"; + private final String name; + + public Line(final String name) { + validateNameSuffix(name); + validateNameLenght(name); this.name = name; } + private void validateNameSuffix(final String input) { + if (!input.endsWith(NAME_SUFFIX)) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "노선은 선으로 끝나야 합니다"); + } + } + + private void validateNameLenght(final String input) { + if (input.length() < MIN_NAME_LENGTH) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "이름은 2글자 이상이여야 합니다."); + } + } + public String getName() { return name; } - // 추가 기능 구현 + public boolean isEqualsByName(final String input) { + return Objects.equals(name, input); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Line line = (Line) o; + return Objects.equals(getName(), line.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(getName()); + } } diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/domain/LineRepository.java index 49132ddb6..b21e9402c 100644 --- a/src/main/java/subway/domain/LineRepository.java +++ b/src/main/java/subway/domain/LineRepository.java @@ -3,20 +3,37 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; +import subway.constant.message.ErrorMessage; public class LineRepository { - private static final List lines = new ArrayList<>(); + private final List lines = new ArrayList<>(); - public static List lines() { + public List getAll() { return Collections.unmodifiableList(lines); } - public static void addLine(Line line) { + public void save(final Line line) { + if (lines.stream() + .anyMatch(element -> element.equals(line))) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "중복된 호선은 입력될 수 없습니다."); + } lines.add(line); } - public static boolean deleteLineByName(String name) { - return lines.removeIf(line -> Objects.equals(line.getName(), name)); + public void delete(final String name) { + if (!deleteLine(name)) { + throw new IllegalStateException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 호선을 입력했습니다."); + } + } + + private boolean deleteLine(final String name) { + return lines.removeIf(station -> station.isEqualsByName(name)); + } + + public Line getByName(final String name) { + return lines.stream() + .filter(e -> e.isEqualsByName(name)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("이름에 해당하는 노선이 없습니다.")); } } diff --git a/src/main/java/subway/domain/Station.java b/src/main/java/subway/domain/Station.java index bdb142590..c9f6d8ffe 100644 --- a/src/main/java/subway/domain/Station.java +++ b/src/main/java/subway/domain/Station.java @@ -1,9 +1,20 @@ package subway.domain; +import java.util.Objects; +import java.util.regex.Pattern; +import subway.constant.message.ErrorMessage; + public class Station { - private String name; - public Station(String name) { + private final Pattern NAME_PATTERN = Pattern.compile("^[가-힣]+$"); + private static final String NAME_SUFFIX = "역"; + private static final int MIN_NAME_LENGTH = 2; + private final String name; + + public Station(final String name) { + validateNameLength(name); + validateNameSuffix(name); + validateNameRegex(name); this.name = name; } @@ -11,5 +22,42 @@ public String getName() { return name; } - // 추가 기능 구현 + private void validateNameSuffix(final String input) { + if (!input.endsWith(NAME_SUFFIX)) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "지하철역은 역으로 끝나야 합니다"); + } + } + + private void validateNameLength(final String input) { + if (input.length() < MIN_NAME_LENGTH) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "이름은 2글자 이상이여야 합니다."); + } + } + + private void validateNameRegex(final String input) { + if (!NAME_PATTERN.matcher(input).matches()) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "한글만 입력 가능합니다."); + } + } + + public boolean isEqualsByName(final String input) { + return Objects.equals(name, input); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Station station = (Station) o; + return Objects.equals(getName(), station.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(getName()); + } } diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java index b7245c0f3..129a65cf5 100644 --- a/src/main/java/subway/domain/StationRepository.java +++ b/src/main/java/subway/domain/StationRepository.java @@ -3,20 +3,37 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; +import subway.constant.message.ErrorMessage; public class StationRepository { - private static final List stations = new ArrayList<>(); + private final List stations = new ArrayList<>(); - public static List stations() { + public List getAll() { return Collections.unmodifiableList(stations); } - public static void addStation(Station station) { + public void save(final Station station) { + if (stations.stream() + .anyMatch(element -> element.equals(station))) { + throw new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "중복된 지하철역은 입력될 수 없습니다."); + } stations.add(station); } - public static boolean deleteStation(String name) { - return stations.removeIf(station -> Objects.equals(station.getName(), name)); + public void delete(final String name) { + if (!deleteStation(name)) { + throw new IllegalStateException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 역을 입력했습니다."); + } + } + + private boolean deleteStation(final String name) { + return stations.removeIf(station -> station.isEqualsByName(name)); + } + + public Station getByName(final String name) { + return stations.stream() + .filter(e -> e.isEqualsByName(name)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("이름에 해당하는 역이 없습니다.")); } } diff --git a/src/main/java/subway/domain/Subway.java b/src/main/java/subway/domain/Subway.java new file mode 100644 index 000000000..b0e3dd726 --- /dev/null +++ b/src/main/java/subway/domain/Subway.java @@ -0,0 +1,40 @@ +package subway.domain; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Subway { + + private static final int INDEX_ADDER = 1; + private final Map> subway; + + public Subway() { + this.subway = new HashMap<>(); + } + + public Map> getSubway() { + return subway; + } + + public void addNewSubwayLine(final Line line, final List stations) { + subway.put(line, stations); + } + + public void deleteSubwayByLine(final Line line) { + subway.remove(line); + } + + public void addStationByLine(final Line line, final Station station, final int stationIndex) { + List stations = subway.get(line); + if (stations.stream().anyMatch(e -> e.equals(station))) { + throw new IllegalArgumentException("중복된 값을 저장할 수 없습니다."); + } + stations.add(stationIndex + INDEX_ADDER, station); + } + + public int getStationSize(final Line line) { + List stations = subway.get(line); + return stations.size(); + } +} diff --git a/src/main/java/subway/service/SubwayService.java b/src/main/java/subway/service/SubwayService.java new file mode 100644 index 000000000..518c58172 --- /dev/null +++ b/src/main/java/subway/service/SubwayService.java @@ -0,0 +1,119 @@ +package subway.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import subway.constant.message.ErrorMessage; +import subway.domain.Line; +import subway.domain.LineRepository; +import subway.domain.Station; +import subway.domain.StationRepository; +import subway.domain.Subway; + +public class SubwayService { + + private static final int LIMIT_SUBWAY_STATION_SIZE = 2; + private final Subway subway; + private final LineRepository lineRepository; + private final StationRepository stationRepository; + + public SubwayService( + final Subway subway, + final LineRepository lineRepository, + final StationRepository stationRepository + ) { + this.subway = subway; + this.lineRepository = lineRepository; + this.stationRepository = stationRepository; + } + + public void saveStation(final String input) { + final Station station = new Station(input); + stationRepository.save(station); + } + + public void deleteStation(final String input) { + stationRepository.delete(input); + } + + + public void saveAllStations(final List inputs) { + List stations = inputs.stream() + .map(Station::new) + .collect(Collectors.toList()); + stations.forEach(stationRepository::save); + } + + public void saveAllLines(final List inputs) { + List lines = inputs.stream() + .map(Line::new) + .collect(Collectors.toList()); + lines.forEach(lineRepository::save); + } + + public void saveAllSubway(final Map> subwayMaps) { + for (Map.Entry> entry : subwayMaps.entrySet()) { + Line line = new Line(entry.getKey()); + List stations = entry.getValue().stream() + .map(Station::new) + .collect(Collectors.toList()); + subway.getSubway().put(line, stations); + } + } + + public void initializeSubway() { + List stations = List.of("교대역", "강남역", "역삼역", "남부터미널역", "양재역", "양재시민의숲역", "매봉역"); + saveAllStations(stations); + List lines = List.of("2호선", "3호선", "신분당선"); + saveAllLines(lines); + Map> map = new HashMap<>(); + map.put("2호선", List.of("교대역", "강남역", "역삼역")); + map.put("3호선", List.of("교대역", "남부터미널역", "양재역", "매봉역")); + map.put("신분당선", List.of("강남역", "양재역", "양재시민의숲역")); + saveAllSubway(map); + } + + public List getAllStations() { + return stationRepository.getAll().stream() + .map(Station::getName) + .collect(Collectors.toList()); + } + + public void saveLine(final String lineName, final String startStationName, final String endStationName) { + final Station startStation = stationRepository.getByName(startStationName); + final Station endStation = stationRepository.getByName(endStationName); + final Line line = lineRepository.getByName(lineName); + subway.addNewSubwayLine(line, List.of(startStation, endStation)); + } + + public void deleteLine(final String lineName) { + lineRepository.delete(lineName); + } + + public List getAllLine() { + return lineRepository.getAll().stream() + .map(Line::getName) + .collect(Collectors.toList()); + } + + public void addSubwayStation(final String inputLine, final String inputStation, final String inputIndex) { + final Line line = lineRepository.getByName(inputLine); + final Station station = stationRepository.getByName(inputStation); + final int stationIndex = Integer.parseInt(inputIndex); + subway.addStationByLine(line, station, stationIndex); + } + + public void deleteSubwayLine(final String inputLine) { + final Line line = lineRepository.getByName(inputLine); + if (subway.getStationSize(line) <= LIMIT_SUBWAY_STATION_SIZE) { + throw new IllegalStateException(ErrorMessage.ERROR_PREFIX.toMessage() + "역이 2개 이하면 삭제할 수 없습니다."); + } + subway.deleteSubwayByLine(line); + } + + public Subway getSubway() { + return subway; + } +} + diff --git a/src/main/java/subway/view/InputView.java b/src/main/java/subway/view/InputView.java new file mode 100644 index 000000000..26026cc72 --- /dev/null +++ b/src/main/java/subway/view/InputView.java @@ -0,0 +1,68 @@ +package subway.view; + +import java.util.Scanner; + +public class InputView { + + private static final Scanner scanner = new Scanner(System.in); + + public String readCommand() { + System.out.println("## 원하는 기능을 선택하세요."); + final String command = scanner.next(); + return command; + } + + public String readSaveStationName() { + System.out.println("## 등록할 역 이름을 입력하세요."); + final String input = scanner.next(); + return input; + + } + + public String readDeleteStationName() { + System.out.println("## 삭제할 역 이름을 입력하세요."); + final String input = scanner.next(); + return input; + } + + public String readSaveLineName() { + System.out.println("## 등록할 노선 이름을 입력하세요."); + final String input = scanner.next(); + return input; + } + public String readDeleteLineName() { + System.out.println("## 삭제할 노선 이름을 입력하세요."); + final String input = scanner.next(); + return input; + } + + public String readLineStartStationName() { + System.out.println("## 등록할 노선의 상행 종점역 이름을 입력하세요."); + final String input = scanner.next(); + return input; + } + + public String readLineEndStatationName() { + System.out.println("## 등록할 노선의 하행 종점역 이름을 입력하세요."); + final String input = scanner.next(); + return input; + } + + public String readLineName() { + System.out.println("## 노선을 입력하세요.\n"); + final String input = scanner.next(); + return input; + } + + public String readStationName() { + System.out.println("## 역이름을 입력하세요.\n"); + final String input = scanner.next(); + return input; + } + + public String readStationInputIndex() { + System.out.println("## 순서를 입력하세요.\n"); + final String input = scanner.next(); + return input; + } +} diff --git a/src/main/java/subway/view/OutputView.java b/src/main/java/subway/view/OutputView.java new file mode 100644 index 000000000..cb91e80d2 --- /dev/null +++ b/src/main/java/subway/view/OutputView.java @@ -0,0 +1,83 @@ +package subway.view; + +import java.util.List; +import java.util.Map.Entry; +import subway.constant.message.Processmessage; +import subway.domain.Line; +import subway.domain.Station; +import subway.domain.Subway; + +public class OutputView { + + public void printMainView() { + System.out.println("## 메인 화면\n" + + "1. 역 관리\n" + + "2. 노선 관리\n" + + "3. 구간 관리\n" + + "4. 지하철 노선도 출력\n" + + "Q. 종료"); + } + + public void printStations(final List inputs) { + for (String input : inputs) { + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + input); + } + } + + public void printStationManageView() { + System.out.println("## 역 관리 화면\n" + + "1. 역 등록\n" + + "2. 역 삭제\n" + + "3. 역 조회\n" + + "B. 돌아가기"); + } + + public void printLineManageView() { + System.out.println("## 노선 관리 화면\n" + + "1. 노선 등록\n" + + "2. 노선 삭제\n" + + "3. 노선 조회\n" + + "B. 돌아가기"); + } + + public void printSubwayAddSuccess() { + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + "지하철 노선이 등록되었습니다."); + } + + public void printSubwayDeleteSuccess() { + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + "지하철 노선이 삭제되었습니다."); + } + + public void printLines(final List inputs) { + for (String input : inputs) { + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + input); + } + } + + public void printSectionManageView() { + System.out.println("## 구간 관리 화면\n" + + "1. 구간 등록\n" + + "2. 구간 삭제\n" + + "B. 돌아가기"); + } + + public void printSubway(final Subway subway) { + for (Entry> entry : subway.getSubway().entrySet()) { + printLines(entry); + printStations(entry); + } + } + + private void printLines(final Entry> entry) { + final Line line = entry.getKey(); + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + line.getName().toString()); + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + "---"); + } + + private void printStations(final Entry> entry) { + for (Station s : entry.getValue()) { + System.out.println(Processmessage.SUCCESS_PREFIX.toMessage() + s.getName().toString()); + } + System.out.println(); + } +} diff --git a/src/test/java/subway/constant/CommandTest.java b/src/test/java/subway/constant/CommandTest.java new file mode 100644 index 000000000..b4e492fde --- /dev/null +++ b/src/test/java/subway/constant/CommandTest.java @@ -0,0 +1,123 @@ +package subway.constant; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.ThrowingSupplier; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@SuppressWarnings("NonAsciiCharacters") +class CommandTest { + + @Nested + class 메인 { + + @ParameterizedTest + @ValueSource(strings = {"1", "2", "3", "4", "Q"}) + void 존재하는_버튼만_눌러야한다(String input) { + //given + //when + final ThrowingSupplier supplier = () -> MainCommand.from(input); + + //then + assertDoesNotThrow(supplier); + } + + @Test + void 존재하는_버튼을_입력하면_정상_생성한다() { + //given + final var input = "B"; + + //when + final ThrowingCallable throwingCallable = () -> MainCommand.from(input); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + } + + @Nested + class 노선 { + + @ParameterizedTest + @ValueSource(strings = {"1", "2", "3", "B"}) + void 존재하는_버튼만_눌러야한다(String input) { + //given + //when + final ThrowingSupplier supplier = () -> LineCommand.from(input); + + //then + assertDoesNotThrow(supplier); + } + + @Test + void 존재하는_버튼을_입력하면_정상_생성한다() { + //given + final var input = "A"; + + //when + final ThrowingCallable throwingCallable = () -> LineCommand.from(input); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + } + + @Nested + class 지하철역 { + + @ParameterizedTest + @ValueSource(strings = {"1", "2", "3", "B"}) + void 존재하는_버튼만_눌러야한다(String input) { + //given + //when + final ThrowingSupplier supplier = () -> StationCommand.from(input); + + //then + assertDoesNotThrow(supplier); + } + + @Test + void 존재하는_버튼을_입력하면_정상_생성한다() { + //given + final var input = "A"; + + //when + final ThrowingCallable throwingCallable = () -> StationCommand.from(input); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + } + + @Nested + class 구간 { + + @ParameterizedTest + @ValueSource(strings = {"1", "2", "B"}) + void 존재하는_버튼만_눌러야한다(String input) { + //given + //when + final ThrowingSupplier supplier = () -> SectionCommand.from(input); + + //then + assertDoesNotThrow(supplier); + } + + @Test + void 존재하는_버튼을_입력하면_정상_생성한다() { + //given + final var input = "Q"; + + //when + final ThrowingCallable throwingCallable = () -> SectionCommand.from(input); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + } +} \ No newline at end of file diff --git a/src/test/java/subway/domain/LineRepositoryTest.java b/src/test/java/subway/domain/LineRepositoryTest.java new file mode 100644 index 000000000..e0f32a83d --- /dev/null +++ b/src/test/java/subway/domain/LineRepositoryTest.java @@ -0,0 +1,120 @@ +package subway.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +class LineRepositoryTest { + + private LineRepository lineRepository; + + @BeforeEach + void 테스트용_객체_주입() { + lineRepository = new LineRepository(); + + lineRepository.save(new Line("1호선")); + lineRepository.save(new Line("2호선")); + lineRepository.save(new Line("3호선")); + } + + @Test + void 노선의_모든_객체를_반환한다() { + //given + //when + final var lists = lineRepository.getAll(); + + //then + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(lists).hasSize(3); + softly.assertThat(lists.get(0)).isEqualTo(new Line("1호선")); + softly.assertThat(lists.get(1)).isEqualTo(new Line("2호선")); + softly.assertThat(lists.get(2)).isEqualTo(new Line("3호선")); + }); + } + + @Test + void 다른_이름의_노선을_저장해야한다() { + //given + final var line = new Line("1호선"); + + //when + final ThrowingCallable throwingCallable = () -> lineRepository.save(line); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 노선을_정상적으로_저장한다() { + //given + final var line = new Line("4호선"); + + //when + lineRepository.save(line); + + //then + final var lists = lineRepository.getAll(); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(lists).hasSize(4); + softly.assertThat(lists.get(3)).isEqualTo(new Line("4호선")); + }); + } + + @Test + void 존재하는_노선을_삭제해야한다() { + //given + final var lineName = "5호선"; + + //when + final ThrowingCallable throwingCallable = () -> lineRepository.delete(lineName); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 노선을_삭제한다() { + //given + final var lineName = "1호선"; + + //when + lineRepository.delete(lineName); + + //then + final var lines = lineRepository.getAll(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(lines).hasSize(2); + softly.assertThat(lines).isNotIn(new Line(lineName)); + }); + } + + @Test + void 존재하는_이름으로_노선을_가져와야한다() { + //given + final var lineName = "5호선"; + + //when + final ThrowingCallable throwingCallable = () -> lineRepository.getByName(lineName); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalStateException.class); + } + + @Test + void 이름으로_노선을_가져온다() { + //given + final var lineName = "1호선"; + + //when + final var line = lineRepository.getByName(lineName); + + //then + assertThat(line).isEqualTo(new Line(lineName)); + } +} \ No newline at end of file diff --git a/src/test/java/subway/domain/LineTest.java b/src/test/java/subway/domain/LineTest.java new file mode 100644 index 000000000..e1ce1a974 --- /dev/null +++ b/src/test/java/subway/domain/LineTest.java @@ -0,0 +1,49 @@ +package subway.domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +class LineTest { + + @Test + void 이름의_길이는_2보다_커야한다() { + //given + final var name = "선"; + + //when + final ThrowingCallable throwingCallable = () -> new Line(name); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 이름은_선으로_끝나야_합니다() { + //given + final var name = "5호선"; + + //when + final ThrowingCallable throwingCallable = () -> new Line(name); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 이름이_일치하는지_확인합니다() { + //given + final var lineName = "2호선"; + final var actualLine = new Line(lineName); + + //when + //then + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(actualLine.isEqualsByName(lineName)).isTrue(); + softly.assertThat(actualLine.isEqualsByName("9호선")).isFalse(); + }); + } +} \ No newline at end of file diff --git a/src/test/java/subway/domain/StationRepositoryTest.java b/src/test/java/subway/domain/StationRepositoryTest.java new file mode 100644 index 000000000..1ae693c7c --- /dev/null +++ b/src/test/java/subway/domain/StationRepositoryTest.java @@ -0,0 +1,120 @@ +package subway.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +class StationRepositoryTest { + + private StationRepository stationRepository; + + @BeforeEach + void 테스트용_객체_주입() { + stationRepository = new StationRepository(); + + stationRepository.save(new Station("인덕원역")); + stationRepository.save(new Station("평촌역")); + stationRepository.save(new Station("범계역")); + } + + @Test + void 역의_모든_객체를_반환한다() { + //given + //when + final var stations = stationRepository.getAll(); + + //then + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(stations).hasSize(3); + softly.assertThat(stations.get(0)).isEqualTo(new Station("인덕원역")); + softly.assertThat(stations.get(1)).isEqualTo(new Station("평촌역")); + softly.assertThat(stations.get(2)).isEqualTo(new Station("범계역")); + }); + } + + @Test + void 다른_이름의_역을_저장해야한다() { + //given + final var station = new Station("인덕원역"); + + //when + final ThrowingCallable throwingCallable = () -> stationRepository.save(station); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 역을_정상적으로_저장한다() { + //given + final var station = new Station("과천역"); + + //when + stationRepository.save(station); + + //then + final var lists = stationRepository.getAll(); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(lists).hasSize(4); + softly.assertThat(lists.get(3)).isEqualTo(new Station("과천역")); + }); + } + + @Test + void 존재하는_역을_삭제해야한다() { + //given + final var stationName = "과천역"; + + //when + final ThrowingCallable throwingCallable = () -> stationRepository.delete(stationName); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalStateException.class); + } + + @Test + void 역을_삭제한다() { + //given + final var stationName = "인덕원역"; + + //when + stationRepository.delete(stationName); + + //then + final var stations = stationRepository.getAll(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(stations).hasSize(2); + softly.assertThat(stations).isNotIn(new Station(stationName)); + }); + } + + @Test + void 존재하는_이름으로_역을_가져와야한다() { + //given + final var stationName = "과천역"; + + //when + final ThrowingCallable throwingCallable = () -> stationRepository.getByName(stationName); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalStateException.class); + } + + @Test + void 이름으로_역을_가져온다() { + //given + final var stationName = "인덕원역"; + + //when + final var station = stationRepository.getByName(stationName); + + //then + assertThat(station).isEqualTo(new Station(stationName)); + } +} \ No newline at end of file diff --git a/src/test/java/subway/domain/StationTest.java b/src/test/java/subway/domain/StationTest.java new file mode 100644 index 000000000..b0d1ebff2 --- /dev/null +++ b/src/test/java/subway/domain/StationTest.java @@ -0,0 +1,60 @@ +package subway.domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.ThrowingSupplier; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@SuppressWarnings("NonAsciiCharacters") +class StationTest { + + @Test + void 이름의_길이는_2보다_커야한다() { + //given + final var name = "역"; + + //when + final ThrowingCallable throwingCallable = () -> new Station(name); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @ValueSource(strings = {"asdf", "asd역"}) + void 지하철역_이름은_한글만_가능합니다(String input) { + //given + //when + final ThrowingCallable throwingCallable = () -> new Station(input); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @ValueSource(strings = {"인덕원", "과천정부청사"}) + void 지하철역_이름은_역으로_끝나야합니다(String input) { + //given + //when + final ThrowingCallable throwingCallable = () -> new Station(input); + + //then + assertThatThrownBy(throwingCallable).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 이름을_정상적으로_입력하면_생성한다() { + //given + final var name = "인덕원역"; + + //when + final ThrowingSupplier supplier = () -> new Station(name); + + //then + assertDoesNotThrow(supplier); + } +} \ No newline at end of file