Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

자동차 경주: 2단계 - 문자열 덧셈 계산기 #5885

Merged
merged 15 commits into from
Mar 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
cb1cdbc
feat(1단계) - 문자열 덧셈 계산기 요구사항 기본 테스트 클래스 추가
HongYeseul Mar 11, 2025
0523fcd
feat(1단계) - 문자열 덧셈 계산기 StringParser 클래스 추가
HongYeseul Mar 11, 2025
f0e04fa
feat(1단계: 문자열 덧셈 계산기) - 기본 요구 사항 구현
HongYeseul Mar 11, 2025
a816c03
refactor(1단계: 문자열 덧셈 계산기) - 클래스 및 메서드 별 관심사 분리
HongYeseul Mar 11, 2025
7cde672
refactor(1단계: 문자열 덧셈 계산기) - 입력값을 위한 클래스 추가
HongYeseul Mar 12, 2025
55055ce
refactor(1단계: 문자열 덧셈 계산기) - Input 클래스를 사용해 클래스 분리
HongYeseul Mar 13, 2025
cfe50d7
refactor(1단계: 문자열 덧셈 계산기) - null과 빈문자 테스트의 경우 NullAndEmptySource 로 교체
HongYeseul Mar 13, 2025
2835957
refactor(1단계: 문자열 덧셈 계산기) - 매직넘버 상수화
HongYeseul Mar 13, 2025
4f953df
fix(1단계: 문자열 덧셈 계산기) - 음수 Matcher '-' 앞 0개 이상 문자 검사
HongYeseul Mar 13, 2025
87ee8af
refactor(1단계: 문자열 덧셈 계산기) - defaultDelimiter array 상수로 전환하여 join해서 사용…
HongYeseul Mar 13, 2025
b61bd79
refactor(1단계: 문자열 덧셈 계산기) - 테스트 코드 내 ParameterizedTest 로 변경
HongYeseul Mar 13, 2025
3109fc7
refactor(1단계: 문자열 덧셈 계산기) - defaultRegex Input 으로 위치 이동
HongYeseul Mar 13, 2025
90c9203
refactor(1단계: 문자열 덧셈 계산기) - Input 클래스 제거
HongYeseul Mar 14, 2025
6450461
test(1단계: 문자열 덧셈 계산기) - Input 클래스 제거 후 메서드 명 변경
HongYeseul Mar 14, 2025
6a187bc
test(1단계: 문자열 덧셈 계산기) - 메서드 분리
HongYeseul Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/java/step2/CalculatorMatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package step2;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CalculatorMatcher {

private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";

private static final Pattern CUSTOM_DELIMITER_PATTERN = Pattern.compile(CUSTOM_DELIMITER_REGEX);

public static Matcher getCustomMatcher(String input) {
Copy link

Choose a reason for hiding this comment

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

static function만 가지고 있는 클래스는 util성 클래스와 그 형태가 비슷해지는데요 :)

이 클래스를 Default delimiter도 사용할 수 있도록 클래스를 설계해보면 좋을것 같습니다.

return CUSTOM_DELIMITER_PATTERN.matcher(input);
}
}
74 changes: 74 additions & 0 deletions src/main/java/step2/StringAddCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package step2;

import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

public class StringAddCalculator {

public static final List<String> DEFAULT_DELIMITER = List.of(",", ":");
private static final int GROUP_INDEX_DELIMITER = 1;
private static final int GROUP_INDEX_NUMBERS = 2;

/**
* 문자열 덧셈 계산기
*
* @param input 입력값
* @return 계산 결과
*/
public static int calculate(String input) {
if (isNullOrBlack(input)) {
return 0;
}

return sum(toInts(split(input)));
}

private static boolean isNullOrBlack(String input) {
return input == null || input.isBlank();
}

private static List<String> split(String input) {
Matcher customMatcher = CalculatorMatcher.getCustomMatcher(input);

if (customMatcher.find()) {
return splitWithCustomDelimiter(customMatcher);
}
return List.of(input.split(defaultDelimiterRegex()));
}

private static List<String> splitWithCustomDelimiter(Matcher customMatcher) {
return Arrays.stream(customMatcher.group(GROUP_INDEX_NUMBERS)
.split(customMatcher.group(GROUP_INDEX_DELIMITER)))
.collect(Collectors.toList());
}

public static String defaultDelimiterRegex() {
return "[" + String.join("", DEFAULT_DELIMITER) + "]";
}

private static List<Integer> toInts(List<String> values) {
return values.stream()
.map(value -> toInt(value))
.collect(Collectors.toList());
}

private static int toInt(String value) {
int number = Integer.parseInt(value);
checkNegativeNumber(number);
return number;
}

private static void checkNegativeNumber(int number) {
if (number < 0) {
throw new RuntimeException(String.format("음수(%s)는 사용할 수 없습니다.", number));
}
}
Comment on lines +57 to +67
Copy link

Choose a reason for hiding this comment

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

이러한 역할을 갖는 클래스를 도출해보면 좋을것 같습니다 :)


private static int sum(List<Integer> values) {
return values.stream()
.mapToInt(Integer::intValue)
.sum();
}
}
69 changes: 69 additions & 0 deletions src/test/java/step2/StringAddCalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package step2;

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;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static step2.StringAddCalculator.calculate;

public class StringAddCalculatorTest {

@ParameterizedTest
@DisplayName("숫자 하나를 문자열로 입력할 경우 해당 숫자를 반환한다.")
@ValueSource(strings = {"1", "2", "3"})
public void calculate_숫자하나(String input) throws Exception {
Copy link

Choose a reason for hiding this comment

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

모든 테스트에서 throw를 하고 있는것 같은데 필요하지 않는것 같아보이네요~

int result = calculate(input);
assertThat(result).isEqualTo(Integer.parseInt(input));
}

@ParameterizedTest
@NullAndEmptySource
@DisplayName("null 또는 빈문자를 입력할 경우 0을 반환해야 한다.")
public void calculate_null_또는_빈문자(String input) {
assertThat(calculate(input)).isEqualTo(0);
}

@ParameterizedTest
@DisplayName("숫자 두 개를 콤마(,) 구분자로 입력할 경우 두 숫자의 합을 반환한다.")
@ValueSource(strings = {"1,2", "1,2,"})
public void calculate_쉼표구분자(String input) throws Exception {
int result = calculate(input);
assertThat(result).isEqualTo(3);
}

@Test
@DisplayName("구분자는 콤마(,)와 콜론(:)을 사용할 수 있다.")
public void calculate_쉼표_또는_콜론_구분자() throws Exception {
int result = calculate("1,2:3");
assertThat(result).isEqualTo(6);
}

@ParameterizedTest
@DisplayName("구분자는 //와 /n 문자를 이용해 커스텀 구분자를 지정할 수 있다.")
@ValueSource(strings = {";", ":"})
public void calculate_custom_구분자(String delimiter) throws Exception {
List<String> inputNumbers = List.of("1", "2", "3");
String customDelimiter = "//" + delimiter + "\n";

int result = calculate(customDelimiter + String.join(delimiter, inputNumbers));
assertThat(result).isEqualTo(
inputNumbers.stream()
.mapToInt(Integer::parseInt)
.sum()
);
}

@ParameterizedTest
@DisplayName("음수를 전달할 경우 예외가 발생한다.")
@ValueSource(strings = {"-1", "1,-2,3"})
public void calculate_negative(String input) throws Exception {
assertThatThrownBy(() -> calculate(input))
.isInstanceOf(RuntimeException.class);
}
}