Skip to content

Commit c060b41

Browse files
authored
고생하셨습니다.
🎉 PR 머지 완료! 🎉
1 parent 34fd4ec commit c060b41

File tree

11 files changed

+308
-157
lines changed

11 files changed

+308
-157
lines changed

build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ repositories {
1010
}
1111

1212
dependencies {
13-
testImplementation platform('org.junit:junit-bom:5.9.1')
14-
testImplementation('org.junit.jupiter:junit-jupiter')
1513
testImplementation platform('org.junit:junit-bom:5.9.1')
1614
testImplementation platform('org.assertj:assertj-bom:3.25.1')
1715
testImplementation('org.junit.jupiter:junit-jupiter')
16+
1817
testImplementation('org.assertj:assertj-core')
1918
}
2019

src/main/java/Calculator.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/main/java/StringCalculator.java

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/main/java/domain/Calculator.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package domain;
2+
3+
import exception.ErrorMessage;
4+
5+
public class Calculator {
6+
7+
public int add(int num1, int num2) {
8+
return Math.addExact(num1, num2);
9+
}
10+
11+
public int sub(int num1, int num2) {
12+
return Math.subtractExact(num1, num2);
13+
}
14+
15+
public int mul(int num1, int num2) {
16+
return Math.multiplyExact(num1, num2);
17+
}
18+
19+
public int div(int num1, int num2) {
20+
if (num2 == 0)
21+
throw new ArithmeticException(ErrorMessage.DIVIDE_BY_ZERO);
22+
23+
return num1 / num2;
24+
}
25+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package domain;
2+
3+
import exception.ErrorMessage;
4+
import util.DelimiterUtil;
5+
import util.ParseUtil;
6+
7+
import java.util.Arrays;
8+
import java.util.List;
9+
import java.util.regex.Pattern;
10+
11+
public class StringCalculator {
12+
13+
public int calculate(String str) {
14+
if (str == null) {
15+
throw new RuntimeException(ErrorMessage.NULL_STRING);
16+
}
17+
18+
if (str.isBlank())
19+
return 0;
20+
21+
String customDelimiter = DelimiterUtil.findCustomDelimiter(str);
22+
String strNumbers = extractNumber(str, customDelimiter);
23+
String[] tokens = splitTokens(strNumbers, customDelimiter);
24+
List<Integer> numbers = ParseUtil.parseNumber(tokens);
25+
26+
return sum(numbers);
27+
}
28+
29+
private String extractNumber(String str, String customDelimiter) {
30+
if (customDelimiter != null) {
31+
return str.substring(str.indexOf("\n") + 1);
32+
}
33+
34+
return str;
35+
}
36+
37+
private String[] splitTokens(String strNumbers, String customDelimiter) {
38+
String delimiterRegex = "[,|:]";
39+
40+
if (customDelimiter == null) {
41+
return strNumbers.split(delimiterRegex);
42+
}
43+
44+
return strNumbers.split(Pattern.quote(customDelimiter));
45+
}
46+
47+
private int sum(List<Integer> numbers) {
48+
return numbers.stream()
49+
.mapToInt(Integer::intValue)
50+
.sum();
51+
}
52+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package exception;
2+
3+
public class ErrorMessage {
4+
5+
public static final String NULL_STRING = "문자열에 null 값이 전달되었습니다.";
6+
public static final String CUSTOM_DELIMITER_NOT_FOUND = "커스텀 구분자를 찾을 수 없습니다.";
7+
public static final String NEGATIVE_NUMBER_NOT_ALLOWED = "음수는 처리할 수 없습니다.";
8+
public static final String INVALID_STRING = "문자열은 처리할 수 없습니다.";
9+
public static final String DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다.";
10+
11+
private ErrorMessage() {
12+
throw new RuntimeException();
13+
}
14+
}

src/main/java/util/DelimiterUtil.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package util;
2+
3+
import exception.ErrorMessage;
4+
5+
public class DelimiterUtil {
6+
7+
public static String findCustomDelimiter(String str) {
8+
int startDelimiterIdx = str.indexOf("//");
9+
int endDelimiterIdx = str.indexOf("\n");
10+
11+
if (startDelimiterIdx == -1 && endDelimiterIdx == -1) {
12+
return null;
13+
}
14+
15+
if ((startDelimiterIdx == -1) ^ (endDelimiterIdx == -1) || (startDelimiterIdx + 2 == endDelimiterIdx)) {
16+
throw new RuntimeException(ErrorMessage.CUSTOM_DELIMITER_NOT_FOUND);
17+
}
18+
19+
return str.substring(startDelimiterIdx + 2, endDelimiterIdx);
20+
}
21+
}

src/main/java/util/ParseUtil.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package util;
2+
3+
import exception.ErrorMessage;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
8+
public class ParseUtil {
9+
10+
public static List<Integer> parseNumber(String[] tokens) {
11+
return Arrays.stream(tokens)
12+
.map(ParseUtil::parseTokenToInt)
13+
.toList();
14+
}
15+
16+
public static int parseTokenToInt(String token) {
17+
try {
18+
int number = Integer.parseInt(token.trim());
19+
20+
if (number < 0) {
21+
throw new RuntimeException(ErrorMessage.NEGATIVE_NUMBER_NOT_ALLOWED);
22+
}
23+
24+
return number;
25+
} catch (NumberFormatException ex) {
26+
throw new RuntimeException(ErrorMessage.INVALID_STRING);
27+
}
28+
}
29+
}

src/test/java/CalculatorTest.java

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,83 @@
1-
import org.junit.jupiter.api.DisplayName;
1+
import domain.Calculator;
2+
import exception.ErrorMessage;
23
import org.junit.jupiter.api.Test;
34

4-
import static org.assertj.core.api.Assertions.assertThat;
5-
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5+
import static org.assertj.core.api.Assertions.*;
66

7-
@DisplayName("계산기 기능 테스트 클래스")
87
public class CalculatorTest {
8+
9+
private final Calculator calculator = new Calculator();
10+
11+
@Test
12+
public void 덧셈_테스트() {
13+
final int num1 = 1;
14+
final int num2 = 2;
15+
final int actual = calculator.add(num1, num2);
16+
17+
assertThat(actual).isEqualTo(num1 + num2);
18+
}
19+
920
@Test
10-
@DisplayName("더하기 연산 테스트")
11-
public void testAdd() {
12-
int num1 = 1, num2 = 2;
21+
public void 뺄셈_테스트() {
22+
final int num1 = 3;
23+
final int num2 = 2;
24+
final int actual = calculator.sub(num1, num2);
1325

14-
assertThat(3).isEqualTo(Calculator.add(num1, num2));
26+
assertThat(actual).isEqualTo(num1 - num2);
1527
}
1628

1729
@Test
18-
@DisplayName("빼기 연산 테스트")
19-
public void testSub() {
20-
int num1 = 1, num2 = 2;
30+
public void 곱셈_테스트() {
31+
final int num1 = 3;
32+
final int num2 = 2;
33+
final int actual = calculator.mul(num1, num2);
2134

22-
assertThat(-1).isEqualTo(Calculator.sub(num1, num2));
35+
assertThat(actual).isEqualTo(num1 * num2);
2336
}
2437

2538
@Test
26-
@DisplayName("곱하기 연산 테스트")
27-
public void testMul() {
28-
int num1 = 10, num2 = 2;
39+
public void 나눗셈_테스트() {
40+
final int num1 = 4;
41+
final int num2 = 2;
42+
final int actual = calculator.div(num1, num2);
2943

30-
assertThat(20).isEqualTo(Calculator.mul(num1, num2));
44+
assertThat(actual).isEqualTo(num1 / num2);
3145
}
3246

3347
@Test
34-
@DisplayName("나누기 연산 테스트")
35-
public void testDiv() {
36-
int num1 = 1, num2 = 2;
48+
public void 덧셈_오버플로우_예외_발생() {
49+
final int num1 = Integer.MAX_VALUE;
50+
final int num2 = Integer.MAX_VALUE;
3751

38-
assertThat(0).isEqualTo(Calculator.div(num1, num2));
52+
assertThatThrownBy(() -> calculator.add(num1, num2))
53+
.isInstanceOf(ArithmeticException.class);
3954
}
4055

4156
@Test
42-
@DisplayName("나누기 연산에서 나누는 값이 0인지 테스트")
43-
public void testDivideByZero() {
44-
int num1 = 2, num2 = 0;
57+
public void 뺄셈_오버플로우_예외_발생() {
58+
final int num1 = Integer.MIN_VALUE;
59+
final int num2 = Integer.MAX_VALUE;
4560

46-
assertThatThrownBy(() -> {
47-
Calculator.div(num1, num2);
48-
}).isInstanceOf(ArithmeticException.class);
61+
assertThatThrownBy(() -> calculator.sub(num1, num2))
62+
.isInstanceOf(ArithmeticException.class);
4963
}
5064

5165
@Test
52-
@DisplayName("연산 후 int 범위를 벗어나는 지 테스트")
53-
public void testOverflow() {
54-
assertThatThrownBy(() -> {
55-
Calculator.add(Integer.MAX_VALUE, 1);
56-
}).isInstanceOf(ArithmeticException.class);
57-
58-
assertThatThrownBy(() -> {
59-
Calculator.sub(Integer.MIN_VALUE, 1);
60-
}).isInstanceOf(ArithmeticException.class);
61-
62-
assertThatThrownBy(() -> {
63-
Calculator.mul(Integer.MAX_VALUE, 2);
64-
}).isInstanceOf(ArithmeticException.class);
66+
public void 곱셈_오버플로우_예외_발생() {
67+
final int num1 = Integer.MAX_VALUE;
68+
final int num2 = Integer.MAX_VALUE;
69+
70+
assertThatThrownBy(() -> calculator.mul(num1, num2))
71+
.isInstanceOf(ArithmeticException.class);
72+
}
73+
74+
@Test
75+
public void 나눗셈_0으로_나누면_예외_발생() {
76+
final int num1 = Integer.MAX_VALUE;
77+
final int num2 = 0;
78+
79+
assertThatThrownBy(() -> calculator.div(num1, num2))
80+
.isInstanceOf(ArithmeticException.class)
81+
.hasMessageContaining(ErrorMessage.DIVIDE_BY_ZERO);
6582
}
6683
}

0 commit comments

Comments
 (0)