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

[Step2] 문자열 덧셈 계산기 #1578

Merged
merged 11 commits into from May 23, 2021
36 changes: 10 additions & 26 deletions src/main/java/calculator/AddCalculatorModel.java
@@ -1,42 +1,26 @@
package calculator;

import static calculator.NumberUtils.*;
import static calculator.StringUtils.*;

import java.util.regex.Pattern;

public final class AddCalculatorModel {
private final static String DEFAULT_DELIMITER_REGEX = ",|:";
private final static String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";

private final String userInput;

private long sum = 0L;

public AddCalculatorModel(String userInput) {
this.userInput = userInput;
}

public String userInput() {
return userInput;
}

public long execute() {
if (isBlank(userInput())) {
return 0L;
public long execute(String userInput) {
if (isBlank(userInput)) {
return sum;
}

DelimiterMatcher matcher = DelimiterMatcher.create(userInput(), Pattern.compile(CUSTOM_DELIMITER_REGEX));

if (matcher.hasCustomDelimiter()) {
return calculate(requireNumber(matcher.getSplitTokens()));
if (CustomDelimiterMatcher.hasDelimiter(userInput)) {
String[] splitTokens = CustomDelimiterMatcher.getSplitTokens(userInput);
return calculate(StringUtils.convertStringToLong(splitTokens));
}
return calculate(getSplit(requireNumber(userInput()), DEFAULT_DELIMITER_REGEX));
return calculate(StringUtils.convertStringToLong(userInput.split(DEFAULT_DELIMITER_REGEX)));
}

private long calculate(String[] strings) {
for (String s : strings) {
this.sum += Long.parseLong(s);
private long calculate(Long[] longs) {
for (Long l : longs) {
this.sum += l;
}
return sum;

Choose a reason for hiding this comment

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

지금과 같은 복잡도에서는 크게 상관은 없겠지만 List 등을 활용하면 어떨까요?

List에서 제공하는 내장 함수나 Stream을 활용하면 좋을 것 같네요.

다음 단계부터는 배열의 사용을 지양하고 List를 사용해보시면 좋을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

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

네 👍 피드백 감사합니다.

}
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/calculator/CustomDelimiterMatcher.java
@@ -0,0 +1,31 @@
package calculator;

import static java.util.Objects.*;

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

public final class CustomDelimiterMatcher {

private final Matcher matcher;
private final static Pattern CUSTOM_DELIMITER_PATTERN = Pattern.compile("//(.)\n(.*)");

private CustomDelimiterMatcher(Matcher matcher) {
requireNonNull(matcher);
this.matcher = matcher;
}

public static String[] getSplitTokens(String userInput) {
Matcher matcher = new CustomDelimiterMatcher(CUSTOM_DELIMITER_PATTERN.matcher(userInput)).matcher;
if (matcher.find() == false) {
throw new RuntimeException("커스텀 구분자를 찾을 수 없습니다");
}
String delimiter = matcher.group(1);
String group = matcher.group(2);
return group.split(delimiter);
}

public static boolean hasDelimiter(String userInput) {
return CUSTOM_DELIMITER_PATTERN.asPredicate().test(userInput);
}
}
41 changes: 0 additions & 41 deletions src/main/java/calculator/DelimiterMatcher.java

This file was deleted.

3 changes: 2 additions & 1 deletion src/main/java/calculator/Main.java
Expand Up @@ -6,6 +6,7 @@ public class Main {

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println(new AddCalculatorModel(sc.nextLine()).execute());
AddCalculatorModel model = new AddCalculatorModel();
System.out.println(model.execute(sc.nextLine()));
}
}
22 changes: 6 additions & 16 deletions src/main/java/calculator/NumberUtils.java
@@ -1,24 +1,14 @@
package calculator;

public final class NumberUtils {
import java.util.regex.Pattern;

public static String[] requireNumber(String[] strings) {
for (String s : strings) {
requireNumber(s);
}
return strings;
}
public final class NumberUtils {

public static String requireNumber(String string) {
for (char aChar : string.toCharArray()) {
requireNumber(aChar);
}
return string;
}
private static final Pattern numberPattern = Pattern.compile("[0-9]*");

public static void requireNumber(char aChar) {
if (aChar == '-' || Character.isAlphabetic(aChar)) {
throw new RuntimeException();
public static void validateString(String string) {
if (numberPattern.matcher(string).matches() == false) {
throw new IllegalArgumentException("유효하지 않은 숫자가 사용되었습니다.");
}
}
}
15 changes: 11 additions & 4 deletions src/main/java/calculator/StringUtils.java
@@ -1,14 +1,21 @@
package calculator;

import static calculator.NumberUtils.*;
import static java.util.Objects.*;

public final class StringUtils {

public static String[] getSplit(String s, String regex) {
return s.split(regex);
}

public static boolean isBlank(String s) {
return isNull(s) || s.equals("");
}

static Long[] convertStringToLong(String[] strings) {
Long[] result = new Long[strings.length];
for (int i = 0; i < strings.length; i++) {
String value = strings[i];
validateString(value);
result[i] = Long.parseLong(value);
}
return result;
}
}
41 changes: 20 additions & 21 deletions src/test/java/calculator/AddCalculatorModelTest.java
Expand Up @@ -2,65 +2,64 @@

import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;

class AddCalculatorModelTest {

AddCalculatorModel sut;
private AddCalculatorModel sut;

@BeforeEach
void setUp() {
sut = new AddCalculatorModel();
}

@ParameterizedTest(name = "빈 문자열 또는 Null 테스트")
@NullAndEmptySource
void 빈문자열_또는_Null_값을_입력할경우_0을_반환(String userInput) {
sut = new AddCalculatorModel(userInput);
assertThat(sut.execute()).isEqualTo(0);
assertThat(sut.execute(userInput)).isEqualTo(0);
}

@ParameterizedTest
@ValueSource(strings = {"1", "2", "999999999999"})
void 숫자_하나를_문자열로_입력할_경우_해당_숫자를_반환(String value) {
long expected = Long.parseLong(value);
sut = new AddCalculatorModel(value);
assertThat(sut.execute()).isEqualTo(expected);
assertThat(sut.execute(value)).isEqualTo(expected);
}

@ParameterizedTest
@ValueSource(strings = {"-1", "-1,-1", "-1,2:3"})
void 마이너스_또는_유효하지_않는_값_입력시_예외호출(String value) {
sut = new AddCalculatorModel(value);
assertThatThrownBy(() -> sut.execute())
.isInstanceOf(RuntimeException.class);
assertThatThrownBy(() -> sut.execute(value))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("유효하지 않은 숫자가 사용되었습니다.");
}

@ParameterizedTest
@CsvSource(value = {"0,1:1", "1,2:3", "999999999999,999999999999:1999999999998"}, delimiterString = ":")
void 숫자_두개를_컴마구분자로_입력할_경우_두숫자의_합을_반환한다(String userInput, Long expected) {
sut = new AddCalculatorModel(userInput);
assertThat(sut.execute()).isEqualTo(expected);
assertThat(sut.execute(userInput)).isEqualTo(expected);
}

@ParameterizedTest(name = "구분자를_컴마_뿐만_아니라_콜론도_가능")
@ValueSource(strings = {"1,2:3","1:2:3"})
@ValueSource(strings = {"1,2:3", "1:2:3"})
void 구분자를_컴마_뿐만_아니라_콜론도_가능(String value) {
sut = new AddCalculatorModel(value);
assertThat(sut.execute()).isEqualTo(6);
assertThat(sut.execute(value)).isEqualTo(6);
}

@ParameterizedTest(name = "[{index}]({argumentsWithNames})“//”와 “\\n” 문자 사이에 커스텀 구분자를 지정하면 지정된 커스텀 구분자로 구분된다.")
@ValueSource(strings = {"//;\n1;2;3","//a\n1a2a3", "//!\n1!2!3"})
@ValueSource(strings = {"//;\n1;2;3", "//a\n1a2a3", "//!\n1!2!3"})
void _1(String value) {
sut = new AddCalculatorModel(value);
assertThat(sut.execute()).isEqualTo(6);
assertThat(sut.execute(value)).isEqualTo(6);
}

@ParameterizedTest(name = "[{index}] 유효하지 않는 커스텀 구분자({argumentsWithNames})를 지정하면 예외 호출한다.")
@ValueSource(strings = {"//abc\n1abc2abc3", "//;\n1;2:3", "//!\n1!2,3"})
void _2(String value) {
sut = new AddCalculatorModel(value);
assertThatThrownBy(() -> sut.execute()).isInstanceOf(RuntimeException.class);
assertThatThrownBy(() -> sut.execute(value)).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("유효하지 않은 숫자가 사용되었습니다.");
}
}
}