From 04a0c88abf7a875f80213d63aa5c084c7f651ea6 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Mon, 24 Mar 2025 16:38:47 +0900 Subject: [PATCH 01/26] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bd90ef0247..d22553fb7a 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -# java-calculator-precourse \ No newline at end of file +# java-calculator-precourse + +## 미션 목표 +- [ ] OOP(Object Oriented Programming) +- [ ] TDD 실습 + +## 미션 요구사항 +입력한 문자열에서 숫자를 추출해 더하는 계산기를 만든다. +- 기본 구분자인 쉼표`,`나 콜론`:`을 구분자로 가지는 문자열을 받으면 구분자를 기준으로 숫자를 분리한 후 숫자를 더한 값을 반환한다. +- 기본 구분자 외에 커스텀 구분자를 `//`와 `\n`에 정의하여 사용할 수 있다. +- 잘못된 값을 입력할 경우 `IlLegaLArgumentException`을 발생시킨 후 종료된다. + +## 구현 기능 목록 + +### 입력 +- [ ] 구분자와 양수로 구성된 문자열을 입력받는다. + +### 구분자 확인 +- [ ] 기본 구분자인지 커스텀 구분자인지 확인한다. + +### 문자열 분리 +- [ ] 구분자를 기준으로 숫자를 분리한다. + +### 문자열 계산 +- [ ] 추출된 문자열을 숫자로 변환한 후 더한다. + +### 예외 상황 +- [ ] 기본 구분자도 커스텀 구분자도 아닌 경우 + - [ ] `,`와 `:` 외의 구분자를 사용한 경우 + - [ ] `//`와 `\n`의 형식을 지키지 않은 경우 +- 음수를 입력한 경우 +- 사용할 수 없는 구분자를 사용한 경우 ex) 숫자, 커스텀 구분자 형식 (`//\n`), 공백 \ No newline at end of file From 39e1c1c374acfec754bbe593f6e4215aa4c5db26 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Tue, 25 Mar 2025 14:48:15 +0900 Subject: [PATCH 02/26] =?UTF-8?q?test:=20=EB=8D=A7=EC=85=88=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/calculator/CalculatorTest.java | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/java/calculator/CalculatorTest.java diff --git a/src/test/java/calculator/CalculatorTest.java b/src/test/java/calculator/CalculatorTest.java new file mode 100644 index 0000000000..5dbbf3595a --- /dev/null +++ b/src/test/java/calculator/CalculatorTest.java @@ -0,0 +1,29 @@ +package calculator; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CalculatorTest { + + @Test + @DisplayName("리스트의 값을 더한다.") + void shouldReturnAdd_whenValues() { + Calculator calculator = new Calculator(); + int result = calculator.addValues(List.of(1, 2, 3, 4, 5)); + + assertEquals(15, result); + } + + @Test + @DisplayName("리스트에 값이 없을 때 0을 반환한다.") + void shouldReturnZero_whenEmptyValue() { + Calculator calculator = new Calculator(); + int result = calculator.addValues(List.of()); + + assertEquals(0, result); + } +} From 629533e2dc6025c3827b5e8be742710380c1a9fe Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Tue, 25 Mar 2025 14:49:01 +0900 Subject: [PATCH 03/26] =?UTF-8?q?feat:=20=EB=8D=A7=EC=85=88=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/calculator/Calculator.java diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java new file mode 100644 index 0000000000..2aaeba605b --- /dev/null +++ b/src/main/java/calculator/Calculator.java @@ -0,0 +1,15 @@ +package calculator; + +import java.util.List; + +public class Calculator { + + public int addValues(List values) { + int result = 0; + for (int value : values) { + result += value; + } + + return result; + } +} From aa5a17c01de45b01d3c5e0e50ee68ed975d16f5d Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Tue, 25 Mar 2025 15:05:57 +0900 Subject: [PATCH 04/26] =?UTF-8?q?test:=20=EB=AC=B8=EC=9E=90=EC=97=B4=20->?= =?UTF-8?q?=20=EC=88=AB=EC=9E=90=20=EB=B3=80=ED=99=98=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/calculator/ParserTest.java | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/java/calculator/ParserTest.java diff --git a/src/test/java/calculator/ParserTest.java b/src/test/java/calculator/ParserTest.java new file mode 100644 index 0000000000..5076adf710 --- /dev/null +++ b/src/test/java/calculator/ParserTest.java @@ -0,0 +1,26 @@ +package calculator; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ParserTest { + + @Test + @DisplayName("문자열을 숫자로 변환한다.") + void shouldParseNumber_whenString() { + int result = Parser.parse("1"); + + assertEquals(1, result); + } + + @Test + @DisplayName("문자열을 숫자로 변환할 수 없는 경우 예외가 발생한다.") + void shouldThrowsException_whenNotNumber() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> Parser.parse("a")); + + assertEquals("숫자로 변환할 수 없는 값이 입력되었습니다.", exception.getMessage()); + } +} From 0c77f38dbe764a1371533491c2f434a622bf4031 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Tue, 25 Mar 2025 15:06:32 +0900 Subject: [PATCH 05/26] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9E=90=EC=97=B4=20->?= =?UTF-8?q?=20=EC=88=AB=EC=9E=90=20=EB=B3=80=ED=99=98=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Parser.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/calculator/Parser.java diff --git a/src/main/java/calculator/Parser.java b/src/main/java/calculator/Parser.java new file mode 100644 index 0000000000..f16db7d365 --- /dev/null +++ b/src/main/java/calculator/Parser.java @@ -0,0 +1,12 @@ +package calculator; + +public class Parser { + + public static int parse(final String s) { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("숫자로 변환할 수 없는 값이 입력되었습니다."); + } + } +} From 4b9d17d549410c0d26e44e1f5869256f43bd854d Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Tue, 25 Mar 2025 17:35:45 +0900 Subject: [PATCH 06/26] =?UTF-8?q?test:=20=EA=B5=AC=EB=B6=84=EC=9E=90=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculator/DelimiterExtractorTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/java/calculator/DelimiterExtractorTest.java diff --git a/src/test/java/calculator/DelimiterExtractorTest.java b/src/test/java/calculator/DelimiterExtractorTest.java new file mode 100644 index 0000000000..dea2d22ecd --- /dev/null +++ b/src/test/java/calculator/DelimiterExtractorTest.java @@ -0,0 +1,29 @@ +package calculator; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class DelimiterExtractorTest { + + @ParameterizedTest + @DisplayName("문자열에서 기본 구분자를 추출한다.") + @CsvSource(value = { + "1,2,3 | ,", + "1:2:3 | :", + "1,2:3 | ,:" + }, delimiter = '|') + void shouldExtractDelimiter_whenGivenString(String input, String expected) { + //given + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + List result = extractor.extract(input); + + //then + assertEquals(List.of(expected.split("")), result); + } +} From 34a8b9ff07d66ece10b6f1d5f3af0d2c42899322 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Tue, 25 Mar 2025 17:36:14 +0900 Subject: [PATCH 07/26] =?UTF-8?q?feat:=20=EA=B8=B0=EB=B3=B8=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=EC=9E=90=20=EC=B6=94=EC=B6=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Delimiter.java | 16 ++++++++++++++++ src/main/java/calculator/DelimiterExtractor.java | 13 +++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/calculator/Delimiter.java create mode 100644 src/main/java/calculator/DelimiterExtractor.java diff --git a/src/main/java/calculator/Delimiter.java b/src/main/java/calculator/Delimiter.java new file mode 100644 index 0000000000..bd7dd926a4 --- /dev/null +++ b/src/main/java/calculator/Delimiter.java @@ -0,0 +1,16 @@ +package calculator; + +public enum Delimiter { + COMMA(","), + COLON(":"); + + private final String delimiter; + + Delimiter(final String delimiter) { + this.delimiter = delimiter; + } + + public String getDelimiter() { + return delimiter; + } +} diff --git a/src/main/java/calculator/DelimiterExtractor.java b/src/main/java/calculator/DelimiterExtractor.java new file mode 100644 index 0000000000..46f90f6697 --- /dev/null +++ b/src/main/java/calculator/DelimiterExtractor.java @@ -0,0 +1,13 @@ +package calculator; + +import java.util.List; +import java.util.stream.Stream; + +public class DelimiterExtractor { + public List extract(String input) { + return Stream.of(Delimiter.values()) + .map(Delimiter::getDelimiter) + .filter(input::contains) + .toList(); + } +} From 2c9b9fd292c65b98fdb9c3d34e3c6dc8d03519b5 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Wed, 26 Mar 2025 14:33:16 +0900 Subject: [PATCH 08/26] =?UTF-8?q?test:=20=EB=B3=80=ED=99=98=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/calculator/ParserTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/calculator/ParserTest.java b/src/test/java/calculator/ParserTest.java index 5076adf710..f4f533dbe6 100644 --- a/src/test/java/calculator/ParserTest.java +++ b/src/test/java/calculator/ParserTest.java @@ -1,6 +1,7 @@ package calculator; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 59f68c4025836fb5705cdea221a118390089ce52 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Thu, 27 Mar 2025 16:05:10 +0900 Subject: [PATCH 09/26] =?UTF-8?q?test:=20=EA=B5=AC=EB=B6=84=EC=9E=90=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 명시된 구분자를 사용하였는지 판별한다. --- src/test/java/calculator/DelimiterTest.java | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/java/calculator/DelimiterTest.java diff --git a/src/test/java/calculator/DelimiterTest.java b/src/test/java/calculator/DelimiterTest.java new file mode 100644 index 0000000000..bd5eb3f274 --- /dev/null +++ b/src/test/java/calculator/DelimiterTest.java @@ -0,0 +1,28 @@ +package calculator; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class DelimiterTest { + + @ParameterizedTest + @DisplayName("명시된 구분자와 일치하는 구분자인지 정상적으로 판별한다.") + @ValueSource(strings = {",", ":"}) + void shouldReturnTrue_whenValidDelimiter(String delimiter) { + assertTrue(Delimiter.isValidDelimiter(delimiter)); + assertTrue(Delimiter.isValidDelimiter(delimiter)); + } + + @ParameterizedTest + @DisplayName("명시된 구분자와 일치하는 구분자가 아니라면 false를 반환한다.") + @ValueSource(strings = {" ", ";", "a"}) + void shouldReturnFalse_whenInvalidDelimiter(String delimiter) { + assertFalse(Delimiter.isValidDelimiter(delimiter)); + assertFalse(Delimiter.isValidDelimiter(delimiter)); + assertFalse(Delimiter.isValidDelimiter(delimiter)); + } +} From cbe76f4b07fa8a00ce3f8257d454baf39c17164c Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Thu, 27 Mar 2025 16:06:01 +0900 Subject: [PATCH 10/26] =?UTF-8?q?feat:=20=EA=B5=AC=EB=B6=84=EC=9E=90=20?= =?UTF-8?q?=ED=8C=90=EB=B3=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 명시된 구분자를 사용하였는지 판별합니다. --- src/main/java/calculator/Delimiter.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/calculator/Delimiter.java b/src/main/java/calculator/Delimiter.java index bd7dd926a4..7a9b4e7383 100644 --- a/src/main/java/calculator/Delimiter.java +++ b/src/main/java/calculator/Delimiter.java @@ -1,5 +1,7 @@ package calculator; +import java.util.stream.Stream; + public enum Delimiter { COMMA(","), COLON(":"); @@ -10,6 +12,11 @@ public enum Delimiter { this.delimiter = delimiter; } + public static boolean isValidDelimiter(String delimiter) { + return Stream.of(values()) + .anyMatch(d -> d.getDelimiter().equals(delimiter)); + } + public String getDelimiter() { return delimiter; } From e3fc33ff81c86f61ab6c61204f3520ebe565ccb2 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Thu, 27 Mar 2025 16:07:28 +0900 Subject: [PATCH 11/26] =?UTF-8?q?test:=20=EA=B5=AC=EB=B6=84=EC=9E=90=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구분자를 추출하여 사용할 수 있는 구분자인지 검증합니다. --- .../calculator/DelimiterExtractorTest.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/test/java/calculator/DelimiterExtractorTest.java b/src/test/java/calculator/DelimiterExtractorTest.java index dea2d22ecd..c9bd7ce55b 100644 --- a/src/test/java/calculator/DelimiterExtractorTest.java +++ b/src/test/java/calculator/DelimiterExtractorTest.java @@ -1,11 +1,13 @@ package calculator; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; class DelimiterExtractorTest { @@ -26,4 +28,19 @@ void shouldExtractDelimiter_whenGivenString(String input, String expected) { //then assertEquals(List.of(expected.split("")), result); } + + @ParameterizedTest + @DisplayName("기본 구분자에 속하지 않는 구분자를 사용할 경우 예외가 발생한다.") + @ValueSource(strings = {"1,2;3", "1;2;3","1:2#3"}) + void shouldReturnEmptyList_whenNotDefaultDelimiter(String input) { + //given + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> extractor.extract(input)); + + //then + assertEquals("기본 구분자(,나 :)를 사용해주세요.", exception.getMessage()); + } } From 6bf86de81750c925ad83dc9ae70a11f9283ed635 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Thu, 27 Mar 2025 16:08:29 +0900 Subject: [PATCH 12/26] =?UTF-8?q?refactor:=20=EA=B5=AC=EB=B6=84=EC=9E=90?= =?UTF-8?q?=20=EC=B6=94=EC=B6=9C=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추출된 구분자가 사용할 수 있는 구분자인지 확인하는 기능을 추가하였습니다. - 구분자가 사용할 수 없는 구분자일 경우 예외를 발생시키도록 수정하였습니다. --- .../java/calculator/DelimiterExtractor.java | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/main/java/calculator/DelimiterExtractor.java b/src/main/java/calculator/DelimiterExtractor.java index 46f90f6697..01d6545232 100644 --- a/src/main/java/calculator/DelimiterExtractor.java +++ b/src/main/java/calculator/DelimiterExtractor.java @@ -1,13 +1,45 @@ package calculator; +import java.util.LinkedHashSet; import java.util.List; -import java.util.stream.Stream; +import java.util.Set; public class DelimiterExtractor { - public List extract(String input) { - return Stream.of(Delimiter.values()) - .map(Delimiter::getDelimiter) - .filter(input::contains) - .toList(); + + public List extract(final String input) { + Set delimiterSet = getDelimiterSet(input); + List delimiters = List.copyOf(delimiterSet); + + validateDefaultDelimiters(delimiters); + return delimiters; + } + + private Set getDelimiterSet(final String input) { + Set delimiterSet = new LinkedHashSet<>(); + + for (char c : input.toCharArray()) { + processCharacter(c, delimiterSet); + } + + return delimiterSet; + } + + private void validateDefaultDelimiters(final List delimiters) { + for (String delimiter : delimiters) { + validateDelimiter(delimiter); + } + } + + private void processCharacter(char c, Set delimiterSet) { + if (Character.isDigit(c)) { + return; + } + delimiterSet.add(String.valueOf(c)); + } + + private void validateDelimiter(final String delimiter) { + if (!Delimiter.isValidDelimiter(delimiter)) { + throw new IllegalArgumentException("기본 구분자(,나 :)를 사용해주세요."); + } } } From c4b4a6dcc484417012bac0d9b3e8e3b9bbd1b96f Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Fri, 28 Mar 2025 17:17:52 +0900 Subject: [PATCH 13/26] =?UTF-8?q?refactor:=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EA=B3=BC=EC=A0=95=20=EC=9D=8C=EC=88=98=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 문자열에서 변환할 때 음수일 경우 예외가 발생한다. --- src/main/java/calculator/Parser.java | 13 +++++++++++-- src/test/java/calculator/ParserTest.java | 22 ++++++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/calculator/Parser.java b/src/main/java/calculator/Parser.java index f16db7d365..39e10671bd 100644 --- a/src/main/java/calculator/Parser.java +++ b/src/main/java/calculator/Parser.java @@ -4,9 +4,18 @@ public class Parser { public static int parse(final String s) { try { - return Integer.parseInt(s); + int number = Integer.parseInt(s); + validatePositiveNumber(number); + + return number; } catch (NumberFormatException e) { - throw new IllegalArgumentException("숫자로 변환할 수 없는 값이 입력되었습니다."); + throw new IllegalArgumentException("숫자만 입력해주세요."); + } + } + + private static void validatePositiveNumber(final int number) { + if (number < 0) { + throw new IllegalArgumentException("양수를 입력해주세요."); } } } diff --git a/src/test/java/calculator/ParserTest.java b/src/test/java/calculator/ParserTest.java index f4f533dbe6..acb98a3042 100644 --- a/src/test/java/calculator/ParserTest.java +++ b/src/test/java/calculator/ParserTest.java @@ -5,23 +5,33 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class ParserTest { @Test @DisplayName("문자열을 숫자로 변환한다.") void shouldParseNumber_whenString() { - int result = Parser.parse("1"); + assertEquals(1, Parser.parse("1")); + } + + @ParameterizedTest + @DisplayName("문자열을 정수로 변환할 수 없는 경우 예외가 발생한다.") + @ValueSource(strings = {"a", " "}) + void shouldThrowException_whenInvalidNumber(String input) { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> Parser.parse(input)); - assertEquals(1, result); + assertEquals("숫자만 입력해주세요.", exception.getMessage()); } @Test - @DisplayName("문자열을 숫자로 변환할 수 없는 경우 예외가 발생한다.") - void shouldThrowsException_whenNotNumber() { + @DisplayName("음수가 입력된 경우 예외가 발생한다.") + void shouldThrowException_whenNegativeNumber() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> Parser.parse("a")); + () -> Parser.parse("-1")); - assertEquals("숫자로 변환할 수 없는 값이 입력되었습니다.", exception.getMessage()); + assertEquals("양수를 입력해주세요.", exception.getMessage()); } } From 2ebbda81f8a15c26b0f393e6bceb83dd36df47f7 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Fri, 28 Mar 2025 18:09:12 +0900 Subject: [PATCH 14/26] =?UTF-8?q?test:=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/calculator/StringSplitterTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/test/java/calculator/StringSplitterTest.java diff --git a/src/test/java/calculator/StringSplitterTest.java b/src/test/java/calculator/StringSplitterTest.java new file mode 100644 index 0000000000..68d9c718f6 --- /dev/null +++ b/src/test/java/calculator/StringSplitterTest.java @@ -0,0 +1,43 @@ +package calculator; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class StringSplitterTest { + + @ParameterizedTest + @DisplayName("입력받은 구분자로 문자열을 나눈다.") + @ValueSource(strings = {"1,2,3", "1:2:3", "1,2:3"}) + void shouldSplitString_whenGivingDelimiters(String input) { + //given + StringSplitter splitter = new StringSplitter(); + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + List delimiters = extractor.extract(input); + List result = splitter.split(input, delimiters); + + //then + assertEquals(List.of("1", "2", "3"), result); + } + + @ParameterizedTest + @DisplayName("입력받은 커스텀 구분자로 문자열을 나눈다.") + @ValueSource(strings = {"//;\\n1;2;3", "//*\\n1*2*3", "//***\\n1***2***3"}) + void shouldSplitString_whenGivingCustomDelimiters(String input) { + //given + StringSplitter splitter = new StringSplitter(); + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + List delimiters = extractor.extract(input); + List result = splitter.split(input, delimiters); + + //then + assertEquals(List.of("1", "2", "3"), result); + } +} From b8a4054907e553f858a2310d84ba6dc655b411bc Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Fri, 28 Mar 2025 18:11:30 +0900 Subject: [PATCH 15/26] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전달받은 구분자를 통해 입력받은 문자열을 구분하는 기능입니다. --- .../calculator/CustomDelimiterFormat.java | 6 ++++ src/main/java/calculator/StringSplitter.java | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/calculator/CustomDelimiterFormat.java create mode 100644 src/main/java/calculator/StringSplitter.java diff --git a/src/main/java/calculator/CustomDelimiterFormat.java b/src/main/java/calculator/CustomDelimiterFormat.java new file mode 100644 index 0000000000..42e583dc19 --- /dev/null +++ b/src/main/java/calculator/CustomDelimiterFormat.java @@ -0,0 +1,6 @@ +package calculator; + +public class CustomDelimiterFormat { + public static final String CUSTOM_DELIMITER_PREFIX = "//"; + public static final String CUSTOM_DELIMITER_SUFFIX = "\\n"; +} diff --git a/src/main/java/calculator/StringSplitter.java b/src/main/java/calculator/StringSplitter.java new file mode 100644 index 0000000000..454af601f4 --- /dev/null +++ b/src/main/java/calculator/StringSplitter.java @@ -0,0 +1,34 @@ +package calculator; + +import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; +import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; + +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class StringSplitter { + + public List split(String input, final List delimiters) { + input = preprocessingCustomDelimiter(input); + + String regex = getRegex(delimiters); + String[] splitResult = input.split(regex); + + return List.of(splitResult); + } + + private String preprocessingCustomDelimiter(String input) { + if (input.startsWith(CUSTOM_DELIMITER_PREFIX) && input.contains(CUSTOM_DELIMITER_SUFFIX)) { + input = input.substring(input.indexOf(CUSTOM_DELIMITER_SUFFIX) + 2); + } + + return input; + } + + private String getRegex(final List delimiters) { + return delimiters.stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + } +} From de3c373d5121b99b7d71c0ee48625f3837b62a34 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sat, 29 Mar 2025 18:17:23 +0900 Subject: [PATCH 16/26] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84=EC=9E=90=20=EC=B6=94=EA=B0=80=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용할 수 없는 커스텀 구분자를 검증하는 테스트를 추가하였습니다. --- src/test/java/calculator/DelimiterTest.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/test/java/calculator/DelimiterTest.java b/src/test/java/calculator/DelimiterTest.java index bd5eb3f274..26b4a3d4b2 100644 --- a/src/test/java/calculator/DelimiterTest.java +++ b/src/test/java/calculator/DelimiterTest.java @@ -14,15 +14,26 @@ class DelimiterTest { @ValueSource(strings = {",", ":"}) void shouldReturnTrue_whenValidDelimiter(String delimiter) { assertTrue(Delimiter.isValidDelimiter(delimiter)); - assertTrue(Delimiter.isValidDelimiter(delimiter)); } @ParameterizedTest @DisplayName("명시된 구분자와 일치하는 구분자가 아니라면 false를 반환한다.") - @ValueSource(strings = {" ", ";", "a"}) + @ValueSource(strings = {";", "a", "@"}) void shouldReturnFalse_whenInvalidDelimiter(String delimiter) { assertFalse(Delimiter.isValidDelimiter(delimiter)); - assertFalse(Delimiter.isValidDelimiter(delimiter)); - assertFalse(Delimiter.isValidDelimiter(delimiter)); + } + + @ParameterizedTest + @DisplayName("커스텀 구분자로 사용할 수 있는 구분자일 경우 true를 반환한다.") + @ValueSource(strings = {";", "#", "@", "a", "*"}) + void shouldReturnTrue_whenValidCustomDelimiter(String delimiter) { + assertTrue(Delimiter.isValidCustomDelimiter(delimiter)); + } + + @ParameterizedTest + @DisplayName("커스텀 구분자로 사용할 수 없는 구분자일 경우 false를 반환한다.") + @ValueSource(strings = {",", ":", " ", "1", "2"}) + void shouldReturnFalse_whenInvalidCustomDelimiter(String delimiter) { + assertFalse(Delimiter.isValidCustomDelimiter(delimiter)); } } From e6b1ba7aa52cf98f6c216da223e2a517b4a6abe7 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sat, 29 Mar 2025 18:18:31 +0900 Subject: [PATCH 17/26] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84=EC=9E=90=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 커스텀 구분자로 사용할 수 없는 문자열을 명시하여 검증합니다. --- src/main/java/calculator/Delimiter.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/calculator/Delimiter.java b/src/main/java/calculator/Delimiter.java index 7a9b4e7383..f1d2639fae 100644 --- a/src/main/java/calculator/Delimiter.java +++ b/src/main/java/calculator/Delimiter.java @@ -3,20 +3,33 @@ import java.util.stream.Stream; public enum Delimiter { - COMMA(","), - COLON(":"); + COMMA(",", true), + COLON(":", true), + DOUBLE_SLASH("//", false), + NEW_LINE("\\n", false), + NUMBER("[0-9]+", false), + SPACE(" ", false) + ; private final String delimiter; + private final boolean isDefault; - Delimiter(final String delimiter) { + Delimiter(final String delimiter, final boolean isDefault) { this.delimiter = delimiter; + this.isDefault = isDefault; } public static boolean isValidDelimiter(String delimiter) { return Stream.of(values()) + .filter(d -> d.isDefault) .anyMatch(d -> d.getDelimiter().equals(delimiter)); } + public static boolean isValidCustomDelimiter(String delimiter) { + return Stream.of(values()) + .noneMatch(d -> d.getDelimiter().equals(delimiter)) && !delimiter.matches(NUMBER.getDelimiter()); + } + public String getDelimiter() { return delimiter; } From f23ecb8bcac38adc74da65ed13d2f186080ed15b Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sat, 29 Mar 2025 18:19:59 +0900 Subject: [PATCH 18/26] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84=EC=9E=90=20=EC=B6=94=EC=B6=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 커스텀 구분자를 정상적으로 추출합니다. --- .../java/calculator/DelimiterExtractor.java | 51 ++++++++++++++++--- .../calculator/DelimiterExtractorTest.java | 31 ++++++++++- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/main/java/calculator/DelimiterExtractor.java b/src/main/java/calculator/DelimiterExtractor.java index 01d6545232..7887b2e50c 100644 --- a/src/main/java/calculator/DelimiterExtractor.java +++ b/src/main/java/calculator/DelimiterExtractor.java @@ -1,5 +1,8 @@ package calculator; +import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; +import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; + import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -7,10 +10,30 @@ public class DelimiterExtractor { public List extract(final String input) { + if (isCustomDelimiterFormat(input)) { + return extractCustomDelimiters(input); + } + + return extractDefaultDelimiters(input); + } + + private boolean isCustomDelimiterFormat(String input) { + return input.startsWith(CUSTOM_DELIMITER_PREFIX) && input.contains(CUSTOM_DELIMITER_SUFFIX); + } + + private List extractCustomDelimiters(String input) { + List delimiters = List.of( + input.substring(CUSTOM_DELIMITER_PREFIX.length(), input.indexOf(CUSTOM_DELIMITER_SUFFIX))); + validateAllCustomDelimiter(delimiters); + + return delimiters; + } + + private List extractDefaultDelimiters(String input) { Set delimiterSet = getDelimiterSet(input); List delimiters = List.copyOf(delimiterSet); + validateAllDefaultDelimiter(delimiters); - validateDefaultDelimiters(delimiters); return delimiters; } @@ -24,12 +47,6 @@ private Set getDelimiterSet(final String input) { return delimiterSet; } - private void validateDefaultDelimiters(final List delimiters) { - for (String delimiter : delimiters) { - validateDelimiter(delimiter); - } - } - private void processCharacter(char c, Set delimiterSet) { if (Character.isDigit(c)) { return; @@ -37,7 +54,25 @@ private void processCharacter(char c, Set delimiterSet) { delimiterSet.add(String.valueOf(c)); } - private void validateDelimiter(final String delimiter) { + private void validateAllCustomDelimiter(List delimiters) { + for (String delimiter : delimiters) { + validateCustomDelimiter(delimiter); + } + } + + private static void validateCustomDelimiter(final String delimiter) { + if (!Delimiter.isValidCustomDelimiter(delimiter)) { + throw new IllegalArgumentException("사용 가능한 커스텀 구분자를 입력해주세요."); + } + } + + private void validateAllDefaultDelimiter(final List delimiters) { + for (String delimiter : delimiters) { + validateDefaultDelimiter(delimiter); + } + } + + private void validateDefaultDelimiter(final String delimiter) { if (!Delimiter.isValidDelimiter(delimiter)) { throw new IllegalArgumentException("기본 구분자(,나 :)를 사용해주세요."); } diff --git a/src/test/java/calculator/DelimiterExtractorTest.java b/src/test/java/calculator/DelimiterExtractorTest.java index c9bd7ce55b..3a124fbd37 100644 --- a/src/test/java/calculator/DelimiterExtractorTest.java +++ b/src/test/java/calculator/DelimiterExtractorTest.java @@ -5,6 +5,7 @@ import java.util.List; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; @@ -31,7 +32,7 @@ void shouldExtractDelimiter_whenGivenString(String input, String expected) { @ParameterizedTest @DisplayName("기본 구분자에 속하지 않는 구분자를 사용할 경우 예외가 발생한다.") - @ValueSource(strings = {"1,2;3", "1;2;3","1:2#3"}) + @ValueSource(strings = {"1,2;3", "1;2;3", "1:2#3"}) void shouldReturnEmptyList_whenNotDefaultDelimiter(String input) { //given DelimiterExtractor extractor = new DelimiterExtractor(); @@ -43,4 +44,32 @@ void shouldReturnEmptyList_whenNotDefaultDelimiter(String input) { //then assertEquals("기본 구분자(,나 :)를 사용해주세요.", exception.getMessage()); } + + @Test + @DisplayName("문자열에서 커스텀 구분자를 추출한다.") + void shouldExtractCustomDelimiter_whenGivingString() { + //given + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + List result = extractor.extract("//;\\n1;2;3"); + + //then + assertEquals(List.of(";"), result); + } + + @ParameterizedTest + @DisplayName("커스텀 구분자로 사용할 수 없는 구분자를 입력했을 경우 예외가 발생한다.") + @ValueSource(strings = {"//1\\n112131", "//:\\n1:2:3", "////\\n1//2//3"}) + void shouldThrowException_whenInvalidCustomDelimiter(String input) { + //given + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> extractor.extract(input)); + + //then + assertEquals("사용 가능한 커스텀 구분자를 입력해주세요.", exception.getMessage()); + } } From 5514ecafb147aac28e52ef58110e3f068ccfd2c5 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sat, 29 Mar 2025 18:22:26 +0900 Subject: [PATCH 19/26] =?UTF-8?q?refactor:=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=20=ED=8C=8C=EC=8B=B1=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 하나의 문자열이 아닌 여러 개의 문자열을 파싱할 수 있도록 수정하였습니다. --- src/main/java/calculator/Parser.java | 19 +++++++++++++++---- src/test/java/calculator/ParserTest.java | 9 +++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/java/calculator/Parser.java b/src/main/java/calculator/Parser.java index 39e10671bd..d85df3a4f7 100644 --- a/src/main/java/calculator/Parser.java +++ b/src/main/java/calculator/Parser.java @@ -1,18 +1,29 @@ package calculator; +import java.util.List; + public class Parser { - public static int parse(final String s) { + public static List parse(final List input) { try { - int number = Integer.parseInt(s); - validatePositiveNumber(number); + List numbers = getNumbers(input); + for (int number : numbers) { + validatePositiveNumber(number); + } - return number; + return numbers; } catch (NumberFormatException e) { throw new IllegalArgumentException("숫자만 입력해주세요."); } } + private static List getNumbers(final List input) { + return input.stream() + .mapToInt(Integer::parseInt) + .boxed() + .toList(); + } + private static void validatePositiveNumber(final int number) { if (number < 0) { throw new IllegalArgumentException("양수를 입력해주세요."); diff --git a/src/test/java/calculator/ParserTest.java b/src/test/java/calculator/ParserTest.java index acb98a3042..d5d0a02e5b 100644 --- a/src/test/java/calculator/ParserTest.java +++ b/src/test/java/calculator/ParserTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -13,15 +14,15 @@ class ParserTest { @Test @DisplayName("문자열을 숫자로 변환한다.") void shouldParseNumber_whenString() { - assertEquals(1, Parser.parse("1")); + assertEquals(List.of(1), Parser.parse(List.of("1"))); } @ParameterizedTest @DisplayName("문자열을 정수로 변환할 수 없는 경우 예외가 발생한다.") - @ValueSource(strings = {"a", " "}) + @ValueSource(strings = {"a", " ", "1a"}) void shouldThrowException_whenInvalidNumber(String input) { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> Parser.parse(input)); + () -> Parser.parse(List.of(input))); assertEquals("숫자만 입력해주세요.", exception.getMessage()); } @@ -30,7 +31,7 @@ void shouldThrowException_whenInvalidNumber(String input) { @DisplayName("음수가 입력된 경우 예외가 발생한다.") void shouldThrowException_whenNegativeNumber() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> Parser.parse("-1")); + () -> Parser.parse(List.of("-1"))); assertEquals("양수를 입력해주세요.", exception.getMessage()); } From 69c6344bfc1216616791ac36b69b8294aa1d77f6 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sat, 29 Mar 2025 18:23:51 +0900 Subject: [PATCH 20/26] =?UTF-8?q?fix:=20=EB=8D=A7=EC=85=88=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 2 +- src/test/java/calculator/CalculatorTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 2aaeba605b..efb3f12b55 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -4,7 +4,7 @@ public class Calculator { - public int addValues(List values) { + public int add(List values) { int result = 0; for (int value : values) { result += value; diff --git a/src/test/java/calculator/CalculatorTest.java b/src/test/java/calculator/CalculatorTest.java index 5dbbf3595a..53b972573b 100644 --- a/src/test/java/calculator/CalculatorTest.java +++ b/src/test/java/calculator/CalculatorTest.java @@ -13,7 +13,7 @@ class CalculatorTest { @DisplayName("리스트의 값을 더한다.") void shouldReturnAdd_whenValues() { Calculator calculator = new Calculator(); - int result = calculator.addValues(List.of(1, 2, 3, 4, 5)); + int result = calculator.add(List.of(1, 2, 3, 4, 5)); assertEquals(15, result); } @@ -22,7 +22,7 @@ void shouldReturnAdd_whenValues() { @DisplayName("리스트에 값이 없을 때 0을 반환한다.") void shouldReturnZero_whenEmptyValue() { Calculator calculator = new Calculator(); - int result = calculator.addValues(List.of()); + int result = calculator.add(List.of()); assertEquals(0, result); } From 9e503afa3341b75d1340beb19759bb924334983f Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sun, 30 Mar 2025 14:03:38 +0900 Subject: [PATCH 21/26] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - model 패키지를 추가하여 관련된 클래스들을 이동하였습니다. --- src/main/java/calculator/Application.java | 24 +++++++- .../calculator/{ => model}/Calculator.java | 2 +- .../{ => model}/CustomDelimiterFormat.java | 2 +- .../calculator/{ => model}/Delimiter.java | 2 +- .../{ => model}/DelimiterExtractor.java | 6 +- .../java/calculator/{ => model}/Parser.java | 2 +- .../{ => model}/StringSplitter.java | 6 +- src/test/java/calculator/ApplicationTest.java | 60 +++++++++++++++---- src/test/java/calculator/CalculatorTest.java | 1 + .../calculator/DelimiterExtractorTest.java | 1 + src/test/java/calculator/DelimiterTest.java | 1 + src/test/java/calculator/ParserTest.java | 1 + .../java/calculator/StringSplitterTest.java | 2 + 13 files changed, 88 insertions(+), 22 deletions(-) rename src/main/java/calculator/{ => model}/Calculator.java (89%) rename src/main/java/calculator/{ => model}/CustomDelimiterFormat.java (86%) rename src/main/java/calculator/{ => model}/Delimiter.java (97%) rename src/main/java/calculator/{ => model}/DelimiterExtractor.java (92%) rename src/main/java/calculator/{ => model}/Parser.java (97%) rename src/main/java/calculator/{ => model}/StringSplitter.java (82%) diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580fb40..c162b35d12 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,7 +1,29 @@ package calculator; +import calculator.model.Calculator; +import calculator.model.DelimiterExtractor; +import calculator.model.Parser; +import calculator.model.StringSplitter; +import calculator.view.InputView; +import calculator.view.OutputView; +import java.util.List; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + + String input = InputView.getInput(); + + DelimiterExtractor delimiterExtractor = new DelimiterExtractor(); + List delimiters = delimiterExtractor.extract(input); + + StringSplitter stringSplitter = new StringSplitter(); + List splitResult = stringSplitter.split(input, delimiters); + + List parseResult = Parser.parse(splitResult); + + Calculator calculator = new Calculator(); + int result = calculator.add(parseResult); + + OutputView.printResult(result); } } diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/model/Calculator.java similarity index 89% rename from src/main/java/calculator/Calculator.java rename to src/main/java/calculator/model/Calculator.java index efb3f12b55..7491be4abd 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/model/Calculator.java @@ -1,4 +1,4 @@ -package calculator; +package calculator.model; import java.util.List; diff --git a/src/main/java/calculator/CustomDelimiterFormat.java b/src/main/java/calculator/model/CustomDelimiterFormat.java similarity index 86% rename from src/main/java/calculator/CustomDelimiterFormat.java rename to src/main/java/calculator/model/CustomDelimiterFormat.java index 42e583dc19..5e72a84427 100644 --- a/src/main/java/calculator/CustomDelimiterFormat.java +++ b/src/main/java/calculator/model/CustomDelimiterFormat.java @@ -1,4 +1,4 @@ -package calculator; +package calculator.model; public class CustomDelimiterFormat { public static final String CUSTOM_DELIMITER_PREFIX = "//"; diff --git a/src/main/java/calculator/Delimiter.java b/src/main/java/calculator/model/Delimiter.java similarity index 97% rename from src/main/java/calculator/Delimiter.java rename to src/main/java/calculator/model/Delimiter.java index f1d2639fae..ca25e7f209 100644 --- a/src/main/java/calculator/Delimiter.java +++ b/src/main/java/calculator/model/Delimiter.java @@ -1,4 +1,4 @@ -package calculator; +package calculator.model; import java.util.stream.Stream; diff --git a/src/main/java/calculator/DelimiterExtractor.java b/src/main/java/calculator/model/DelimiterExtractor.java similarity index 92% rename from src/main/java/calculator/DelimiterExtractor.java rename to src/main/java/calculator/model/DelimiterExtractor.java index 7887b2e50c..73dfeef432 100644 --- a/src/main/java/calculator/DelimiterExtractor.java +++ b/src/main/java/calculator/model/DelimiterExtractor.java @@ -1,7 +1,7 @@ -package calculator; +package calculator.model; -import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; -import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; +import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; +import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; import java.util.LinkedHashSet; import java.util.List; diff --git a/src/main/java/calculator/Parser.java b/src/main/java/calculator/model/Parser.java similarity index 97% rename from src/main/java/calculator/Parser.java rename to src/main/java/calculator/model/Parser.java index d85df3a4f7..48032425ee 100644 --- a/src/main/java/calculator/Parser.java +++ b/src/main/java/calculator/model/Parser.java @@ -1,4 +1,4 @@ -package calculator; +package calculator.model; import java.util.List; diff --git a/src/main/java/calculator/StringSplitter.java b/src/main/java/calculator/model/StringSplitter.java similarity index 82% rename from src/main/java/calculator/StringSplitter.java rename to src/main/java/calculator/model/StringSplitter.java index 454af601f4..2d28d27a37 100644 --- a/src/main/java/calculator/StringSplitter.java +++ b/src/main/java/calculator/model/StringSplitter.java @@ -1,7 +1,7 @@ -package calculator; +package calculator.model; -import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; -import static calculator.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; +import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; +import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; import java.util.List; import java.util.regex.Pattern; diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 93771fb011..302bbfe27d 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -1,26 +1,64 @@ package calculator; -import camp.nextstep.edu.missionutils.test.NsTest; -import org.junit.jupiter.api.Test; - import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import camp.nextstep.edu.missionutils.test.NsTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + class ApplicationTest extends NsTest { - @Test - void 커스텀_구분자_사용() { + + @ParameterizedTest + @DisplayName("값이 입력되지 않았을 때 0이 반환된다.") + @NullAndEmptySource + void shouldReturnZero_whenNotingInput(String input) { + assertSimpleTest(() -> { + run(input); + assertThat(output()).contains("결과 : 0"); + }); + } + + @ParameterizedTest + @DisplayName("기본 구분자를 사용하였을 때 정상적으로 결과가 반환된다.") + @ValueSource(strings = {"1,2:3", "1,2,3", "1:2:3"}) + void shouldReturn_whenUsingDefaultDelimiter(String input) { assertSimpleTest(() -> { - run("//;\\n1"); - assertThat(output()).contains("결과 : 1"); + run(input); + assertThat(output()).contains("결과 : 6"); }); } - @Test - void 예외_테스트() { + @ParameterizedTest + @DisplayName("커스텀 구분자를 사용하였을 때 정상적으로 결과가 반환된다.") + @ValueSource(strings = {"//;\\n1;2;3", "//#\\n1#2#3"}) + void shouldReturn_whenUsingCustomDelimiter(String input) { + assertSimpleTest(() -> { + run(input); + assertThat(output()).contains("결과 : 6"); + }); + } + + @ParameterizedTest + @DisplayName("입력값이 올바르지 않을 때 예외가 발생한다.") + @ValueSource(strings = {"1:a:3", "-1,2:3", "-1,2,3"}) + void exception_test(String input) { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException(input)) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @ParameterizedTest + @DisplayName("커스텀 구분자 형식을 올바르게 입력하지 않았을 경우 예외가 발생한다.") + @ValueSource(strings = {"//;1;2;3", "1#2#3", "#\\n1#2#3"}) + void shouldTest(String input) { assertSimpleTest(() -> - assertThatThrownBy(() -> runException("-1,2,3")) - .isInstanceOf(IllegalArgumentException.class) + assertThatThrownBy(() -> runException(input)) + .isInstanceOf(IllegalArgumentException.class) ); } diff --git a/src/test/java/calculator/CalculatorTest.java b/src/test/java/calculator/CalculatorTest.java index 53b972573b..767c6ac2af 100644 --- a/src/test/java/calculator/CalculatorTest.java +++ b/src/test/java/calculator/CalculatorTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import calculator.model.Calculator; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/calculator/DelimiterExtractorTest.java b/src/test/java/calculator/DelimiterExtractorTest.java index 3a124fbd37..43561170d6 100644 --- a/src/test/java/calculator/DelimiterExtractorTest.java +++ b/src/test/java/calculator/DelimiterExtractorTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import calculator.model.DelimiterExtractor; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/calculator/DelimiterTest.java b/src/test/java/calculator/DelimiterTest.java index 26b4a3d4b2..1613046171 100644 --- a/src/test/java/calculator/DelimiterTest.java +++ b/src/test/java/calculator/DelimiterTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import calculator.model.Delimiter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; diff --git a/src/test/java/calculator/ParserTest.java b/src/test/java/calculator/ParserTest.java index d5d0a02e5b..2e0ca88484 100644 --- a/src/test/java/calculator/ParserTest.java +++ b/src/test/java/calculator/ParserTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import calculator.model.Parser; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/calculator/StringSplitterTest.java b/src/test/java/calculator/StringSplitterTest.java index 68d9c718f6..3daed3d9da 100644 --- a/src/test/java/calculator/StringSplitterTest.java +++ b/src/test/java/calculator/StringSplitterTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.*; +import calculator.model.DelimiterExtractor; +import calculator.model.StringSplitter; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; From 05971beabdeca7aada2df234c1251599c5621ee7 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Sun, 30 Mar 2025 14:04:50 +0900 Subject: [PATCH 22/26] =?UTF-8?q?feat:=20View=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/view/InputView.java | 22 +++++++++++++++++++ src/main/java/calculator/view/OutputView.java | 11 ++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/main/java/calculator/view/InputView.java create mode 100644 src/main/java/calculator/view/OutputView.java diff --git a/src/main/java/calculator/view/InputView.java b/src/main/java/calculator/view/InputView.java new file mode 100644 index 0000000000..d9d4d96b6c --- /dev/null +++ b/src/main/java/calculator/view/InputView.java @@ -0,0 +1,22 @@ +package calculator.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + + public static final String START_MESSAGE = "덧셈할 문자열을 입력해주세요."; + public static final String USAGE_GUIDE = """ + [사용 안내] + 기본 구분자 => ,(쉼표)와 :(콜론)만 사용 가능합니다. + 커스텀 구분자 => //{구분자}\\n 형식을 사용해 선언할 수 있습니다. + """; + + private InputView() { + } + + public static String getInput() { + System.out.println(START_MESSAGE); + System.out.println(USAGE_GUIDE); + return Console.readLine(); + } +} diff --git a/src/main/java/calculator/view/OutputView.java b/src/main/java/calculator/view/OutputView.java new file mode 100644 index 0000000000..8419a140fd --- /dev/null +++ b/src/main/java/calculator/view/OutputView.java @@ -0,0 +1,11 @@ +package calculator.view; + +public class OutputView { + + private OutputView() { + } + + public static void printResult(final int result) { + System.out.println("결과 : " + result); + } +} From 4852356eeb4c579cccd92747a4a58977c5a02d7f Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Mon, 31 Mar 2025 14:44:21 +0900 Subject: [PATCH 23/26] =?UTF-8?q?refactor:=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=ED=9D=90=EB=A6=84=20=EA=B4=80=EB=A6=AC=20controller=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존에는 main에서 실행과 흐름 제어를 담당하고 있었는데, controller가 흐름 제어를 담당하도록 변경하였습니다. --- src/main/java/calculator/Application.java | 25 ++++--------- .../controller/CalculatorController.java | 35 +++++++++++++++++++ 2 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 src/main/java/calculator/controller/CalculatorController.java diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index c162b35d12..a8d8d841c9 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,29 +1,18 @@ package calculator; +import calculator.controller.CalculatorController; import calculator.model.Calculator; import calculator.model.DelimiterExtractor; -import calculator.model.Parser; import calculator.model.StringSplitter; -import calculator.view.InputView; -import calculator.view.OutputView; -import java.util.List; public class Application { public static void main(String[] args) { - String input = InputView.getInput(); - - DelimiterExtractor delimiterExtractor = new DelimiterExtractor(); - List delimiters = delimiterExtractor.extract(input); - - StringSplitter stringSplitter = new StringSplitter(); - List splitResult = stringSplitter.split(input, delimiters); - - List parseResult = Parser.parse(splitResult); - - Calculator calculator = new Calculator(); - int result = calculator.add(parseResult); - - OutputView.printResult(result); + CalculatorController calculatorController = new CalculatorController( + new DelimiterExtractor(), + new StringSplitter(), + new Calculator() + ); + calculatorController.run(); } } diff --git a/src/main/java/calculator/controller/CalculatorController.java b/src/main/java/calculator/controller/CalculatorController.java new file mode 100644 index 0000000000..36e0e3c0f5 --- /dev/null +++ b/src/main/java/calculator/controller/CalculatorController.java @@ -0,0 +1,35 @@ +package calculator.controller; + +import calculator.model.Calculator; +import calculator.model.DelimiterExtractor; +import calculator.model.Parser; +import calculator.model.StringSplitter; +import calculator.view.InputView; +import calculator.view.OutputView; +import java.util.List; + +public class CalculatorController { + + private final DelimiterExtractor delimiterExtractor; + private final StringSplitter stringSplitter; + private final Calculator calculator; + + public CalculatorController(final DelimiterExtractor delimiterExtractor, + final StringSplitter stringSplitter, + final Calculator calculator) { + this.delimiterExtractor = delimiterExtractor; + this.stringSplitter = stringSplitter; + this.calculator = calculator; + } + + public void run() { + String input = InputView.getInput(); + + List delimiters = delimiterExtractor.extract(input); + List numbers = stringSplitter.split(input, delimiters); + List parsedNumbers = Parser.parse(numbers); + int result = calculator.add(parsedNumbers); + + OutputView.printResult(result); + } +} From d599266b123f0d2008ec8ad5a863655737fe8866 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Mon, 31 Mar 2025 17:34:01 +0900 Subject: [PATCH 24/26] =?UTF-8?q?refactor:=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=EC=9E=90=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기본 구분자를 사용할 때도 추출을 하였으나, 기본 구분자일 경우에는 추출하지 않는 로직으로 변경하였습니다. - 구분자를 위한 상수들을 분리하였습니다. --- src/main/java/calculator/Application.java | 1 - .../controller/CalculatorController.java | 16 ++++++- src/main/java/calculator/model/Delimiter.java | 24 +++++++--- ...terFormat.java => DelimiterConstants.java} | 5 +- .../calculator/model/DelimiterExtractor.java | 46 +++---------------- .../java/calculator/model/StringSplitter.java | 5 +- src/test/java/calculator/ApplicationTest.java | 15 +++--- .../calculator/DelimiterExtractorTest.java | 11 ++--- .../java/calculator/StringSplitterTest.java | 2 +- 9 files changed, 57 insertions(+), 68 deletions(-) rename src/main/java/calculator/model/{CustomDelimiterFormat.java => DelimiterConstants.java} (50%) diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index a8d8d841c9..1525f4eb69 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -7,7 +7,6 @@ public class Application { public static void main(String[] args) { - CalculatorController calculatorController = new CalculatorController( new DelimiterExtractor(), new StringSplitter(), diff --git a/src/main/java/calculator/controller/CalculatorController.java b/src/main/java/calculator/controller/CalculatorController.java index 36e0e3c0f5..e055897b8f 100644 --- a/src/main/java/calculator/controller/CalculatorController.java +++ b/src/main/java/calculator/controller/CalculatorController.java @@ -6,6 +6,7 @@ import calculator.model.StringSplitter; import calculator.view.InputView; import calculator.view.OutputView; +import camp.nextstep.edu.missionutils.Console; import java.util.List; public class CalculatorController { @@ -24,12 +25,25 @@ public CalculatorController(final DelimiterExtractor delimiterExtractor, public void run() { String input = InputView.getInput(); + if (isCheckedEmptyOrNull(input)) { + return; + } List delimiters = delimiterExtractor.extract(input); List numbers = stringSplitter.split(input, delimiters); List parsedNumbers = Parser.parse(numbers); - int result = calculator.add(parsedNumbers); + int result = calculator.add(parsedNumbers); OutputView.printResult(result); + Console.close(); + } + + private boolean isCheckedEmptyOrNull(final String input) { + if (input == null || input.isBlank()) { + OutputView.printResult(0); + return true; + } + + return false; } } diff --git a/src/main/java/calculator/model/Delimiter.java b/src/main/java/calculator/model/Delimiter.java index ca25e7f209..2902186736 100644 --- a/src/main/java/calculator/model/Delimiter.java +++ b/src/main/java/calculator/model/Delimiter.java @@ -1,5 +1,8 @@ package calculator.model; +import static calculator.model.DelimiterConstants.DEFAULT_DELIMITER_REGEX; + +import java.util.List; import java.util.stream.Stream; public enum Delimiter { @@ -8,8 +11,8 @@ public enum Delimiter { DOUBLE_SLASH("//", false), NEW_LINE("\\n", false), NUMBER("[0-9]+", false), - SPACE(" ", false) - ; + SPACE(" ", false); + private final String delimiter; private final boolean isDefault; @@ -19,10 +22,15 @@ public enum Delimiter { this.isDefault = isDefault; } - public static boolean isValidDelimiter(String delimiter) { + public static List getDefaultDelimiter() { return Stream.of(values()) - .filter(d -> d.isDefault) - .anyMatch(d -> d.getDelimiter().equals(delimiter)); + .filter(Delimiter::isDefault) + .map(Delimiter::getDelimiter) + .toList(); + } + + public static boolean isValidDelimiter(String input) { + return DEFAULT_DELIMITER_REGEX.matcher(input).matches(); } public static boolean isValidCustomDelimiter(String delimiter) { @@ -30,7 +38,11 @@ public static boolean isValidCustomDelimiter(String delimiter) { .noneMatch(d -> d.getDelimiter().equals(delimiter)) && !delimiter.matches(NUMBER.getDelimiter()); } - public String getDelimiter() { + private String getDelimiter() { return delimiter; } + + private boolean isDefault() { + return isDefault; + } } diff --git a/src/main/java/calculator/model/CustomDelimiterFormat.java b/src/main/java/calculator/model/DelimiterConstants.java similarity index 50% rename from src/main/java/calculator/model/CustomDelimiterFormat.java rename to src/main/java/calculator/model/DelimiterConstants.java index 5e72a84427..68d3b8a81b 100644 --- a/src/main/java/calculator/model/CustomDelimiterFormat.java +++ b/src/main/java/calculator/model/DelimiterConstants.java @@ -1,6 +1,9 @@ package calculator.model; -public class CustomDelimiterFormat { +import java.util.regex.Pattern; + +public class DelimiterConstants { + public static final Pattern DEFAULT_DELIMITER_REGEX = Pattern.compile("^[0-9,:]+$"); public static final String CUSTOM_DELIMITER_PREFIX = "//"; public static final String CUSTOM_DELIMITER_SUFFIX = "\\n"; } diff --git a/src/main/java/calculator/model/DelimiterExtractor.java b/src/main/java/calculator/model/DelimiterExtractor.java index 73dfeef432..a59cc26142 100644 --- a/src/main/java/calculator/model/DelimiterExtractor.java +++ b/src/main/java/calculator/model/DelimiterExtractor.java @@ -1,11 +1,9 @@ package calculator.model; -import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; -import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; +import static calculator.model.DelimiterConstants.CUSTOM_DELIMITER_PREFIX; +import static calculator.model.DelimiterConstants.CUSTOM_DELIMITER_SUFFIX; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; public class DelimiterExtractor { @@ -14,7 +12,8 @@ public List extract(final String input) { return extractCustomDelimiters(input); } - return extractDefaultDelimiters(input); + validateDefaultDelimiter(input); + return Delimiter.getDefaultDelimiter(); } private boolean isCustomDelimiterFormat(String input) { @@ -29,51 +28,20 @@ private List extractCustomDelimiters(String input) { return delimiters; } - private List extractDefaultDelimiters(String input) { - Set delimiterSet = getDelimiterSet(input); - List delimiters = List.copyOf(delimiterSet); - validateAllDefaultDelimiter(delimiters); - - return delimiters; - } - - private Set getDelimiterSet(final String input) { - Set delimiterSet = new LinkedHashSet<>(); - - for (char c : input.toCharArray()) { - processCharacter(c, delimiterSet); - } - - return delimiterSet; - } - - private void processCharacter(char c, Set delimiterSet) { - if (Character.isDigit(c)) { - return; - } - delimiterSet.add(String.valueOf(c)); - } - private void validateAllCustomDelimiter(List delimiters) { for (String delimiter : delimiters) { validateCustomDelimiter(delimiter); } } - private static void validateCustomDelimiter(final String delimiter) { + private void validateCustomDelimiter(final String delimiter) { if (!Delimiter.isValidCustomDelimiter(delimiter)) { throw new IllegalArgumentException("사용 가능한 커스텀 구분자를 입력해주세요."); } } - private void validateAllDefaultDelimiter(final List delimiters) { - for (String delimiter : delimiters) { - validateDefaultDelimiter(delimiter); - } - } - - private void validateDefaultDelimiter(final String delimiter) { - if (!Delimiter.isValidDelimiter(delimiter)) { + private void validateDefaultDelimiter(final String input) { + if (!Delimiter.isValidDelimiter(input)) { throw new IllegalArgumentException("기본 구분자(,나 :)를 사용해주세요."); } } diff --git a/src/main/java/calculator/model/StringSplitter.java b/src/main/java/calculator/model/StringSplitter.java index 2d28d27a37..3c2adbbf26 100644 --- a/src/main/java/calculator/model/StringSplitter.java +++ b/src/main/java/calculator/model/StringSplitter.java @@ -1,7 +1,6 @@ package calculator.model; -import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_PREFIX; -import static calculator.model.CustomDelimiterFormat.CUSTOM_DELIMITER_SUFFIX; +import static calculator.model.DelimiterConstants.CUSTOM_DELIMITER_SUFFIX; import java.util.List; import java.util.regex.Pattern; @@ -19,7 +18,7 @@ public List split(String input, final List delimiters) { } private String preprocessingCustomDelimiter(String input) { - if (input.startsWith(CUSTOM_DELIMITER_PREFIX) && input.contains(CUSTOM_DELIMITER_SUFFIX)) { + if (input.contains(CUSTOM_DELIMITER_SUFFIX)) { input = input.substring(input.indexOf(CUSTOM_DELIMITER_SUFFIX) + 2); } diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 302bbfe27d..ceae47420c 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -6,18 +6,17 @@ import camp.nextstep.edu.missionutils.test.NsTest; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; class ApplicationTest extends NsTest { - @ParameterizedTest + @Test @DisplayName("값이 입력되지 않았을 때 0이 반환된다.") - @NullAndEmptySource - void shouldReturnZero_whenNotingInput(String input) { + void shouldReturnZero_whenEmptyInput() { assertSimpleTest(() -> { - run(input); + run(" "); assertThat(output()).contains("결과 : 0"); }); } @@ -45,7 +44,7 @@ void shouldReturn_whenUsingCustomDelimiter(String input) { @ParameterizedTest @DisplayName("입력값이 올바르지 않을 때 예외가 발생한다.") @ValueSource(strings = {"1:a:3", "-1,2:3", "-1,2,3"}) - void exception_test(String input) { + void shouldThrowException_whenInvalidInput(String input) { assertSimpleTest(() -> assertThatThrownBy(() -> runException(input)) .isInstanceOf(IllegalArgumentException.class) @@ -54,8 +53,8 @@ void exception_test(String input) { @ParameterizedTest @DisplayName("커스텀 구분자 형식을 올바르게 입력하지 않았을 경우 예외가 발생한다.") - @ValueSource(strings = {"//;1;2;3", "1#2#3", "#\\n1#2#3"}) - void shouldTest(String input) { + @ValueSource(strings = {"//;1;2;3", "1#2#3", "#\\n1#2#3", "/;\\n1;2;3"}) + void shouldThrowException_whenInvalidCustomDelimiterFormat(String input) { assertSimpleTest(() -> assertThatThrownBy(() -> runException(input)) .isInstanceOf(IllegalArgumentException.class) diff --git a/src/test/java/calculator/DelimiterExtractorTest.java b/src/test/java/calculator/DelimiterExtractorTest.java index 43561170d6..ced6257e26 100644 --- a/src/test/java/calculator/DelimiterExtractorTest.java +++ b/src/test/java/calculator/DelimiterExtractorTest.java @@ -8,19 +8,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; class DelimiterExtractorTest { @ParameterizedTest @DisplayName("문자열에서 기본 구분자를 추출한다.") - @CsvSource(value = { - "1,2,3 | ,", - "1:2:3 | :", - "1,2:3 | ,:" - }, delimiter = '|') - void shouldExtractDelimiter_whenGivenString(String input, String expected) { + @ValueSource(strings = {"1,2,3", "1:2:3", "1,2:3"}) + void shouldExtractDelimiter_whenGivenString(String input) { //given DelimiterExtractor extractor = new DelimiterExtractor(); @@ -28,7 +23,7 @@ void shouldExtractDelimiter_whenGivenString(String input, String expected) { List result = extractor.extract(input); //then - assertEquals(List.of(expected.split("")), result); + assertEquals(List.of(",", ":"), result); } @ParameterizedTest diff --git a/src/test/java/calculator/StringSplitterTest.java b/src/test/java/calculator/StringSplitterTest.java index 3daed3d9da..13f249b303 100644 --- a/src/test/java/calculator/StringSplitterTest.java +++ b/src/test/java/calculator/StringSplitterTest.java @@ -1,6 +1,6 @@ package calculator; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import calculator.model.DelimiterExtractor; import calculator.model.StringSplitter; From 417abf757a632c3b8051e9a59a919acddebd9b15 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Thu, 3 Apr 2025 16:08:07 +0900 Subject: [PATCH 25/26] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84=EC=9E=90=20=ED=98=95=EC=8B=9D=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 커스텀 구분자 형식 (`//{delimiter}\n`)을 검증하는 테스트 케이스를 추가하였습니다. - 커스텀 구분자를 추출하기 전 형식이 올바른지 검증하는 로직을 구현하였습니다. --- .../calculator/model/DelimiterExtractor.java | 23 +++++++++++++-- src/test/java/calculator/ApplicationTest.java | 15 ++++++++-- .../calculator/DelimiterExtractorTest.java | 29 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/main/java/calculator/model/DelimiterExtractor.java b/src/main/java/calculator/model/DelimiterExtractor.java index a59cc26142..f42c728837 100644 --- a/src/main/java/calculator/model/DelimiterExtractor.java +++ b/src/main/java/calculator/model/DelimiterExtractor.java @@ -8,7 +8,8 @@ public class DelimiterExtractor { public List extract(final String input) { - if (isCustomDelimiterFormat(input)) { + if (isCheckCustomDelimiterFormat(input)) { + validateCustomDelimiterFormat(input); return extractCustomDelimiters(input); } @@ -16,8 +17,24 @@ public List extract(final String input) { return Delimiter.getDefaultDelimiter(); } - private boolean isCustomDelimiterFormat(String input) { - return input.startsWith(CUSTOM_DELIMITER_PREFIX) && input.contains(CUSTOM_DELIMITER_SUFFIX); + private boolean isCheckCustomDelimiterFormat(final String input) { + return input.startsWith(CUSTOM_DELIMITER_PREFIX) || input.contains(CUSTOM_DELIMITER_SUFFIX); + } + + private void validateCustomDelimiterFormat(final String input) { + if (!input.startsWith(CUSTOM_DELIMITER_PREFIX) || !input.contains(CUSTOM_DELIMITER_SUFFIX)) { + throw new IllegalArgumentException("커스텀 구분자 형식에 맞게 입력해주세요."); + } + validateEmptyCustomDelimiter(input); + } + + private void validateEmptyCustomDelimiter(final String input) { + int start = CUSTOM_DELIMITER_PREFIX.length(); + int end = input.indexOf(CUSTOM_DELIMITER_SUFFIX); + + if (end <= start) { + throw new IllegalArgumentException("커스텀 구분자를 입력해주세요."); + } } private List extractCustomDelimiters(String input) { diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index ceae47420c..48b582df6e 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -21,6 +21,15 @@ void shouldReturnZero_whenEmptyInput() { }); } + @Test + @DisplayName("올바른 값을 하나만 입력했을 경우 결과가 반환된다.") + void shouldReturnValue_whenSingleInput() { + assertSimpleTest(() -> { + run("3"); + assertThat(output()).contains("결과 : 3"); + }); + } + @ParameterizedTest @DisplayName("기본 구분자를 사용하였을 때 정상적으로 결과가 반환된다.") @ValueSource(strings = {"1,2:3", "1,2,3", "1:2:3"}) @@ -42,7 +51,7 @@ void shouldReturn_whenUsingCustomDelimiter(String input) { } @ParameterizedTest - @DisplayName("입력값이 올바르지 않을 때 예외가 발생한다.") + @DisplayName("기본 구분자를 올바르게 입력하지 않았을 경우 예외가 발생한다.") @ValueSource(strings = {"1:a:3", "-1,2:3", "-1,2,3"}) void shouldThrowException_whenInvalidInput(String input) { assertSimpleTest(() -> @@ -52,8 +61,8 @@ void shouldThrowException_whenInvalidInput(String input) { } @ParameterizedTest - @DisplayName("커스텀 구분자 형식을 올바르게 입력하지 않았을 경우 예외가 발생한다.") - @ValueSource(strings = {"//;1;2;3", "1#2#3", "#\\n1#2#3", "/;\\n1;2;3"}) + @DisplayName("커스텀 구분자를 올바르게 입력하지 않았을 경우 예외가 발생한다.") + @ValueSource(strings = {"//;1;2;3", "#\\n1#2#3", "/;\\n1;2;3", "//\\n1;2;3"}) void shouldThrowException_whenInvalidCustomDelimiterFormat(String input) { assertSimpleTest(() -> assertThatThrownBy(() -> runException(input)) diff --git a/src/test/java/calculator/DelimiterExtractorTest.java b/src/test/java/calculator/DelimiterExtractorTest.java index ced6257e26..c32283979a 100644 --- a/src/test/java/calculator/DelimiterExtractorTest.java +++ b/src/test/java/calculator/DelimiterExtractorTest.java @@ -41,6 +41,35 @@ void shouldReturnEmptyList_whenNotDefaultDelimiter(String input) { assertEquals("기본 구분자(,나 :)를 사용해주세요.", exception.getMessage()); } + @ParameterizedTest + @DisplayName("커스텀 구분자의 형식이 올바르지 않을 경우 예외가 발생한다.") + @ValueSource(strings = {"//;1;2;3", "#\\n1#2#3", "/;\\n1;2;3"}) + void shouldThrowException_whenInvalidCustomDelimiterFormat(String input) { + //given + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> extractor.extract(input)); + + //then + assertEquals("커스텀 구분자 형식에 맞게 입력해주세요.", exception.getMessage()); + } + + @Test + @DisplayName("커스텀 구분자 형식에서 구분자가 비어있을 경우 예외가 발생한다.") + void shouldThrowException_whenEmptyCustomDelimiterFormat() { + //given + DelimiterExtractor extractor = new DelimiterExtractor(); + + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> extractor.extract("//\\n1;2;3")); + + //then + assertEquals("커스텀 구분자를 입력해주세요.", exception.getMessage()); + } + @Test @DisplayName("문자열에서 커스텀 구분자를 추출한다.") void shouldExtractCustomDelimiter_whenGivingString() { From a1fbb56f35039691efa4be7dc335bb3917d115b7 Mon Sep 17 00:00:00 2001 From: dd-jiyun Date: Fri, 4 Apr 2025 16:17:14 +0900 Subject: [PATCH 26/26] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20=EC=82=AC=ED=95=AD=20README=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d22553fb7a..4d9bb29fe9 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # java-calculator-precourse ## 미션 목표 -- [ ] OOP(Object Oriented Programming) -- [ ] TDD 실습 + +- 🔺 OOP(Object Oriented Programming) +- ✅ TDD 실습 ## 미션 요구사항 + 입력한 문자열에서 숫자를 추출해 더하는 계산기를 만든다. + - 기본 구분자인 쉼표`,`나 콜론`:`을 구분자로 가지는 문자열을 받으면 구분자를 기준으로 숫자를 분리한 후 숫자를 더한 값을 반환한다. - 기본 구분자 외에 커스텀 구분자를 `//`와 `\n`에 정의하여 사용할 수 있다. - 잘못된 값을 입력할 경우 `IlLegaLArgumentException`을 발생시킨 후 종료된다. @@ -13,20 +16,47 @@ ## 구현 기능 목록 ### 입력 -- [ ] 구분자와 양수로 구성된 문자열을 입력받는다. + +- [x] 구분자와 양수로 구성된 문자열을 입력받는다. ### 구분자 확인 -- [ ] 기본 구분자인지 커스텀 구분자인지 확인한다. + +- [x] 기본 구분자인지 커스텀 구분자인지 확인한다. ### 문자열 분리 -- [ ] 구분자를 기준으로 숫자를 분리한다. + +- [x] 구분자를 기준으로 숫자를 분리한다. ### 문자열 계산 -- [ ] 추출된 문자열을 숫자로 변환한 후 더한다. + +- [x] 추출된 문자열을 숫자로 변환한 후 더한다. ### 예외 상황 -- [ ] 기본 구분자도 커스텀 구분자도 아닌 경우 - - [ ] `,`와 `:` 외의 구분자를 사용한 경우 - - [ ] `//`와 `\n`의 형식을 지키지 않은 경우 + +- [x] 기본 구분자도 커스텀 구분자도 아닌 경우 + - [x] `,`와 `:` 외의 구분자를 사용한 경우 + - [x] `//`와 `\n`의 형식을 지키지 않은 경우 - 음수를 입력한 경우 -- 사용할 수 없는 구분자를 사용한 경우 ex) 숫자, 커스텀 구분자 형식 (`//\n`), 공백 \ No newline at end of file +- 사용할 수 없는 구분자를 사용한 경우 ex) 숫자, 커스텀 구분자 형식 (`// \n`), 공백 + +## 예제 + +| 입력 | 출력 | +|--------------|-----| +| `""` | `0` | +| `1` | `1` | +| `1,2,3` | `6` | +| `1,2:3` | `6` | +| `1:2:3` | `6` | +| `//;\n1;2;3` | `6` | + +## 예외 예제 + +| 입력 | 예외 (`IllegalArgumentException`) | +|---------------|---------------------------------| +| `1,2,-3` | `양수를 입력해주세요` | +| `1,2;3` | `기본 구분자(,나 :)를 사용해주세요.` | +| `1,a,3` | `숫자만 입력해주세요.` | +| `//;1;2;3` | `커스텀 구분자 형식에 맞게 입력해주세요.` | +| `//\n123` | `커스텀 구분자를 입력해주세요.` | +| `//1\n112131` | `사용 가능한 커스텀 구분자를 입력해주세요.` |