-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🚀 2단계 - 문자열 덧셈 계산기 #5916
🚀 2단계 - 문자열 덧셈 계산기 #5916
Changes from all commits
d9fac6a
941666c
67bdcd8
b2cd7ec
4ea613c
5c4c8c7
7ba4a01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package step2.calculator; | ||
|
||
public class StringAddCalculator { | ||
|
||
public static int calculate(String input) { | ||
validate(input); | ||
StringAddCalculatorToken[] tokens = split(input); | ||
return sum(tokens); | ||
} | ||
|
||
private static void validate(String input) { | ||
StringAddCalculatorInputValidator.validate(input); | ||
} | ||
|
||
private static StringAddCalculatorToken[] split(String input) { | ||
return StringAddCalculatorTokenGenerator.generateTokens(input); | ||
} | ||
|
||
private static int sum(StringAddCalculatorToken[] tokens) { | ||
return StringAddCalculatorTokenCalculator.sum(tokens); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package step2.calculator; | ||
|
||
class StringAddCalculatorInputValidatorException extends RuntimeException { | ||
|
||
public StringAddCalculatorInputValidatorException(String message) { | ||
super(message); | ||
} | ||
} | ||
|
||
public class StringAddCalculatorInputValidator { | ||
|
||
public static void validate(String input) { | ||
if (input == null || input.isEmpty()) { | ||
// "null" and "Empty String" are valid in StringAddCalculator | ||
return; | ||
} | ||
|
||
if (input.isBlank()) { | ||
throw new StringAddCalculatorInputValidatorException("Input cannot be blank"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package step2.calculator; | ||
|
||
|
||
class StringAddCalculatorTokenException extends RuntimeException { | ||
|
||
public StringAddCalculatorTokenException(String message) { | ||
super(message); | ||
} | ||
} | ||
|
||
|
||
public class StringAddCalculatorToken { | ||
|
||
private final int value; | ||
|
||
public StringAddCalculatorToken(String value) { | ||
if (value.isEmpty()) { | ||
this.value = 0; | ||
return; | ||
} | ||
this.value = Integer.parseInt(value); | ||
validate(); | ||
} | ||
|
||
public StringAddCalculatorToken(int number) { | ||
this.value = number; | ||
validate(); | ||
} | ||
|
||
private void validate() { | ||
if (this.value < 0) { | ||
throw new StringAddCalculatorTokenException("Don't allow negative number"); | ||
} | ||
} | ||
|
||
public int toInteger() { | ||
return this.value; | ||
} | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package step2.calculator; | ||
|
||
public class StringAddCalculatorTokenCalculator { | ||
|
||
public static int sum(StringAddCalculatorToken[] tokens) { | ||
int result = 0; | ||
for (StringAddCalculatorToken stringAddCalculatorToken : tokens) { | ||
result += stringAddCalculatorToken.toInteger(); | ||
} | ||
return result; | ||
} | ||
} | ||
Comment on lines
+3
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 sum을 할 수 있는 별도의 역할을 표현해주셨네요~! 이와 관련하여 일급컬렉션이라는 개념을 한번 찾아보시고 다음 미션부터 참고하여 반영해보시면 좋을것 같습니다 :) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package step2.calculator; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class StringAddCalculatorTokenGenerator { | ||
|
||
final static String[] defaultDelimiters = {",", ":"}; | ||
final static String[] defaultCustomDelimiterRegexes = {"//(\\D)\n(.*)"}; | ||
|
||
public static StringAddCalculatorToken[] generateTokens(String input) { | ||
if (input == null || input.isEmpty()) { | ||
return new StringAddCalculatorToken[]{new StringAddCalculatorToken(0)}; | ||
} | ||
|
||
ArrayList<String> delimiters = new ArrayList<>(Arrays.asList(defaultDelimiters)); | ||
|
||
for (String customDelimiterRegex : defaultCustomDelimiterRegexes) { | ||
Matcher matcher = Pattern.compile(customDelimiterRegex).matcher(input); | ||
if (matcher.find()) { | ||
String customDelimiter = matcher.group(1); | ||
delimiters.add(customDelimiter); | ||
input = matcher.group(2); | ||
Comment on lines
+23
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1과 2는 어떠한 의미를 가지는 숫자일까요? 🤔 다른사람이 단번에 알아차릴 수 있도록 이름을 부여해보는건 어떨까요? |
||
} | ||
} | ||
Comment on lines
+20
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 일련의 과정도 이 애플리케이션에서 아주 중요한 항목으로 보이기 때문에 굉장히 테스트를 하면 좋을 부분으로 보이네요! 이렇게 패턴을 찾고 문자열과 구분자를 분리해내는 것도 하나의 클래스가 담당할 수 있게 만들어보면 좋을 것 같습니다~ 그리고 거기서 더 나아간다면 확장에 용이하도록 디자인 패턴(전략 패턴)을 적용해볼 수 있을것 같네요! 지금하기에는 너무 많은 변경사항이니 관련하여 슬~쩍 찾아만 보셔도 좋을것 같습니다. :) |
||
|
||
String[] stringTokens = input.split(String.format("[%s]", String.join("", delimiters))); | ||
if (input.startsWith("-") && delimiters.contains("-")) { | ||
stringTokens[1] = "-" + stringTokens[1]; | ||
} | ||
|
||
StringAddCalculatorToken[] stringAddCalculatorTokens = new StringAddCalculatorToken[stringTokens.length]; | ||
for (int i = 0; i < stringTokens.length; i++) { | ||
stringAddCalculatorTokens[i] = new StringAddCalculatorToken(stringTokens[i]); | ||
} | ||
|
||
return stringAddCalculatorTokens; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package step1.study; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; | ||
|
||
public class StringTest { | ||
|
||
@Test | ||
@DisplayName("String.split()") | ||
void split() { | ||
String value1 = "1,2"; | ||
assertThat(value1.split(",")).containsExactly("1", "2"); | ||
|
||
String value2 = "1"; | ||
assertThat(value2.split(",")).containsExactly("1"); | ||
} | ||
|
||
@Test | ||
@DisplayName("String.substring()") | ||
void substring() { | ||
String value = "(1,2)"; | ||
assertThat(value.substring(1, value.length() - 1)).isEqualTo("1,2"); | ||
} | ||
|
||
@Test | ||
@DisplayName("String.charAt()") | ||
void charAt() { | ||
String value = "abc"; | ||
assertThat(value.charAt(0)).isEqualTo('a'); | ||
assertThat(value.charAt(1)).isEqualTo('b'); | ||
assertThat(value.charAt(2)).isEqualTo('c'); | ||
assertThatThrownBy(() -> {value.charAt(3);}). | ||
isExactlyInstanceOf(StringIndexOutOfBoundsException.class); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package step2.calculator; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class StringAddCalculatorTest { | ||
|
||
@Test | ||
@DisplayName("input: null --> output: 0") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 보통 테스트 이름은 서술적으로 표현하는 편인 것 같습니다~ "입력이 null이면 기본값을 반환한다." 와 같이 지을 수 있을것 같네요 :) |
||
public void givenNull_whenCalculate_thenReturnZero() { | ||
int result = StringAddCalculator.calculate(null); | ||
assertThat(result).isEqualTo(0); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"\"(=Empty String) --> output: 0") | ||
public void givenEmptyString_whenCalculate_thenReturnZero() { | ||
int result = StringAddCalculator.calculate(""); | ||
assertThat(result).isEqualTo(0); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \" \"(=Blank) --> output: RuntimeException") | ||
public void givenBlank_whenCalculate_thenThrowException() { | ||
assertThatThrownBy(() -> StringAddCalculator.calculate(" ")).isExactlyInstanceOf( | ||
StringAddCalculatorInputValidatorException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"1\" --> output: 1") | ||
public void givenOneNumber_whenCalculate_thenReturnJustNumber() { | ||
int result = StringAddCalculator.calculate("1"); | ||
assertThat(result).isEqualTo(1); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"1,2\" --> output: 3") | ||
public void givenTwoNumbersWithComma_whenCalculate_thenReturnSum() { | ||
int result = StringAddCalculator.calculate("1,2"); | ||
assertThat(result).isEqualTo(3); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"1,2:3\" --> output: 6") | ||
public void givenThreeNumbersWithCommaAndColon_whenCalculate_thenReturnSum() { | ||
int result = StringAddCalculator.calculate("1,2:3"); | ||
assertThat(result).isEqualTo(6); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"//;\\n1;2;3\" --> output: 6") | ||
public void givenThreeNumbersWithCustomDelimiter_whenCalculate_thenReturnSum() { | ||
int result = StringAddCalculator.calculate("//;\n1;2;3"); | ||
assertThat(result).isEqualTo(6); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"//-\n1-2-3\" --> output: 6") | ||
public void givenThreeNumbersWithCustomDelimiterMinus_whenCalculate_thenReturnSum() { | ||
int result = StringAddCalculator.calculate("//-\n1-2-3"); | ||
assertThat(result).isEqualTo(6); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"-1,2,3\" --> output: StringAddCalculatorTokenException(=RuntimeException)") | ||
public void givenNegativeNumber_whenCalculate_thenThrowException() { | ||
assertThatThrownBy(() -> StringAddCalculator.calculate("-1,2,3")).isExactlyInstanceOf( | ||
StringAddCalculatorTokenException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"//-\n-1-2-3\" --> output: RuntimeException") | ||
public void givenNegativeNumberWithCustomDelimiterMinus_whenCalculate_thenThrowException() { | ||
assertThatThrownBy(() -> StringAddCalculator.calculate("//-\n-1-2-3")).isExactlyInstanceOf( | ||
StringAddCalculatorTokenException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("input: \"11,22:33\" --> output: 66") | ||
public void givenTwoDigits_whenCalculate_thenReturnSum() { | ||
int result = StringAddCalculator.calculate("11,22:33"); | ||
assertThat(result).isEqualTo(66); | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍 👍
그런데 Exception과 같은 파일에 선언해두면 클래스를 찾기 어려우니 독립적으로 선언해주시면 더 좋을것 같습니다. :)