Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[지하철 노선도] 구현 완료 #1

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/main/java/docs/README.md
Original file line number Diff line number Diff line change
@@ -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] 노선에 존재하는 역만 삭제할 수 있다.
6 changes: 3 additions & 3 deletions src/main/java/subway/Application.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
51 changes: 51 additions & 0 deletions src/main/java/subway/common/AppConfig.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
13 changes: 13 additions & 0 deletions src/main/java/subway/common/ExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
25 changes: 25 additions & 0 deletions src/main/java/subway/constant/LineCommand.java
Original file line number Diff line number Diff line change
@@ -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() + "없는 버튼입니다."));
}
}
25 changes: 25 additions & 0 deletions src/main/java/subway/constant/MainCommand.java
Original file line number Diff line number Diff line change
@@ -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() + "없는 버튼입니다."));
}
}
23 changes: 23 additions & 0 deletions src/main/java/subway/constant/SectionCommand.java
Original file line number Diff line number Diff line change
@@ -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() + "없는 버튼입니다."));
}
}
24 changes: 24 additions & 0 deletions src/main/java/subway/constant/StationCommand.java
Original file line number Diff line number Diff line change
@@ -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()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

특정 값을 필터링한 이후, findAny()를 통해 값을 찾고있어요.

findAny()를 하게되면 필터링한 값들 중 for과 같은 반복을 통해 내부 필터링된 값을 스캔하여 찾는다고 생각했어요.
그것보다 현재 해당 메서드에서 기대하는건 내가 눌렀던 커맨드 키를 찾는것이니 findFirst가 더 적절하겠어요.

또한, from 이라는 메서드는 특정 값을 통한 인스턴스 생성을 기대하는데, 내부 동작에서는 필터링을 통한 "내가 선택한 커맨드 키"를 찾는과정이 더 강하다고 느껴졌어요.

해당 메서드명도 적절하게 수정하면 좋겠어요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 구현을 할 당시 findAny(), findFirst()를 검색해보았는데 Parallel한 환경이 아닌 이상 둘 중 무엇을 사용하더라도 문제가 없다고 생각을 했어요.. 의빈민 말씀을 들으니 findFirst()가 적절하다고 느껴지네요
  • 사실 enum을 사용해서 특정 버튼을 누르면 함수형 인터페이스를 사용해 해당 함수를 호출하는 로직을 작성을 하려고 했는데 static으로 선언을 하지 않는 이상 호출을 할 수 없더라고요..
    결국 인스턴스를 반환하는 형식이 되버려 정적펙토리 메서드 네이밍 컨벤션인 from()을 사용했습니다. 말씀해 주신것처럼 무슨 과정을 처리하는지 고려하는게 좋은거 같네요

ps. 혹시 메서드명을 적절하게 선정하는 팁같은게 있으신가요?? 🤔🤔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가적으로 findAny 내부를 들어가서 살펴보시면 아래와 같은 내용을 찾아보실 수 있어요.

해당 사진을 참고하시면 답을 얻으실 수 있을수도 있겠네요.

image

.orElseThrow(() -> new IllegalArgumentException(ErrorMessage.ERROR_PREFIX.toMessage() + "없는 버튼입니다."));
}
}
15 changes: 15 additions & 0 deletions src/main/java/subway/constant/message/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
16 changes: 16 additions & 0 deletions src/main/java/subway/constant/message/Processmessage.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
127 changes: 127 additions & 0 deletions src/main/java/subway/controller/SubwayController.java
Original file line number Diff line number Diff line change
@@ -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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 모든 메서드가 While(true)라고 선언되어있어요.

프로그램을 시작할때 시작 -> 수행 -> 종료와 같은 기본적인 프로세스 동작을 기대하는데 현재 코드로 살펴봤을때 시작 -> 수행은 보이지만 종료에 대해서는 확인할 수 없다고 생각했어요

또한, startSubWay()라는 메서드명을 봤을땐 지하철 노선 동작의 시작이라는 기대를 하고 있는데 전체 동작을 다 수행한다고 생각이 들어 기대값과 메서드명이 일치하지 않는다고 생각이 들었어요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀해주신 방법을 고려해봤는데 enum으로 INPROGRESS,END상태를 표현해 while의 조건문의 상태를 변경해주는 방법으로 해결하는건 어떻게 생각을 하시나요?

Copy link

@JoeCP17 JoeCP17 May 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

탈출조건이 명확하게 보이면 좋을 것 같아요

현재 해당 로직은 시작,수행,탈출에 대한 수행을 최상위로 표현하는 유스케이스 느낌이라고 생각하는데 보다 적절한 네이밍으로 사용되면 좋겠고
해당 메서드에서는 동작의 흐름만을 볼 수 있도록 수정한다면 보다 가독성도 올라갈 것같단 생각이 들었어요

유스케이스가 주는 의미를 생각해보시면 좋겠어요.

}
}

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<String> 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<String> stationsName = subwayService.getAllStations();
outputView.printStations(stationsName);
}
if (stationCommand.equals(StationCommand.BACK)) {
break;
}
}
}
}
Loading