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

[문자열 덧셈 계산기] 송재훈 미션 제출합니다. #4

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1656366
docs: 기능 요구사항 및 구현할 기능 목록 작성
SongJaeHoonn Oct 17, 2024
bac710f
docs: 기능 요구사항 수정
SongJaeHoonn Oct 17, 2024
420bc6b
feat: 문자열 입력 클래스 분리
SongJaeHoonn Oct 17, 2024
d819ad3
feat: 문자열 입력받기 기능 구현
SongJaeHoonn Oct 17, 2024
0274d61
docs: 기능 요구사항 수정
SongJaeHoonn Oct 17, 2024
c77d5ad
feat: 문자열 분석 클래스 분리
SongJaeHoonn Oct 17, 2024
603ec68
Revert "feat: 문자열 분석 클래스 분리"
SongJaeHoonn Oct 17, 2024
cb34140
feat: 문자열 분석 클래스 분리
SongJaeHoonn Oct 17, 2024
591d269
feat: 커스텀 구분자 분리 메서드 구현
SongJaeHoonn Oct 17, 2024
e2c8dba
refactor: 조건문 로직 메서드 추출
SongJaeHoonn Oct 17, 2024
d3156b6
docs: 기능 요구사항 수정
SongJaeHoonn Oct 17, 2024
3f0334e
feat: 쉼표 및 세미콜론 또는 커스텀 구분자 검증 로직 구현
SongJaeHoonn Oct 17, 2024
025dc53
refactor: 구분자 인덱스를 enum으로 관리하도록 변경
SongJaeHoonn Oct 17, 2024
4fde90b
docs: 기능 요구사항 수정
SongJaeHoonn Oct 17, 2024
33f2615
docs: 구분자를 이용하여 문자열을 숫자만 있는 배열로 추출
SongJaeHoonn Oct 17, 2024
3572207
feat: 계산 클래스 분리
SongJaeHoonn Oct 17, 2024
093c1df
feat: 숫자 덧셈 구현
SongJaeHoonn Oct 17, 2024
2c0fd9f
refactor: 하드코딩 되어있던 기본 구분자를 enum 사용으로 변경
SongJaeHoonn Oct 17, 2024
2e0ee37
docs: 기능 요구사항 수정
SongJaeHoonn Oct 17, 2024
24ae6ef
refactor: 반환값을 primitive로 변경
SongJaeHoonn Oct 17, 2024
ff6e4ea
feat: 결과 반환
SongJaeHoonn Oct 17, 2024
76ea3d2
refactor: 구분자 적용식을 삭제한 후 검증하도록 변경
SongJaeHoonn Oct 18, 2024
439f1de
refactor: 커스텀 구분자를 적용할 때에만 구분자 적용식을 삭제하도록 변경
SongJaeHoonn Oct 18, 2024
7d2b2c8
refactor: 패키지 구조 변경
SongJaeHoonn Oct 19, 2024
e6f3e1a
docs: 기능 요구사항 수정
SongJaeHoonn Oct 19, 2024
841e578
refactor: Delimiter 싱글톤 패턴으로 변경
SongJaeHoonn Oct 19, 2024
72bd283
refactor: InputHandler 싱글톤 패턴으로 변경
SongJaeHoonn Oct 19, 2024
2cdc2a0
refactor: Calculation 싱글톤 패턴으로 변경
SongJaeHoonn Oct 19, 2024
9f42d0c
refactor: main 메서드에 집중된 로직 분산
SongJaeHoonn Oct 19, 2024
9d98f7f
docs: 기능 요구사항 완성
SongJaeHoonn Oct 19, 2024
9ba6493
feat: 테스트 코드 작성
SongJaeHoonn Oct 19, 2024
1255875
docs: 기능 요구사항 양식 수정
SongJaeHoonn Oct 19, 2024
a10af82
refactor: 이스케이프 문자 처리
SongJaeHoonn Oct 21, 2024
82a7a7a
refactor: 이스케이프 문자 재처리
SongJaeHoonn Oct 21, 2024
1f805a3
refactor: 빈 숫자 입력 시 0 반환
SongJaeHoonn Oct 21, 2024
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
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,41 @@
# java-calculator-precourse
# java-calculator-precourse

## 기능 요구사항
입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.

쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다. <br>

예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6 <br>

앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. <br>
커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다. <br>

예를 들어 `"//;\n1;2;3"`과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 `6`이 반환되어야 한다. <br>

사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다.

- 입력 : 구분자와 양수로 구성된 문자열 <br>
- 출력 : 덧셈 결과

### 라이브러리 <br>
`camp.nextstep.edu.missionutils`에서 제공하는 Console API를 사용하여 구현해야 한다. <br>
사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다.
## 기능 목록

- [x] 문자열 입력받기
- [x] 입력 클래스 분리
- [x] 문자열 입력 문구 출력
- [x] 라이브러리를 이용한 문자열 입력
- [x] 문자열 쉼표 및 세미콜론, 커스텀 구분자로 나누기
- [x] 문자열 분석 클래스 분리
- [x] 구분자 분리
- [x] 커스텀 구분자 분리
- [x] 입력값에서 쉼표 및 세미콜론 또는 커스텀 구분자만 사용되었는지 검증
- [x] 구분 인덱스 enum으로 관리
- [x] 커스텀 구분자 구문 삭제 로직 구현
- [x] 구분자를 이용하여 문자열을 숫자만 있는 배열로 추출
- [x] 숫자 덧셈
- [x] 계산 클래스 분리
- [x] 구분자를 비교하여 숫자 덧셈 구현
- [x] runner 구현
- [x] 결과 반환
7 changes: 6 additions & 1 deletion src/main/java/calculator/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package calculator;

import calculator.runner.CalculateRunner;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현

CalculateRunner runner = CalculateRunner.getInstance();

runner.run();
}
}
20 changes: 20 additions & 0 deletions src/main/java/calculator/calculation/Calculation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package calculator.calculation;

public class Calculation {

private static final Calculation INSTANCE = new Calculation();

private Calculation() {}

public static Calculation getInstance() {
return INSTANCE;
}

public int calculate(String[] numbers) {
int sum = 0;
for(String number : numbers) {
sum += Integer.parseInt(number);
}
return sum;
}
}
17 changes: 17 additions & 0 deletions src/main/java/calculator/global/DefaultDelimiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package calculator.global;

public enum DefaultDelimiter {

COMMA(","),
COLON(":");

private final String key;

DefaultDelimiter(String key) {
this.key = key;
}

public String getKey() {
return key;
}
}
19 changes: 19 additions & 0 deletions src/main/java/calculator/global/DelimiterSyntaxIndex.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package calculator.global;

public enum DelimiterSyntaxIndex {

DELIMITER_INDEX(2),
FIRST_END(3),
SECOND_END(4),
DELIMITER_AFTER(5);

private final int key;

DelimiterSyntaxIndex(int key) {
this.key = key;
}

public int getKey() {
return key;
}
}
23 changes: 23 additions & 0 deletions src/main/java/calculator/input/InputHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package calculator.input;

import camp.nextstep.edu.missionutils.Console;

public class InputHandler {

private static final InputHandler INSTANCE = new InputHandler();

private InputHandler() {}

public static InputHandler getInstance() {
return INSTANCE;
}

public String getInput() {
System.out.println("문자열을 입력해주세요");
return Console.readLine();
}

public void closeInput() {
Console.close();
}
}
46 changes: 46 additions & 0 deletions src/main/java/calculator/parser/Delimiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package calculator.parser;

import calculator.global.DefaultDelimiter;
import calculator.global.DelimiterSyntaxIndex;

public class Delimiter {

private static final Delimiter INSTANCE = new Delimiter();

private Delimiter() {}

public static Delimiter getInstance() {
return INSTANCE;
}

public String extractDelimiter(String input) {
if (validateStartsWith(input) && validateEndsWith(input)) {
String rawDelimiter = String.valueOf(input.charAt(DelimiterSyntaxIndex.DELIMITER_INDEX.getKey()));

return rawDelimiter.matches("[.\\^$|?*+(){}\\[\\]\\\\]") ? "\\" + rawDelimiter : rawDelimiter;
}
return "";
}

public String removeDelimiterSyntax(String input) {
if (validateStartsWith(input) && validateEndsWith(input)) {
return input.substring(DelimiterSyntaxIndex.DELIMITER_AFTER.getKey());
}
return input;
}

public boolean validateHasDefaultOrCustomDelimiter(String input, String customDelimiter) {
String delimiters = DefaultDelimiter.COLON.getKey() + DefaultDelimiter.COMMA.getKey() + customDelimiter;
String defaultSyntax = removeDelimiterSyntax(input);
return defaultSyntax.matches("[0-9" + delimiters + "]*");
}

private boolean validateStartsWith(String input) {
return input.startsWith("//");
}

private boolean validateEndsWith(String input) {
return input.charAt(DelimiterSyntaxIndex.FIRST_END.getKey()) == '\\'
&& input.charAt(DelimiterSyntaxIndex.SECOND_END.getKey()) == 'n';
}
}
37 changes: 37 additions & 0 deletions src/main/java/calculator/parser/InputParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package calculator.parser;

import calculator.global.DefaultDelimiter;

import java.util.Arrays;

public class InputParser {

private static final InputParser INSTANCE = new InputParser();
private final Delimiter delimiter;

private InputParser() {
this.delimiter = Delimiter.getInstance();
}

public static InputParser getInstance() {
return INSTANCE;
}

public String[] parse(String input) {
String customDelimiter = delimiter.extractDelimiter(input);
if (!delimiter.validateHasDefaultOrCustomDelimiter(input, customDelimiter)) {
throw new IllegalArgumentException("허용되지 않는 입력문입니다.");
}
String defaultSyntax = delimiter.removeDelimiterSyntax(input);
return extractNumbersFromDefaultSyntax(defaultSyntax, customDelimiter);
}

private String[] extractNumbersFromDefaultSyntax(String defaultSyntax, String customDelimiter) {
String delimiters = DefaultDelimiter.COMMA.getKey() + DefaultDelimiter.COLON.getKey() + customDelimiter;
String[] numbers = defaultSyntax.split("[" + delimiters + "]");

return Arrays.stream(numbers)
.filter(num -> !num.isEmpty())
.toArray(String[]::new);
}
}
34 changes: 34 additions & 0 deletions src/main/java/calculator/runner/CalculateRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package calculator.runner;

import calculator.calculation.Calculation;
import calculator.input.InputHandler;
import calculator.parser.InputParser;

public class CalculateRunner {

private static final CalculateRunner INSTANCE = new CalculateRunner();

public static CalculateRunner getInstance() {
return INSTANCE;
}

private final Calculation calculator;
private final InputParser parser;
private final InputHandler inputHandler;

private CalculateRunner() {
this.calculator = Calculation.getInstance();
this.parser = InputParser.getInstance();
this.inputHandler = InputHandler.getInstance();
}

public void run() {
String input = inputHandler.getInput();
String[] parseResult = parser.parse(input);
int result = calculator.calculate(parseResult);

System.out.println("Result: " + result);

inputHandler.closeInput();
}
}
48 changes: 48 additions & 0 deletions src/test/java/calculator/calculation/CalculationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package calculator.calculation;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class CalculationTest {

private final Calculation calculator = Calculation.getInstance();

@Test
@DisplayName("정상적인 숫자 배열로 계산")
void testCalculateWithValidNumbers() {
// Given
String[] numbers = {"1", "2", "3"};

// When
int result = calculator.calculate(numbers);

// Then
assertEquals(6, result);
}

@Test
@DisplayName("빈 배열 입력 시 0 반환")
void testCalculateWithEmptyArray() {
// Given
String[] numbers = {};

// When
int result = calculator.calculate(numbers);

// Then
assertEquals(0, result);
}

@Test
@DisplayName("숫자가 아닌 값이 포함되었을 때 예외 발생")
void testCalculateWithInvalidNumber() {
// Given
String[] numbers = {"1", "a", "3"};

// When & Then
assertThrows(IllegalArgumentException.class, () -> calculator.calculate(numbers));
}
}
60 changes: 60 additions & 0 deletions src/test/java/calculator/delimiter/DelimiterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package calculator.delimiter;

import calculator.parser.Delimiter;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class DelimiterTest {

private final Delimiter delimiter = Delimiter.getInstance();

@Test
@DisplayName("커스텀 구분자를 올바르게 추출")
void testExtractDelimiterWithCustomSyntax() {
// Given
String input = "//;\\n1;2;3";

// When
String result = delimiter.extractDelimiter(input);

// Then
assertEquals(";", result);
}

@Test
@DisplayName("구분자 구문 제거 테스트")
void testRemoveDelimiterSyntax() {
// Given
String input = "//;\\n1;2;3";

// When
String result = delimiter.removeDelimiterSyntax(input);

// Then
assertEquals("1;2;3", result);
}

@Test
@DisplayName("기본 또는 커스텀 구분자 유효성 테스트")
void testValidateHasDefaultOrCustomDelimiter() {
// Given
String input = "//;\\n1;2;3";

// When & Then
assertTrue(delimiter.validateHasDefaultOrCustomDelimiter(input, ";"));
}

@Test
@DisplayName("잘못된 구분자 입력 시 실패")
void testValidateInvalidDelimiter() {
// Given
String input = "//;\\n1,2;3";

// When & Then
assertFalse(delimiter.validateHasDefaultOrCustomDelimiter(input, "|"));
}
}
Loading