diff --git a/.github/solving_process.md b/.github/solving_process.md
new file mode 100644
index 000000000..7d6aa8807
--- /dev/null
+++ b/.github/solving_process.md
@@ -0,0 +1,3873 @@
+# π§ μ§νμ² λ
Έμ λ κ²½λ‘ μ‘°ν λ―Έμ
+
+[μ°μνν
ν¬μ½μ€](https://github.com/woowacourse) precourse λ¬Έμ
+μ€ [μ§νμ² λ
Έμ λ κ²½λ‘ μ‘°ν λ―Έμ
](https://github.com/woowacourse/java-subway-path-precourse) νμ΄ κΈ°λ‘νκΈ°.
+
+DDD ꡬ쑰μ MVC ν¨ν΄μ μ μ©νμ¬ TDD λ°©μμΌλ‘ κ°λ°νκ³ , μ
μΆλ ₯ λ° νλ‘κ·Έλλ° μꡬμ¬νμ λΆν©νλλ‘ νμ΄ λ³Ό μμ .
+
+## 0. μ€κ³
+
+### application
+
+| λΉμ¦λμ€ | κΈ°λ₯ |
+|:-------:|:------------------------------------------------------------------|
+| section | - κ΅¬κ° CRUD
- κ·Έλν λ
Έλ μΆκ°
- μ΅λ¨κ±°λ¦¬ κ²½λ‘ κ΅¬νκΈ°
- μ΅μμκ° κ²½λ‘ κ΅¬νκΈ° |
+
+### domain
+
+| λΉμ¦λμ€ | κΈ°λ₯ |
+|:-------:|:-----------------------|
+| line | - λ
Έμ κ°μ²΄
- λ
Έμ CRUD |
+| section | - κ΅¬κ° κ°μ²΄
- κ΅¬κ° CRUD |
+| station | - μ κ°μ²΄
- μ CRUD |
+
+### infrastructure
+
+| ν΄λμ€ | κΈ°λ₯ |
+|:-------------:|:------------------------|
+| FileParser | - abstract
- νμΌ κ²μ¦ |
+| XmlFileParser | - xml νμΌ λ‘λ λ° λ°μ΄ν° νμ± |
+
+### presentation
+
+| ν΄λμ€ | κΈ°λ₯ |
+|:-------------------:|:-------------------------------|
+| ViewController | - interface |
+| IntroViewController | - λλ©μΈ λ°μ΄ν° μ΄κΈ°ν
- μΈνΈλ‘ νλ©΄ μ μ΄ |
+| MainViewController | - λ©μΈ νλ©΄ μ μ΄ |
+| PathViewController | - κ²½λ‘ κΈ°μ€(νλ©΄) μ μ΄ |
+| LineController | - λ
Έμ λΉμ¦λμ€ μ²λ¦¬ |
+| SectionController | - κ΅¬κ° λΉμ¦λμ€ μ²λ¦¬ |
+| StationController | - μ λΉμ¦λμ€ μ²λ¦¬ |
+| ShortCostController | - μ΅λ¨(μ΅μ) κ²½λ‘ μ²λ¦¬ |
+
+### ui
+
+| ν΄λμ€ | κΈ°λ₯ |
+|:-------:|:------------|
+| Console | - μ½μ μ
μΆλ ₯ μ²λ¦¬ |
+
+### view
+
+| ν΄λμ€ | κΈ°λ₯ |
+|:-----------------:|:------------------------------------------------|
+| View | - interface |
+| MenuView | - λ©λ΄ μΆλ ₯
- νλͺ© μ
λ ₯
- λ©λ΄ μ ν μ΄λ²€νΈ λ°μ |
+| Menu | - interface
- λ©λ΄ λͺ©λ‘ μ‘°ν
- λͺ
λ Ήμ΄ κΈ°μ€ λ¨κ±΄ μ‘°ν |
+| MenuEventRegister | - λ©λ΄ μ ν μ΄λ²€νΈ μ²λ¦¬ λ±λ‘ |
+
+### util
+
+| ν΄λμ€ | κΈ°λ₯ |
+|:----------:|:------------|
+| Validation | - κ³΅ν΅ μ ν¨μ± κ²μ¦ |
+
+## 1. Line CRUD
+
+```java
+// LineDTOTest.java
+
+package subway.domain.line;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class LineDTOTest {
+ @Test
+ public void constructor__LineNameEssentialException() {
+ String message = "λ
Έμ μ΄λ¦μ νμμ
λλ€.";
+ assertThatThrownBy(() -> new LineDTO(null)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ assertThatThrownBy(() -> new LineDTO("")).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// LineDTO.java
+
+package subway.domain.line;
+
+public class LineDTO {
+ private static final String LINE_NAME_ESSENTIAL_MESSAGE = "λ
Έμ μ΄λ¦μ νμμ
λλ€.";
+
+ private final String name;
+
+ public LineDTO(String name) {
+ this.validate(name);
+ this.name = name;
+ }
+
+ private void validate(String name) {
+ if (name == null || name.trim().isEmpty()) {
+ throw new IllegalArgumentException(LINE_NAME_ESSENTIAL_MESSAGE);
+ }
+ }
+
+ public String getName() {
+ return this.name;
+ }
+}
+```
+
+λ€μν κ³μΈ΅μμ μ°μΌ κΈ°λ³Έ LineDTO ꡬν.
+
+```java
+// LineService.java
+
+package subway.domain.line;
+
+import java.util.List;
+
+public class LineService {
+ public List findAll() {
+ return LineRepository.lines();
+ }
+
+ public void deleteAll() {
+ LineRepository.deleteAll();
+ }
+}
+```
+
+κΈ°λ³Έ μ 체 μ‘°ν λ° μμ κΈ°λ₯ μμ±.
+
+### 1-1. CREATE
+
+```java
+// LineRepositoryTest.java
+
+package subway.domain.line;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class LineRepositoryTest {
+ @Test
+ public void exists() {
+ Line line = new Line("test");
+ assertThat(LineRepository.exists(line)).isEqualTo(false);
+ LineRepository.addLine(line);
+ assertThat(LineRepository.exists(line)).isEqualTo(true);
+ }
+
+ @AfterEach
+ public void init() {
+ LineRepository.deleteAll();
+ }
+}
+```
+
+```java
+// LineRepository.java
+
+package subway.domain.line;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class LineRepository {
+ public static boolean exists(Line other) {
+ return lines.stream().anyMatch(line -> line.getName().equals(other.getName()));
+ }
+}
+```
+
+λ
Έμ μ€λ³΅ μμ± λ°©μ§λ₯Ό μν΄ μ΄λ¦ κΈ°μ€μΌλ‘ μ‘΄μ¬ μ¬λΆ νμΈ κΈ°λ₯ ꡬν.
+
+```java
+// LineServiceTest.java
+
+package subway.domain.line;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class LineServiceTest {
+ private final LineService lineService = new LineService();
+
+ @Test
+ public void addLine() {
+ LineDTO lineDTO = new LineDTO("test");
+ assertThat(lineService.findAll()).hasSize(0);
+ this.lineService.addLine(lineDTO);
+ assertThat(lineService.findAll()).hasSize(1);
+ }
+
+ @Test
+ public void addLine__AlreadyExistsLineException() {
+ LineDTO lineDTO = new LineDTO("test");
+ String message = "μ΄λ―Έ λ±λ‘λμ΄μλ λ
Έμ μ
λλ€.";
+ this.lineService.addLine(lineDTO);
+ assertThatThrownBy(() -> this.lineService.addLine(lineDTO)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @AfterEach
+ public void init() {
+ this.lineService.deleteAll();
+ }
+}
+```
+
+```java
+// LineService.java
+
+package subway.domain.line;
+
+import java.util.List;
+
+public class LineService {
+ private static final String ALREADY_EXISTS_LINE_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ λ
Έμ μ
λλ€.";
+
+ public void addLine(LineDTO lineDTO) {
+ Line line = new Line(lineDTO.getName());
+ if (LineRepository.exists(line)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_MESSAGE);
+ }
+ LineRepository.addLine(line);
+ }
+}
+```
+
+λ
Έμ μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// LineController.java
+
+package subway.presentation;
+
+import subway.domain.line.LineDTO;
+import subway.domain.line.LineService;
+
+public class LineController {
+ private final LineService lineService = new LineService();
+
+ public void addLine(String lineName) {
+ LineDTO lineDTO = new LineDTO(lineName);
+ this.lineService.addLine(lineDTO);
+ }
+}
+```
+
+μ μ΄ κ³μΈ΅μ λ
Έμ μΆκ° κΈ°λ₯ λ§€ν.
+
+### 1-2. READ
+
+```java
+// LineRepositoryTest.java
+
+package subway.domain;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.line.LineRepository;
+
+public class LineRepositoryTest {
+ @Test
+ public void findByName() {
+ String name = "test";
+ Line line = new Line(name);
+ assertThat(LineRepository.findByName(name)).isNotPresent();
+ LineRepository.addLine(line);
+ assertThat(LineRepository.findByName(name)).isPresent();
+ }
+}
+```
+
+```java
+// LineRepository.java
+
+package subway.domain.line;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+public class LineRepository {
+ public static Optional findByName(String name) {
+ return lines.stream().filter(line -> line.getName().equals(name)).findFirst();
+ }
+}
+```
+
+```java
+// LineServiceTest.java
+
+package subway.domain.line;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class LineServiceTest {
+ @Test
+ public void findOneByName() {
+ String name = "test";
+ LineDTO lineDTO = new LineDTO(name);
+ this.lineService.addLine(lineDTO);
+ Line line = this.lineService.findOneByName(name);
+ assertThat(line.getName()).isEqualTo(name);
+ }
+
+ @Test
+ public void findOneByName__NotExistsLineException() {
+ String message = "μ‘΄μ¬νμ§ μμ λ
Έμ μ
λλ€.";
+ assertThatThrownBy(() -> this.lineService.findOneByName("test")).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// LineService.java
+
+package subway.domain.line;
+
+import java.util.List;
+
+public class LineService {
+ private static final String NOT_EXISTS_LINE_MESSAGE = "μ‘΄μ¬νμ§ μμ λ
Έμ μ
λλ€.";
+
+ public Line findOneByName(String name) {
+ return LineRepository.findByName(name).orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_MESSAGE));
+ }
+}
+```
+
+λ
Έμ μ΄λ¦ κΈ°μ€ λ¨κ±΄ μ‘°ν κΈ°λ₯ ꡬν.
+
+## 2. Station CRUD
+
+```java
+// StationDTOTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class StationDTOTest {
+ @Test
+ public void constructor__StationNameEssentialException() {
+ String message = "μ μ΄λ¦μ νμμ
λλ€.";
+ assertThatThrownBy(() -> new StationDTO(null)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ assertThatThrownBy(() -> new StationDTO("")).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// StationDTO.java
+
+package subway.domain.station;
+
+public class StationDTO {
+ private static final String STATION_NAME_ESSENTIAL_MESSAGE = "μ μ΄λ¦μ νμμ
λλ€.";
+
+ private final String name;
+
+ public StationDTO(String name) {
+ this.validate(name);
+ this.name = name;
+ }
+
+ private void validate(String name) {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException(STATION_NAME_ESSENTIAL_MESSAGE);
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+}
+```
+
+λ€μν κ³μΈ΅μμ μ°μΌ κΈ°λ³Έ StationDTO ꡬν.
+
+```java
+// StationService.java
+
+package subway.domain.station;
+
+import java.util.List;
+
+public class StationService {
+ public List findAll() {
+ return StationRepository.stations();
+ }
+
+ public void deleteAll() {
+ StationRepository.deleteAll();
+ }
+}
+
+```
+
+κΈ°λ³Έ μ 체 μ‘°ν λ° μμ κΈ°λ₯ μμ±.
+
+### 2-1. CREATE
+
+```java
+// StationRepositoryTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class StationRepositoryTest {
+ @Test
+ public void exists() {
+ Station station = new Station("test");
+ assertThat(StationRepository.exists(station)).isEqualTo(false);
+ StationRepository.addStation(station);
+ assertThat(StationRepository.exists(station)).isEqualTo(true);
+ }
+
+ @AfterEach
+ public void init() {
+ StationRepository.deleteAll();
+ }
+}
+```
+
+```java
+// StationRepository.java
+
+package subway.domain.station;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+public class StationRepository {
+ public static boolean exists(Station other) {
+ return stations().stream().anyMatch(station -> station.getName().equals(other.getName()));
+ }
+}
+```
+
+μ μ€λ³΅ μμ± λ°©μ§λ₯Ό μν΄ μ΄λ¦ κΈ°μ€μΌλ‘ μ‘΄μ¬ μ¬λΆ νμΈ κΈ°λ₯ ꡬν.
+
+```java
+// StationServiceTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class StationServiceTest {
+ private final StationService stationService = new StationService();
+
+ @Test
+ public void addStation() {
+ StationDTO stationDTO = new StationDTO("test");
+ assertThat(this.stationService.findAll()).hasSize(0);
+ this.stationService.addStation(stationDTO);
+ assertThat(this.stationService.findAll()).hasSize(1);
+ }
+
+ @Test
+ public void addStation__AlreadyExistsStationException() {
+ StationDTO stationDTO = new StationDTO("test");
+ String message = "μ΄λ―Έ λ±λ‘λμ΄μλ μμ
λλ€.";
+ this.stationService.addStation(stationDTO);
+ assertThatThrownBy(() -> this.stationService.addStation(stationDTO)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ this.stationService.deleteAll();
+ }
+
+ @AfterEach
+ public void init() {
+ this.stationService.deleteAll();
+ }
+}
+```
+
+```java
+// StationService.java
+
+package subway.domain.station;
+
+import java.util.List;
+
+public class StationService {
+ private static final String ALREADY_EXISTS_STATION_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ μμ
λλ€.";
+
+ public void addStation(StationDTO stationDTO) {
+ Station station = new Station(stationDTO.getName());
+ if (StationRepository.exists(station)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_STATION_MESSAGE);
+ }
+ StationRepository.addStation(station);
+ }
+}
+```
+
+μ μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// StationController.java
+
+package subway.presentation;
+
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class StationController {
+ private final StationService stationService = new StationService();
+
+ public void addStation(String name) {
+ StationDTO stationDTO = new StationDTO(name);
+ stationService.addStation(stationDTO);
+ }
+}
+```
+
+μ μ΄ κ³μΈ΅μ μ μΆκ° κΈ°λ₯ λ§€ν.
+
+### 2-2. READ
+
+```java
+// StationRepositoryTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class StationRepositoryTest {
+ @Test
+ public void findByName() {
+ String name = "test";
+ Station station = new Station(name);
+ assertThat(StationRepository.findByName(name)).isNotPresent();
+ StationRepository.addStation(station);
+ assertThat(StationRepository.findByName(name)).isPresent();
+ }
+}
+```
+
+```java
+// StationRepository.java
+
+package subway.domain.station;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+public class StationRepository {
+ public static Optional findByName(String name) {
+ return stations.stream().filter(station -> station.getName().equals(name)).findFirst();
+ }
+}
+```
+
+```java
+// StationRepositoryTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class StationServiceTest {
+ @Test
+ public void findOneByName() {
+ String name = "test";
+ StationDTO stationDTO = new StationDTO(name);
+ this.stationService.addStation(stationDTO);
+ Station station = this.stationService.findOneByName(name);
+ assertThat(station.getName()).isEqualTo(name);
+ }
+
+ @Test
+ public void findOneByName__NotExistsStationException() {
+ String message = "μ‘΄μ¬νμ§ μμ μμ
λλ€.";
+ assertThatThrownBy(() -> this.stationService.findOneByName("test")).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// StationService.java
+
+package subway.domain.station;
+
+import java.util.List;
+
+public class StationService {
+ private static final String NOT_EXISTS_STATION_MESSAGE = "μ‘΄μ¬νμ§ μμ μμ
λλ€.";
+
+ public Station findOneByName(String name) {
+ return StationRepository.findByName(name)
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_STATION_MESSAGE));
+ }
+}
+```
+
+μ μ΄λ¦ κΈ°μ€ λ¨κ±΄ μ‘°ν κΈ°λ₯ ꡬν.
+
+## 3. Section CRUD
+
+```java
+// Section.java
+
+package subway.domain.section;
+
+import subway.domain.line.Line;
+import subway.domain.station.Station;
+
+public class Section {
+ private final Line line;
+ private final Station source;
+ private final Station sink;
+ private final int distance;
+ private final int time;
+
+ public Section(Line line, Station source, Station sink, int distance, int time) {
+ this.line = line;
+ this.source = source;
+ this.sink = sink;
+ this.distance = distance;
+ this.time = time;
+ }
+
+ public Line getLine() {
+ return line;
+ }
+
+ public Station getSource() {
+ return source;
+ }
+
+ public Station getSink() {
+ return sink;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ public int getTime() {
+ return time;
+ }
+}
+```
+
+κ΅¬κ° κ°μ²΄ μμ±.
+
+```java
+// LineTest.java
+
+package subway.domain.line;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+public class LineTest {
+ @Test
+ public void addSection() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ Section section = new Section(line, source, sink, 0, 0);
+ assertThat(line.getSectionList()).hasSize(0);
+ line.addSection(section);
+ assertThat(line.getSectionList()).hasSize(1);
+ }
+
+ @Test
+ public void addSection__SectionAddingOtherLineReceiveException() {
+ Line line = new Line("line");
+ Line other = new Line("other");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ Section section = new Section(other, source, sink, 0, 0);
+ String message = "κ΅¬κ° μΆκ°μ λ€λ₯Έ λ
Έμ μ λ°μμ΅λλ€.";
+ assertThatThrownBy(() -> line.addSection(section)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// Line.java
+
+package subway.domain.line;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import subway.domain.section.Section;
+
+public class Line {
+ private static final String SECTION_ADDING_OTHER_LINE_RECEIVE_MESSAGE = "κ΅¬κ° μΆκ°μ λ€λ₯Έ λ
Έμ μ λ°μμ΅λλ€.";
+
+ private final List sectionList = new ArrayList<>();
+
+ public void addSection(Section section) {
+ this.validateSection(section);
+ this.sectionList.add(section);
+ }
+
+ private void validateSection(Section section) {
+ if (this != section.getLine()) {
+ throw new IllegalArgumentException(SECTION_ADDING_OTHER_LINE_RECEIVE_MESSAGE);
+ }
+ }
+}
+```
+
+Line 1 : N λ§€μΉ.
+
+Section μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// StationTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.section.Section;
+
+public class StationTest {
+ @Test
+ public void addSection() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ Section section = new Section(line, source, sink, 0, 0);
+ assertThat(source.getSectionList()).hasSize(0);
+ source.addSection(section);
+ assertThat(source.getSectionList()).hasSize(1);
+ }
+
+ @Test
+ public void addSection__SectionAddingOtherSourceStationReceiveException() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station other = new Station("other");
+ Station sink = new Station("sink");
+ Section section = new Section(line, other, sink, 0, 0);
+ String message = "κ΅¬κ° μΆκ°μ λ€λ₯Έ μμμμ λ°μμ΅λλ€.";
+ assertThatThrownBy(() -> source.addSection(section)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// Station.java
+
+package subway.domain.station;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import subway.domain.section.Section;
+
+public class Station {
+ private static final String SECTION_ADDING_OTHER_SOURCE_STATION_RECEIVE_MESSAGE = "κ΅¬κ° μΆκ°μ λ€λ₯Έ μμμμ λ°μμ΅λλ€.";
+
+ private final List sectionList = new ArrayList<>();
+
+ public List getSectionList() {
+ return Collections.unmodifiableList(this.sectionList);
+ }
+
+ public void addSection(Section section) {
+ this.validateSection(section);
+ this.sectionList.add(section);
+ }
+
+ public void validateSection(Section section) {
+ if (this != section.getSource()) {
+ throw new IllegalArgumentException(SECTION_ADDING_OTHER_SOURCE_STATION_RECEIVE_MESSAGE);
+ }
+ }
+}
+```
+
+Station 1 : N λ§€μΉ.
+
+Section μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// SectionDTOTest.java
+
+package subway.application.section.dto;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.LineDTO;
+import subway.domain.station.StationDTO;
+
+public class SectionDTOTest {
+ @Test
+ public void constructor__SectionAddingLineInfoEssentialException() {
+ StationDTO sourceDTO = new StationDTO("source");
+ StationDTO sinkDTO = new StationDTO("sink");
+ String message = "κ΅¬κ° μμ±μ λ
Έμ μ 보λ νμμ
λλ€.";
+ assertThatThrownBy(() -> new SectionDTO(null, sourceDTO, sinkDTO)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @Test
+ public void constructor__SectionAddingSourceStationInfoEssentialException() {
+ LineDTO lineDTO = new LineDTO("line");
+ StationDTO sinkDTO = new StationDTO("sink");
+ String message = "κ΅¬κ° μμ±μ μμμ μ 보λ νμμ
λλ€.";
+ assertThatThrownBy(() -> new SectionDTO(lineDTO, null, sinkDTO)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @Test
+ public void constructor__SectionAddingSinkStationInfoEssentialException() {
+ LineDTO lineDTO = new LineDTO("line");
+ StationDTO sourceDTO = new StationDTO("source");
+ String message = "κ΅¬κ° μμ±μ μ’
λ£μ μ 보λ νμμ
λλ€.";
+ assertThatThrownBy(() -> new SectionDTO(lineDTO, sourceDTO, null)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// SectionDTO.java
+
+package subway.application.section.dto;
+
+import subway.domain.line.LineDTO;
+import subway.domain.station.StationDTO;
+
+public class SectionDTO {
+ private static final String SECTION_ADDING_LINE_INFO_ESSENTIAL_MESSAGE = "κ΅¬κ° μμ±μ λ
Έμ μ 보λ νμμ
λλ€.";
+ private static final String SECTION_ADDING_SOURCE_STATION_INFO_ESSENTIAL_MESSAGE = "κ΅¬κ° μμ±μ μμμ μ 보λ νμμ
λλ€.";
+ private static final String SECTION_ADDING_SINK_STATION_INFO_ESSENTIAL_MESSAGE = "κ΅¬κ° μμ±μ μ’
λ£μ μ 보λ νμμ
λλ€.";
+
+ private final LineDTO lineDTO;
+ private final StationDTO sourceDTO;
+ private final StationDTO sinkDTO;
+ private int distance;
+ private int time;
+
+ public SectionDTO(LineDTO lineDTO, StationDTO sourceDTO, StationDTO sinkDTO) {
+ this(lineDTO, sourceDTO, sinkDTO, 0, 0);
+ }
+
+ public SectionDTO(LineDTO lineDTO, StationDTO sourceDTO, StationDTO sinkDTO, int distance, int time) {
+ this.validate(lineDTO, sourceDTO, sinkDTO);
+ this.lineDTO = lineDTO;
+ this.sourceDTO = sourceDTO;
+ this.sinkDTO = sinkDTO;
+ this.distance = distance;
+ this.time = time;
+ }
+
+ private void validate(LineDTO lineDTO, StationDTO sourceDTO, StationDTO sinkDTO) {
+ if (lineDTO == null) {
+ throw new IllegalArgumentException(SECTION_ADDING_LINE_INFO_ESSENTIAL_MESSAGE);
+ }
+ if (sourceDTO == null) {
+ throw new IllegalArgumentException(SECTION_ADDING_SOURCE_STATION_INFO_ESSENTIAL_MESSAGE);
+ }
+ if (sinkDTO == null) {
+ throw new IllegalArgumentException(SECTION_ADDING_SINK_STATION_INFO_ESSENTIAL_MESSAGE);
+ }
+ }
+
+ public LineDTO getLineDTO() {
+ return lineDTO;
+ }
+
+ public StationDTO getSourceDTO() {
+ return sourceDTO;
+ }
+
+ public StationDTO getSinkDTO() {
+ return sinkDTO;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ public void setDistance(int distance) {
+ this.distance = distance;
+ }
+
+ public int getTime() {
+ return time;
+ }
+
+ public void setTime(int time) {
+ this.time = time;
+ }
+}
+```
+
+λ€μν κ³μΈ΅μμ μ°μΌ κΈ°λ³Έ SectionDTO ꡬν.
+
+```java
+// SectionRepository.java
+
+package subway.domain.section;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class SectionRepository {
+ private static final List sections = new ArrayList<>();
+
+ public static List sections() {
+ return Collections.unmodifiableList(sections);
+ }
+
+ public static void deleteAll() {
+ sections.clear();
+ }
+}
+```
+
+```java
+// SectionService.java
+
+package subway.application.section.service;
+
+import java.util.List;
+
+import subway.domain.section.Section;
+import subway.domain.section.SectionRepository;
+
+public class SectionService {
+ public List findAll() {
+ return SectionRepository.sections();
+ }
+
+ public void deleteAll() {
+ SectionRepository.deleteAll();
+ }
+}
+```
+
+κΈ°λ³Έ μ 체 μ‘°ν λ° μμ κΈ°λ₯ μμ±.
+
+### 3-1. CREATE
+
+```java
+// SectionRepositoryTest.java
+
+package subway.domain.section;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.station.Station;
+
+public class SectionRepositoryTest {
+ @Test
+ public void exists() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ Section section = new Section(line, source, sink, 0, 0);
+ assertThat(SectionRepository.exists(section)).isEqualTo(false);
+ SectionRepository.addSection(section);
+ assertThat(SectionRepository.exists(section)).isEqualTo(true);
+ }
+
+ @AfterEach
+ public void init() {
+ SectionRepository.deleteAll();
+ }
+}
+```
+
+```java
+// SectionRepository.java
+
+package subway.domain.section;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class SectionRepository {
+ public static void addSection(Section section) {
+ sections.add(section);
+ }
+
+ public static boolean exists(Section other) {
+ return sections.stream()
+ .anyMatch(section -> section.getLine() == other.getLine() && section.getSource() == other.getSource()
+ && section.getSink() == other.getSink());
+ }
+}
+```
+
+κ΅¬κ° λ°μ΄ν° μΆκ° κΈ°λ₯ ꡬν.
+
+κ΅¬κ° μ€λ³΅ μμ± λ°©μ§λ₯Ό μν΄ λ
Έμ , μμμ, μ’
λ£μ κΈ°μ€μΌλ‘ μ‘΄μ¬ μ¬λΆ νμΈ κΈ°λ₯ ꡬν.
+
+```java
+// SectionServiceTest.java
+
+package subway.application.section.service;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import subway.application.section.dto.SectionDTO;
+import subway.domain.line.Line;
+import subway.domain.line.LineDTO;
+import subway.domain.line.LineService;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionServiceTest {
+ private final LineDTO lineDTO = new LineDTO("line");
+ private final StationDTO sourceDTO = new StationDTO("source");
+ private final StationDTO sinkDTO = new StationDTO("sink");
+
+ private final LineService lineService = new LineService();
+ private final StationService stationService = new StationService();
+ private final SectionService sectionService = new SectionService();
+
+ @BeforeEach
+ public void setup() {
+ this.lineService.addLine(lineDTO);
+ this.stationService.addStation(sourceDTO);
+ this.stationService.addStation(sinkDTO);
+ }
+
+ @Test
+ public void addSection() {
+ Line line = this.lineService.findOneByName(lineDTO.getName());
+ Station source = this.stationService.findOneByName(sourceDTO.getName());
+ SectionDTO sectionDTO = new SectionDTO(lineDTO, sourceDTO, sinkDTO);
+ assertThat(this.sectionService.findAll()).hasSize(0);
+ assertThat(line.getSectionList()).hasSize(0);
+ assertThat(source.getSectionList()).hasSize(0);
+ this.sectionService.addSection(sectionDTO);
+ assertThat(this.sectionService.findAll()).hasSize(1);
+ assertThat(line.getSectionList()).hasSize(1);
+ assertThat(source.getSectionList()).hasSize(1);
+ }
+
+ @Test
+ public void addSection__AlreadyExistsSectionException() {
+ SectionDTO sectionDTO = new SectionDTO(lineDTO, sourceDTO, sinkDTO);
+ String message = "μ΄λ―Έ λ±λ‘λμ΄μλ ꡬκ°μ
λλ€.";
+ this.sectionService.addSection(sectionDTO);
+ assertThatThrownBy(() -> this.sectionService.addSection(sectionDTO)).isInstanceOf(
+ IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @AfterEach
+ public void init() {
+ lineService.deleteAll();
+ stationService.deleteAll();
+ sectionService.deleteAll();
+ }
+}
+```
+
+```java
+// SectionService.java
+
+package subway.application.section.service;
+
+import java.util.List;
+
+import subway.application.section.dto.SectionDTO;
+import subway.domain.line.Line;
+import subway.domain.line.LineService;
+import subway.domain.section.Section;
+import subway.domain.section.SectionRepository;
+import subway.domain.station.Station;
+import subway.domain.station.StationService;
+
+public class SectionService {
+ private static final String ALREADY_EXISTS_SECTION_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ ꡬκ°μ
λλ€.";
+
+ private final LineService lineService = new LineService();
+ private final StationService stationService = new StationService();
+
+ public void addSection(SectionDTO sectionDTO) {
+ Line line = this.lineService.findOneByName(sectionDTO.getLineDTO().getName());
+ Station source = this.stationService.findOneByName(sectionDTO.getSourceDTO().getName());
+ Station sink = this.stationService.findOneByName(sectionDTO.getSinkDTO().getName());
+ Section section = new Section(line, source, sink, sectionDTO.getDistance(), sectionDTO.getTime());
+ if (SectionRepository.exists(section)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_SECTION_MESSAGE);
+ }
+ SectionRepository.addSection(section);
+ line.addSection(section);
+ source.addSection(section);
+ }
+}
+```
+
+κ΅¬κ° μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// ValidationTest.java
+
+package subway.util;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class ValidationTest {
+ @Test
+ public void isNumeric() {
+ assertThat(Validation.isNumeric("1")).isEqualTo(true);
+ assertThat(Validation.isNumeric("")).isEqualTo(false);
+ assertThat(Validation.isNumeric("number")).isEqualTo(false);
+ }
+}
+```
+
+```java
+// Validation.java
+
+package subway.util;
+
+import java.util.regex.Pattern;
+
+public final class Validation {
+ private Validation() {
+ }
+
+ public static boolean isNumeric(String str) {
+ return str != null && !str.isEmpty() && Pattern.matches("^[0-9]*$", str);
+ }
+}
+```
+
+μ«μ νμμ λ¬Έμμ΄ νμΈ κΈ°λ₯ ꡬν.
+
+```java
+// SectionController.java
+
+package subway.presentation;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class SectionControllerTest {
+ private final SectionController sectionController = new SectionController();
+
+ @Test
+ public void addSection__InputEssentialDistanceException() {
+ String lineName = "line";
+ String sourceName = "source";
+ String sinkName = "sink";
+ String time = "0";
+ String message = "거리 μ
λ ₯μ νμμ
λλ€.";
+ assertThatThrownBy(
+ () -> this.sectionController.addSection(lineName, sourceName, sinkName, null, time)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ assertThatThrownBy(
+ () -> this.sectionController.addSection(lineName, sourceName, sinkName, "", time)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void addSection__OnlyPossibleNumericInputDistanceException() {
+ String lineName = "line";
+ String sourceName = "source";
+ String sinkName = "sink";
+ String time = "0";
+ String message = "거리λ μ«μ νμμ μ
λ ₯λ§ κ°λ₯ν©λλ€.";
+ assertThatThrownBy(
+ () -> this.sectionController.addSection(lineName, sourceName, sinkName, "distance", time)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void addSection__InputEssentialTimeException() {
+ String lineName = "line";
+ String sourceName = "source";
+ String sinkName = "sink";
+ String distance = "0";
+ String message = "μκ° μ
λ ₯μ νμμ
λλ€.";
+ assertThatThrownBy(
+ () -> this.sectionController.addSection(lineName, sourceName, sinkName, distance, null)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ assertThatThrownBy(
+ () -> this.sectionController.addSection(lineName, sourceName, sinkName, distance, "")).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void addSection__OnlyPossibleNumericInputTimeException() {
+ String lineName = "line";
+ String sourceName = "source";
+ String sinkName = "sink";
+ String distance = "0";
+ String message = "μκ°μ μ«μ νμμ μ
λ ₯λ§ κ°λ₯ν©λλ€.";
+ assertThatThrownBy(
+ () -> this.sectionController.addSection(lineName, sourceName, sinkName, distance, "time")).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+}
+```
+
+```java
+// SectionController.java
+
+package subway.presentation;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.service.SectionService;
+import subway.domain.line.LineDTO;
+import subway.domain.station.StationDTO;
+import subway.util.Validation;
+
+public class SectionController {
+ private static final String INPUT_ESSENTIAL_DISTANCE_MESSAGE = "거리 μ
λ ₯μ νμμ
λλ€.";
+ private static final String ONLY_POSSIBLE_NUMERIC_INPUT_DISTANCE_MESSAGE = "거리λ μ«μ νμμ μ
λ ₯λ§ κ°λ₯ν©λλ€.";
+ private static final String INPUT_ESSENTIAL_TIME_MESSAGE = "μκ° μ
λ ₯μ νμμ
λλ€.";
+ private static final String ONLY_POSSIBLE_NUMERIC_INPUT_TIME_MESSAGE = "μκ°μ μ«μ νμμ μ
λ ₯λ§ κ°λ₯ν©λλ€.";
+
+ private final SectionService sectionService = new SectionService();
+
+ public void addSection(String lineName, String sourceName, String sinkName, String _distance, String _time) {
+ LineDTO lineDTO = new LineDTO(lineName);
+ StationDTO sourceDTO = new StationDTO(sourceName);
+ StationDTO sinkDTO = new StationDTO(sinkName);
+ this.validateDistance(_distance);
+ int distance = Integer.parseInt(_distance);
+ this.validateTime(_time);
+ int time = Integer.parseInt(_time);
+ SectionDTO sectionDTO = new SectionDTO(lineDTO, sourceDTO, sinkDTO, distance, time);
+ this.sectionService.addSection(sectionDTO);
+ }
+
+ private void validateDistance(String distance) {
+ if (distance == null || distance.trim().isEmpty()) {
+ throw new IllegalArgumentException(INPUT_ESSENTIAL_DISTANCE_MESSAGE);
+ }
+ if (!Validation.isNumeric(distance)) {
+ throw new IllegalArgumentException(ONLY_POSSIBLE_NUMERIC_INPUT_DISTANCE_MESSAGE);
+ }
+ }
+
+ private void validateTime(String time) {
+ if (time == null || time.trim().isEmpty()) {
+ throw new IllegalArgumentException(INPUT_ESSENTIAL_TIME_MESSAGE);
+ }
+ if (!Validation.isNumeric(time)) {
+ throw new IllegalArgumentException(ONLY_POSSIBLE_NUMERIC_INPUT_TIME_MESSAGE);
+ }
+ }
+}
+```
+
+μ μ΄ κ³μΈ΅μ κ΅¬κ° μΆκ° κΈ°λ₯ λ§€ν.
+
+## 4. μ΅λ¨ 거리 λ
Έλ, κ°μ μΆκ°
+
+### 4-1. λ
Έλ
+
+```java
+// ShortDistanceService.java
+
+package subway.application.section.service;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.station.Station;
+
+class shortDistanceService {
+ private static final DirectedWeightedMultigraph graph = new DirectedWeightedMultigraph<>(
+ DefaultWeightedEdge.class);
+
+ protected Set findAllNode() {
+ return Collections.unmodifiableSet(graph.vertexSet());
+ }
+
+ protected void deleteAllNode() {
+ Set nodes = new HashSet<>(this.findAllNode());
+ graph.removeAllVertices(nodes);
+ }
+}
+```
+
+κΈ°λ³Έ μ 체 μ‘°ν λ° μμ κΈ°λ₯ μμ±.
+
+```java
+// ShortDistanceServiceTest.java
+
+package subway.application.section.service;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import subway.domain.station.Station;
+
+class shortDistanceServiceTest {
+ private final Station source = new Station("source");
+
+ private final ShortDistanceService shortDistanceService = new ShortDistanceService();
+
+ @Test
+ public void addNode() {
+ assertThat(this.shortDistanceService.findAllNode()).hasSize(0);
+ this.shortDistanceService.addNode(this.source);
+ assertThat(this.shortDistanceService.findAllNode()).hasSize(1);
+ }
+
+ @Test
+ public void addNode_AlreadyExistsNodeException() {
+ String message = "μ΄λ―Έ λ±λ‘λμ΄μλ λ
Έλμ
λλ€.";
+ this.shortDistanceService.addNode(this.source);
+ assertThatThrownBy(() -> this.shortDistanceService.addNode(this.source)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @AfterEach
+ public void init() {
+ this.shortDistanceService.deleteAllNode();
+ }
+}
+```
+
+```java
+// ShortDistanceService.java
+
+package subway.application.section.service;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.station.Station;
+
+class shortDistanceService {
+ private static final String ALREADY_EXISTS_NODE_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ λ
Έλμ
λλ€.";
+
+ protected void addNode(Station station) {
+ if (graph.containsVertex(station)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_NODE_MESSAGE);
+ }
+ graph.addVertex(station);
+ }
+}
+```
+
+λ
Έμ μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// SectionService.java
+
+package subway.application.section.service;
+
+import java.util.List;
+
+import subway.application.section.dto.SectionDTO;
+import subway.domain.line.Line;
+import subway.domain.line.LineService;
+import subway.domain.section.Section;
+import subway.domain.section.SectionRepository;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionService {
+ private final ShortDistanceService shortDistanceService = new ShortDistanceService();
+
+ public void addNode(StationDTO stationDTO) {
+ Station station = this.stationService.findOneByName(stationDTO.getName());
+ this.shortDistanceService.addNode(station);
+ }
+}
+```
+
+무λΆλ³ν λ
Έλ μΆκ°λ₯Ό λ°©μ§νκΈ° μν΄ Section Service κ³μΈ΅μ ν΅ν΄μλ§ μ²λ¦¬λλλ‘ ν¨.
+
+## 4-2. κ°μ
+
+```java
+// ShortDistanceService.java
+
+package subway.application.section.service;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.station.Station;
+
+class shortDistanceService {
+ protected Set findAllEdge() {
+ return Collections.unmodifiableSet(graph.edgeSet());
+ }
+
+ protected void deleteAllEdge() {
+ graph.removeAllEdges(graph.edgeSet());
+ }
+
+ protected void deleteAll() {
+ this.deleteAllEdge();
+ this.deleteAllNode();
+ }
+}
+```
+
+κΈ°λ³Έ μ 체 μ‘°ν λ° μμ κΈ°λ₯ μμ±.
+
+```java
+// ShortDistanceServiceTest.java
+
+package subway.application.section.service;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+class shortDistanceServiceTest {
+ private final Line line = new Line("line");
+ private final Station sink = new Station("sink");
+
+ @Test
+ public void addEdge_NotExistsSourceNodeException() {
+ Section section = new Section(this.line, this.source, this.sink, 0, 0);
+ String message = "μ‘΄μ¬νμ§ μμ μμλ
Έλμ
λλ€.";
+ this.shortDistanceService.addNode(this.sink);
+ assertThatThrownBy(() -> this.shortDistanceService.addEdge(section)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void addEdge_NotExistsSinkNodeException() {
+ Section section = new Section(this.line, this.source, this.sink, 0, 0);
+ String message = "μ‘΄μ¬νμ§ μμ μ’
λ£λ
Έλμ
λλ€.";
+ this.shortDistanceService.addNode(this.source);
+ assertThatThrownBy(() -> this.shortDistanceService.addEdge(section)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void addEdge_AlreadyExistsEdgeException() {
+ Section section = new Section(this.line, this.source, this.sink, 0, 0);
+ String message = "μ΄λ―Έ λ±λ‘λμ΄μλ κ°μ μ
λλ€.";
+ this.shortDistanceService.addNode(this.source);
+ this.shortDistanceService.addNode(this.sink);
+ this.shortDistanceService.addEdge(section);
+ assertThatThrownBy(() -> this.shortDistanceService.addEdge(section)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @AfterEach
+ public void init() {
+ this.shortDistanceService.deleteAll();
+ }
+}
+```
+
+```java
+// ShortDistanceService.java
+
+package subway.application.section.service;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+class shortDistanceService {
+ private static final String NOT_EXISTS_SOURCE_NODE_MESSAGE = "μ‘΄μ¬νμ§ μμ μμλ
Έλμ
λλ€.";
+ private static final String NOT_EXISTS_SINK_NODE_MESSAGE = "μ‘΄μ¬νμ§ μμ μ’
λ£λ
Έλμ
λλ€.";
+ private static final String ALREADY_EXISTS_EDGE_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ κ°μ μ
λλ€.";
+
+ protected void addEdge(Section section) {
+ Station source = section.getSource();
+ Station sink = section.getSink();
+ this.validateSource(source);
+ this.validateSink(sink);
+ if (graph.containsEdge(source, sink)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_EDGE_MESSAGE);
+ }
+ graph.setEdgeWeight(graph.addEdge(source, sink), section.getDistance());
+ }
+
+ private void validateSource(Station source) {
+ if (!graph.containsVertex(source)) {
+ throw new IllegalArgumentException(NOT_EXISTS_SOURCE_NODE_MESSAGE);
+ }
+ }
+
+ private void validateSink(Station sink) {
+ if (!graph.containsVertex(sink)) {
+ throw new IllegalArgumentException(NOT_EXISTS_SINK_NODE_MESSAGE);
+ }
+ }
+}
+```
+
+κ°μ μΆκ° κΈ°λ₯ ꡬν.
+
+```java
+// SectionService.java
+
+package subway.application.section.service;
+
+import java.util.List;
+
+import subway.application.section.dto.SectionDTO;
+import subway.domain.line.Line;
+import subway.domain.line.LineService;
+import subway.domain.section.Section;
+import subway.domain.section.SectionRepository;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionService {
+ public void addSection(SectionDTO sectionDTO) {
+ Line line = this.lineService.findOneByName(sectionDTO.getLineDTO().getName());
+ Station source = this.stationService.findOneByName(sectionDTO.getSourceDTO().getName());
+ Station sink = this.stationService.findOneByName(sectionDTO.getSinkDTO().getName());
+ Section section = new Section(line, source, sink, sectionDTO.getDistance(), sectionDTO.getTime());
+ if (SectionRepository.exists(section)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_SECTION_MESSAGE);
+ }
+ SectionRepository.addSection(section);
+ +shortDistanceService.addEdge(section);
+ line.addSection(section);
+ source.addSection(section);
+ }
+}
+```
+
+무λΆλ³ν κ°μ μΆκ°λ₯Ό λ°©μ§νκΈ° μν΄ Section Service κ³μΈ΅μ ν΅ν΄μλ§ μ²λ¦¬λλλ‘ ν¨.
+
+## 5. μ΅λ¨ 거리 κ²½λ‘ κ΅¬νκΈ°
+
+```java
+// ShortDistanceServiceTest.java
+
+package subway.application.section.service;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.jgrapht.GraphPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+class shortDistanceServiceTest {
+ @Test
+ public void compute__NotExistsSourceNodeException() {
+ String message = "μ‘΄μ¬νμ§ μμ μμλ
Έλμ
λλ€.";
+ this.shortDistanceService.addNode(this.sink);
+ assertThatThrownBy(() -> this.shortDistanceService.compute(this.source, this.sink)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void compute__NotExistsSinkNodeException() {
+ String message = "μ‘΄μ¬νμ§ μμ μ’
λ£λ
Έλμ
λλ€.";
+ this.shortDistanceService.addNode(this.source);
+ assertThatThrownBy(() -> this.shortDistanceService.compute(this.source, this.sink)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+}
+```
+
+```java
+// ShortDistanceService.java
+
+package subway.application.section.service;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jgrapht.GraphPath;
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+class shortDistanceService {
+ protected GraphPath compute(Station source, Station sink) {
+ this.validateSource(source);
+ this.validateSink(sink);
+ DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath<>(graph);
+ return dijkstraShortestPath.getPath(source, sink);
+ }
+}
+```
+
+μ΅λ¨ 거리 κ²½λ‘ λ°ν κΈ°λ₯ ꡬν.
+
+```java
+// ShortCostRequestTest.java
+
+package subway.application.section.dto;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.station.StationDTO;
+
+public class ShortCostRequestTest {
+ @Test
+ public void constructor__SourceStationInfoEssentialException() {
+ StationDTO sinkDTO = new StationDTO("sink");
+ String message = "μμμ μ 보λ νμμ
λλ€.";
+ assertThatThrownBy(() -> new ShortCostRequest(null, sinkDTO)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @Test
+ public void constructor__SinkStationInfoEssentialException() {
+ StationDTO sourceDTO = new StationDTO("source");
+ String message = "μ’
λ£μ μ 보λ νμμ
λλ€.";
+ assertThatThrownBy(() -> new ShortCostRequest(sourceDTO, null)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @Test
+ public void constructor__SameSourceAndSinkStationException() {
+ StationDTO sourceDTO = new StationDTO("same");
+ StationDTO sinkDTO = new StationDTO("same");
+ String message = "μΆλ°μκ³Ό λμ°©μμ΄ λμΌν©λλ€.";
+ assertThatThrownBy(() -> new ShortCostRequest(sourceDTO, sinkDTO)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// StationDTO.java
+
+package subway.domain.station;
+
+import java.util.Objects;
+
+public class StationDTO {
+ @Override
+ public boolean equals(Object object) {
+ if (this == object)
+ return true;
+ if (!(object instanceof StationDTO))
+ return false;
+ StationDTO other = (StationDTO)object;
+ return Objects.equals(this.name, other.name);
+ }
+}
+```
+
+λμΌ μ²΄ν¬λ₯Ό μν΄ μ΄λ¦ κΈ°μ€μΌλ‘ λΉκ΅λλλ‘ equal μ€λ²λΌμ΄λ©.
+
+```java
+// ShortCostRequest.java
+
+package subway.application.section.dto;
+
+import subway.domain.station.StationDTO;
+
+public class ShortCostRequest {
+ private static final String SOURCE_STATION_INFO_ESSENTIAL_MESSAGE = "μμμ μ 보λ νμμ
λλ€.";
+ private static final String SINK_STATION_INFO_ESSENTIAL_MESSAGE = "μ’
λ£μ μ 보λ νμμ
λλ€.";
+ private static final String SAME_SOURCE_AND_SINK_STATION_MESSAGE = "μΆλ°μκ³Ό λμ°©μμ΄ λμΌν©λλ€.";
+
+ private final StationDTO sourceDTO;
+ private final StationDTO sinkDTO;
+
+ public ShortCostRequest(StationDTO sourceDTO, StationDTO sinkDTO) {
+ this.validate(sourceDTO, sinkDTO);
+ this.sourceDTO = sourceDTO;
+ this.sinkDTO = sinkDTO;
+ }
+
+ private void validate(StationDTO sourceDTO, StationDTO sinkDTO) {
+ if (sourceDTO == null) {
+ throw new IllegalArgumentException(SOURCE_STATION_INFO_ESSENTIAL_MESSAGE);
+ }
+ if (sinkDTO == null) {
+ throw new IllegalArgumentException(SINK_STATION_INFO_ESSENTIAL_MESSAGE);
+ }
+ if (sourceDTO.equals(sinkDTO)) {
+ throw new IllegalArgumentException(SAME_SOURCE_AND_SINK_STATION_MESSAGE);
+ }
+ }
+
+ public StationDTO getSourceDTO() {
+ return sourceDTO;
+ }
+
+ public StationDTO getSinkDTO() {
+ return sinkDTO;
+ }
+}
+```
+
+μ΅λ¨(μ΅μ) κ²½λ‘ κ³μ° μμ² DTO ꡬν.
+
+```java
+// ShortCostResponseTest.java
+
+package subway.application.section.dto;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+public class ShortCostResponseTest {
+ @Test
+ public void constructor() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ List stationList = Arrays.asList(source, sink);
+ int distance = 1;
+ int time = 8;
+ Section section = new Section(line, source, sink, distance, time);
+ source.addSection(section);
+ ShortCostResponse shortCostResponse = new ShortCostResponse(stationList);
+ assertThat(shortCostResponse.getTotalDistance()).isEqualTo(distance);
+ assertThat(shortCostResponse.getTotalTime()).isEqualTo(time);
+ assertThat(shortCostResponse.getStationNameList()).containsExactly(source.getName(), sink.getName());
+ }
+}
+```
+
+```java
+// StationTest.java
+
+package subway.domain.station;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+import subway.domain.line.Line;
+import subway.domain.section.Section;
+
+public class StationTest {
+ @Test
+ public void findDistanceTo() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ int distance = 1;
+ Section section = new Section(line, source, sink, distance, 0);
+ source.addSection(section);
+ assertThat(source.findDistanceTo(sink)).isEqualTo(distance);
+ }
+
+ @Test
+ public void findDistanceTo__NotExistsPathToSinkStationException() {
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ String message = "μ’
λ£μκΉμ§ κ²½λ‘κ° μ‘΄μ¬νμ§ μμ΅λλ€.";
+ assertThatThrownBy(() -> source.findDistanceTo(sink)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @Test
+ public void findTimeTo() {
+ Line line = new Line("line");
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ int time = 8;
+ Section section = new Section(line, source, sink, 0, time);
+ source.addSection(section);
+ assertThat(source.findTimeTo(sink)).isEqualTo(time);
+ }
+
+ @Test
+ public void findTimeTo__NotExistsPathToSinkStationException() {
+ Station source = new Station("source");
+ Station sink = new Station("sink");
+ String message = "μ’
λ£μκΉμ§ κ²½λ‘κ° μ‘΄μ¬νμ§ μμ΅λλ€.";
+ assertThatThrownBy(() -> source.findTimeTo(sink)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+}
+```
+
+```java
+// Station.java
+
+package subway.domain.station;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import subway.domain.section.Section;
+
+public class Station {
+ private static final String NOT_EXISTS_PATH_TO_SINK_STATION_MESSAGE = "μ’
λ£μκΉμ§ κ²½λ‘κ° μ‘΄μ¬νμ§ μμ΅λλ€.";
+
+ public int findDistanceTo(Station sink) {
+ return this.sectionList.stream()
+ .filter(section -> section.getSink() == sink)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_PATH_TO_SINK_STATION_MESSAGE))
+ .getDistance();
+ }
+
+ public int findTimeTo(Station sink) {
+ return this.sectionList.stream()
+ .filter(section -> section.getSink() == sink)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_PATH_TO_SINK_STATION_MESSAGE))
+ .getTime();
+ }
+}
+```
+
+μ§μ κ²½λ‘κΉμ§μ 거리μ μκ°μ κ³μ°νκΈ° μν΄ κΈ°λ₯ ꡬν.
+
+```java
+// ShortCostResponse.java
+
+package subway.application.section.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import subway.domain.station.Station;
+
+public class ShortCostResponse {
+ private int totalDistance;
+ private int totalTime;
+ private final List stationNameList;
+
+ public ShortCostResponse(List stationList) {
+ this.stationNameList = new ArrayList<>();
+ int length = stationList.size();
+ for (int index = 0; index < length; index++) {
+ Station source = stationList.get(index);
+ if (index + 1 < length) {
+ Station sink = stationList.get(index + 1);
+ totalDistance += source.findDistanceTo(sink);
+ totalTime += source.findTimeTo(sink);
+ }
+ stationNameList.add(source.getName());
+ }
+ }
+
+ public int getTotalDistance() {
+ return totalDistance;
+ }
+
+ public void setTotalDistance(int totalDistance) {
+ this.totalDistance = totalDistance;
+ }
+
+ public int getTotalTime() {
+ return totalTime;
+ }
+
+ public void setTotalTime(int totalTime) {
+ this.totalTime = totalTime;
+ }
+
+ public List getStationNameList() {
+ return stationNameList;
+ }
+}
+```
+
+μ§μ κ²½λ‘κΉμ§ μ΄ κ±°λ¦¬, μκ°κ³Ό μμΉ μ 보λ₯Ό λ°ν ν μ μλλ‘ κ΅¬ν.
+
+```java
+// SectionServiceTest.java
+
+package subway.application.section.service;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.dto.ShortCostRequest;
+import subway.application.section.dto.ShortCostResponse;
+import subway.domain.line.Line;
+import subway.domain.line.LineDTO;
+import subway.domain.line.LineService;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionServiceTest {
+ private final ShortDistanceService shortDistanceService = new ShortDistanceService();
+
+ @Test
+ public void computeShortDistance() {
+ int distance = 1;
+ int time = 8;
+ SectionDTO sectionDTO = new SectionDTO(this.lineDTO, this.sourceDTO, this.sinkDTO, distance, time);
+ ShortCostRequest shortCostRequest = new ShortCostRequest(this.sourceDTO, this.sinkDTO);
+ this.sectionService.addNode(this.sourceDTO);
+ this.sectionService.addNode(this.sinkDTO);
+ this.sectionService.addSection(sectionDTO);
+ ShortCostResponse shortCostResponse = this.sectionService.computeShortDistance(shortCostRequest);
+ assertThat(shortCostResponse.getTotalDistance()).isEqualTo(distance);
+ assertThat(shortCostResponse.getTotalTime()).isEqualTo(time);
+ assertThat(shortCostResponse.getStationNameList()).containsExactly(this.sourceDTO.getName(),
+ this.sinkDTO.getName());
+ }
+
+ @Test
+ public void computeShortDistance__NotExistsSourceStationException() {
+ StationDTO otherDTO = new StationDTO("other");
+ ShortCostRequest shortCostRequest = new ShortCostRequest(otherDTO, this.sinkDTO);
+ String message = "μ‘΄μ¬νμ§ μμ μμμμ
λλ€.";
+ assertThatThrownBy(() -> this.sectionService.computeShortDistance(shortCostRequest)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void computeShortDistance__NotExistsSinkStationException() {
+ StationDTO otherDTO = new StationDTO("other");
+ ShortCostRequest shortCostRequest = new ShortCostRequest(this.sourceDTO, otherDTO);
+ String message = "μ‘΄μ¬νμ§ μμ μ’
λ£μμ
λλ€.";
+ assertThatThrownBy(() -> this.sectionService.computeShortDistance(shortCostRequest)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @Test
+ public void computeShortDistance__NotConnectedSourceAndSinkStationException() {
+ ShortCostRequest shortCostRequest = new ShortCostRequest(this.sourceDTO, this.sinkDTO);
+ String message = "μμ μ§μ κ³Ό μ’
λ£μμ΄ μ°κ²°λμ΄ μμ§ μμ΅λλ€.";
+ this.sectionService.addNode(this.sourceDTO);
+ this.sectionService.addNode(this.sinkDTO);
+ assertThatThrownBy(() -> this.sectionService.computeShortDistance(shortCostRequest)).isInstanceOf(
+ IllegalArgumentException.class).hasMessage(message);
+ }
+
+ @AfterEach
+ public void init() {
+ this.lineService.deleteAll();
+ this.stationService.deleteAll();
+ this.sectionService.deleteAll();
+ this.shortDistanceService.deleteAll();
+ }
+}
+```
+
+```java
+// StationService.java
+
+package subway.domain.station;
+
+import java.util.List;
+import java.util.Optional;
+
+public class StationService {
+ public Optional findByName(String name) {
+ return StationRepository.findByName(name);
+ }
+}
+```
+
+```java
+// SectionService.java
+
+package subway.application.section.service;
+
+import java.util.List;
+
+import org.jgrapht.GraphPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.dto.ShortCostRequest;
+import subway.application.section.dto.ShortCostResponse;
+import subway.domain.line.Line;
+import subway.domain.line.LineService;
+import subway.domain.section.Section;
+import subway.domain.section.SectionRepository;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionService {
+ private static final String NOT_EXISTS_SOURCE_STATION_MESSAGE = "μ‘΄μ¬νμ§ μμ μμμμ
λλ€.";
+ private static final String NOT_EXISTS_SINK_STATION_MESSAGE = "μ‘΄μ¬νμ§ μμ μ’
λ£μμ
λλ€.";
+ private static final String NOT_CONNECTED_SOURCE_AND_SINK_STATION_MESSAGE = "μμ μ§μ κ³Ό μ’
λ£μμ΄ μ°κ²°λμ΄ μμ§ μμ΅λλ€.";
+
+ public ShortCostResponse computeShortDistance(ShortCostRequest shortCostRequest) {
+ Station source = this.stationService.findByName(shortCostRequest.getSourceDTO().getName())
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_SOURCE_STATION_MESSAGE));
+ Station sink = this.stationService.findByName(shortCostRequest.getSinkDTO().getName())
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_SINK_STATION_MESSAGE));
+ GraphPath graphPath = this.shortDistanceService.compute(source, sink);
+ if (graphPath == null) {
+ throw new IllegalArgumentException(NOT_CONNECTED_SOURCE_AND_SINK_STATION_MESSAGE);
+ }
+ return new ShortCostResponse(graphPath.getVertexList());
+ }
+}
+```
+
+μ΅λ¨ 거리 κ³μ° ꡬν.
+
+```java
+// SectionController.java
+
+package subway.presentation;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.dto.ShortCostRequest;
+import subway.application.section.dto.ShortCostResponse;
+import subway.application.section.service.SectionService;
+import subway.domain.line.LineDTO;
+import subway.domain.station.StationDTO;
+import subway.util.Validation;
+
+public class SectionController {
+ public ShortCostResponse computeShortDistance(String sourceName, String sinkName) {
+ ShortCostRequest shortCostRequest = this.createShortCostRequest(sourceName, sinkName);
+ return this.sectionService.computeShortDistance(shortCostRequest);
+ }
+
+ private ShortCostRequest createShortCostRequest(String sourceName, String sinkName) {
+ StationDTO sourceDTO = new StationDTO(sourceName);
+ StationDTO sinkDTO = new StationDTO(sinkName);
+ return new ShortCostRequest(sourceDTO, sinkDTO);
+ }
+}
+```
+
+μ μ΄ κ³μΈ΅μ μ΅λ¨ 거리 κ³μ° κΈ°λ₯ λ§€ν.
+
+## 6. κ·Έλν κ΄λ ¨ κΈ°λ₯ ν΅ν©
+
+```java
+// ShortCostService.java
+
+package subway.application.section.service;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jgrapht.GraphPath;
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.AbstractBaseGraph;
+import org.jgrapht.graph.DefaultWeightedEdge;
+
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+abstract class ShortCostService {
+ private final AbstractBaseGraph graph;
+
+ public ShortCostService(AbstractBaseGraph graph) {
+ this.graph = graph;
+ }
+
+ private static final String ALREADY_EXISTS_NODE_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ λ
Έλμ
λλ€.";
+ private static final String NOT_EXISTS_SOURCE_NODE_MESSAGE = "μ‘΄μ¬νμ§ μμ μμλ
Έλμ
λλ€.";
+ private static final String NOT_EXISTS_SINK_NODE_MESSAGE = "μ‘΄μ¬νμ§ μμ μ’
λ£λ
Έλμ
λλ€.";
+ private static final String ALREADY_EXISTS_EDGE_MESSAGE = "μ΄λ―Έ λ±λ‘λμ΄μλ κ°μ μ
λλ€.";
+
+ protected Set findAllNode() {
+ return Collections.unmodifiableSet(graph.vertexSet());
+ }
+
+ protected Set findAllEdge() {
+ return Collections.unmodifiableSet(graph.edgeSet());
+ }
+
+ protected void addNode(Station station) {
+ if (graph.containsVertex(station)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_NODE_MESSAGE);
+ }
+ graph.addVertex(station);
+ }
+
+ protected void addEdge(Section section) {
+ Station source = section.getSource();
+ Station sink = section.getSink();
+ this.validateSource(source);
+ this.validateSink(sink);
+ if (graph.containsEdge(source, sink)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_EDGE_MESSAGE);
+ }
+ graph.setEdgeWeight(graph.addEdge(source, sink), this.getWeight(section));
+ }
+
+ protected abstract double getWeight(Section section);
+
+ protected GraphPath compute(Station source, Station sink) {
+ this.validateSource(source);
+ this.validateSink(sink);
+ DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath<>(graph);
+ return dijkstraShortestPath.getPath(source, sink);
+ }
+
+ protected void validateSource(Station source) {
+ if (!graph.containsVertex(source)) {
+ throw new IllegalArgumentException(NOT_EXISTS_SOURCE_NODE_MESSAGE);
+ }
+ }
+
+ protected void validateSink(Station sink) {
+ if (!graph.containsVertex(sink)) {
+ throw new IllegalArgumentException(NOT_EXISTS_SINK_NODE_MESSAGE);
+ }
+ }
+
+ protected void deleteAllNode() {
+ Set nodes = new HashSet<>(this.findAllNode());
+ graph.removeAllVertices(nodes);
+ }
+
+ protected void deleteAllEdge() {
+ graph.removeAllEdges(graph.edgeSet());
+ }
+
+ protected void deleteAll() {
+ this.deleteAllEdge();
+ this.deleteAllNode();
+ }
+}
+
+```
+
+```java
+// ShortDistanceService.java
+
+package subway.application.section.service;
+
+import org.jgrapht.graph.AbstractBaseGraph;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+class ShortDistanceService extends ShortCostService {
+ private static final AbstractBaseGraph graph = new DirectedWeightedMultigraph<>(
+ DefaultWeightedEdge.class);
+
+ public ShortDistanceService() {
+ super(graph);
+ }
+
+ @Override
+ protected double getWeight(Section section) {
+ return section.getDistance();
+ }
+}
+```
+
+μ΄νμ ꡬνλ μ΅μ μκ° κ²½λ‘μ κ·Έλν κ΄λ ¨ κΈ°λ₯μ μ μ¬νκΈ° λλ¬Έμ λ°λ‘ μΆμ κ³΅ν΅ ν΄λμ€λ₯Ό μμ±νμ¬ μ½λ μ€λ³΅ λ°©μ§.
+
+## 7. μ΅μ μκ° κ²½λ‘ κ΅¬νκΈ°
+
+```java
+// ShortTimeService.java
+
+package subway.application.section.service;
+
+import org.jgrapht.graph.AbstractBaseGraph;
+import org.jgrapht.graph.DefaultWeightedEdge;
+import org.jgrapht.graph.DirectedWeightedMultigraph;
+
+import subway.domain.section.Section;
+import subway.domain.station.Station;
+
+class ShortTimeService extends ShortCostService {
+ private static final AbstractBaseGraph graph = new DirectedWeightedMultigraph<>(
+ DefaultWeightedEdge.class);
+
+ public ShortTimeService() {
+ super(graph);
+ }
+
+ @Override
+ protected double getWeight(Section section) {
+ return section.getTime();
+ }
+}
+```
+
+μ΅μ μκ° κ²½λ‘ λ°ν κΈ°λ₯ ꡬν.
+
+```java
+// SectionService.java
+
+package subway.application.section.service;
+
+import java.util.List;
+
+import org.jgrapht.GraphPath;
+import org.jgrapht.graph.DefaultWeightedEdge;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.dto.ShortCostRequest;
+import subway.application.section.dto.ShortCostResponse;
+import subway.domain.line.Line;
+import subway.domain.line.LineService;
+import subway.domain.section.Section;
+import subway.domain.section.SectionRepository;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionService {
+ private final ShortTimeService shortTimeService = new ShortTimeService();
+
+ public void addNode(StationDTO stationDTO) {
+ Station station = this.stationService.findOneByName(stationDTO.getName());
+ this.shortDistanceService.addNode(station);
++ this.shortTimeService.addNode(station);
+ }
+
+ public void addSection(SectionDTO sectionDTO) {
+ Line line = this.lineService.findOneByName(sectionDTO.getLineDTO().getName());
+ Station source = this.stationService.findOneByName(sectionDTO.getSourceDTO().getName());
+ Station sink = this.stationService.findOneByName(sectionDTO.getSinkDTO().getName());
+ Section section = new Section(line, source, sink, sectionDTO.getDistance(), sectionDTO.getTime());
+ if (SectionRepository.exists(section)) {
+ throw new IllegalArgumentException(ALREADY_EXISTS_SECTION_MESSAGE);
+ }
+ SectionRepository.addSection(section);
+ this.shortDistanceService.addEdge(section);
++ this.shortTimeService.addEdge(section);
+ line.addSection(section);
+ source.addSection(section);
+ }
+
+ public ShortCostResponse computeShortTime(ShortCostRequest shortCostRequest) {
+ Station source = this.stationService.findByName(shortCostRequest.getSourceDTO().getName())
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_SOURCE_STATION_MESSAGE));
+ Station sink = this.stationService.findByName(shortCostRequest.getSinkDTO().getName())
+ .orElseThrow(() -> new IllegalArgumentException(NOT_EXISTS_SINK_STATION_MESSAGE));
+ GraphPath graphPath = this.shortDistanceService.compute(source, sink);
+ if (graphPath == null) {
+ throw new IllegalArgumentException(NOT_CONNECTED_SOURCE_AND_SINK_STATION_MESSAGE);
+ }
+ return new ShortCostResponse(graphPath.getVertexList());
+ }
+}
+```
+
+μ΅μ μκ° κ·Έλνλ κ°μ΄ λ
Έλ, κ°μ μΆκ°μ ν¬ν¨.
+
+μ΅μ μκ° κ³μ° ꡬν.
+
+```java
+// SectionServiceTest.java
+
+package subway.application.section.service;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.dto.ShortCostRequest;
+import subway.application.section.dto.ShortCostResponse;
+import subway.domain.line.Line;
+import subway.domain.line.LineDTO;
+import subway.domain.line.LineService;
+import subway.domain.station.Station;
+import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class SectionServiceTest {
+ private final ShortTimeService shortTimeService = new ShortTimeService();
+
+ @AfterEach
+ public void init() {
+ this.lineService.deleteAll();
+ this.stationService.deleteAll();
+ this.sectionService.deleteAll();
+ this.shortDistanceService.deleteAll();
++ this.shortTimeService.deleteAll();
+ }
+}
+```
+
+μ΅μ μκ° κΈ°λ₯ λμ
μΌλ‘ μΈν ν
μ€νΈ μ΄κΈ°ν μμ€ λ³κ²½.
+
+```java
+// SectionController.java
+
+package subway.presentation;
+
+import subway.application.section.dto.SectionDTO;
+import subway.application.section.dto.ShortCostRequest;
+import subway.application.section.dto.ShortCostResponse;
+import subway.application.section.service.SectionService;
+import subway.domain.line.LineDTO;
+import subway.domain.station.StationDTO;
+import subway.util.Validation;
+
+public class SectionController {
+ public ShortCostResponse computeShortTime(String sourceName, String sinkName) {
+ ShortCostRequest shortCostRequest = this.createShortCostRequest(sourceName, sinkName);
+ return this.sectionService.computeShortTime(shortCostRequest);
+ }
+}
+```
+
+μ μ΄ κ³μΈ΅μ μ΅μ μκ° κ³μ° κΈ°λ₯ λ§€ν.
+
+```java
+// StationController.java
+
+package subway.presentation;
+
+import subway.application.section.service.SectionService;import subway.domain.station.StationDTO;
+import subway.domain.station.StationService;
+
+public class StationController {
+ private final SectionService sectionService = new SectionService();
+
+ public void addStation(String name) {
+ StationDTO stationDTO = new StationDTO(name);
+ stationService.addStation(stationDTO);
++ this.sectionService.addNode(stationDTO);
+ }
+}
+```
+
+μκ³Ό κ°μ΄ λ
Έλλ κ°μ΄ μΆκ°λλλ‘ λ³κ²½.
+
+## 8. Screen Layer Skeleton
+
+### Ui
+
+```java
+// Console.java
+
+package subway.screen.ui;
+
+import java.io.PrintStream;
+import java.util.Scanner;
+
+public final class Console {
+ private static final Scanner scanner = new Scanner(System.in);
+ private static final PrintStream printer = System.out;
+
+ private static final String HEADER_OUTPUT_FORMAT = "## %s";
+ private static final String INFO_OUTPUT_FORMAT = "[INFO] %s";
+ private static final String ERROR_OUTPUT_FORMAT = "[ERROR] %s";
+
+ public static String readline() {
+ return scanner.nextLine();
+ }
+
+ public static void println() {
+ printer.println();
+ }
+
+ public static void println(String message) {
+ printer.println(message);
+ }
+
+ public static void printHeader(String message) {
+ println(String.format(HEADER_OUTPUT_FORMAT, message));
+ }
+
+ public static void printInfo(String message) {
+ println(String.format(INFO_OUTPUT_FORMAT, message));
+ }
+
+ public static void printError(String message) {
+ println(String.format(ERROR_OUTPUT_FORMAT, message));
+ }
+
+}
+```
+
+μ½μ μ
μΆλ ₯ κΈ°λ₯ ꡬν.
+
+### View
+
+```java
+// View.java
+
+package subway.screen.view;
+
+public interface View {
+ String title();
+ void show();
+}
+```
+
+νλ©΄ κΈ°λ³Έ κΈ°λ₯ μ μ.
+
+```java
+// MenuTest.java
+
+package subway.screen.view;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+public class MenuTest {
+ private static class ClassMenu implements Menu {
+ @Override
+ public String getCommand() {
+ return "";
+ }
+
+ @Override
+ public String getName() {
+ return "";
+ }
+ }
+
+ private enum EnumMenu implements Menu {
+ ITEM("command", "name");
+
+ private final String command;
+ private final String name;
+
+ EnumMenu(String command, String name) {
+ this.command = command;
+ this.name = name;
+ }
+
+ @Override
+ public String getCommand() {
+ return this.command;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+ }
+
+ @Test
+ public void findAll() {
+ EnumMenu[] enumMenus = EnumMenu.values();
+ assertThat(Menu.findAll(EnumMenu.class)).containsExactly(enumMenus);
+ }
+
+ @Test
+ public void findAll__NotEnumTypeClassException() {
+ String message = "Enum νμμ ν΄λμ€κ° μλλλ€.";
+ assertThatThrownBy(() -> Menu.findAll(ClassMenu.class)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(message);
+ }
+
+ @Test
+ public void findByCommand() {
+ EnumMenu item = EnumMenu.ITEM;
+ assertThat(Menu.findByCommand(EnumMenu.class, "none")).isNotPresent();
+ assertThat(Menu.findByCommand(EnumMenu.class, item.getCommand())).isPresent();
+ }
+}
+```
+
+```java
+// Menu.java
+
+package subway.screen.view;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public interface Menu {
+ String getCommand();
+
+ String getName();
+
+ static List