diff --git a/README.md b/README.md index bd90ef0247..05ec7475ad 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# java-calculator-precourse \ No newline at end of file +# java-calculator-precourse + +## 구현할 기능 목록 +1. **입력 기능**: 문자열 입력 받기 +2. **구분자 처리 기능**: 기본 구분자(쉼표, 콜론)와 필요시 커스텀 구분자를 사용하여 숫자 분리 +3. **덧셈 기능**: 분리된 숫자들의 합 계산 +4. **출력 기능**: 계산 결과 출력 diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580fb40..7e0f9ac8d6 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,7 +1,32 @@ package calculator; +import java.util.Arrays; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + // 사용자 입력을 처리하는 객체 생성 + InputHandler inputHandler = new InputHandler(); + // 문자열 계산기를 처리하는 객체 생성 + StringCalculator calculator = new StringCalculator(); + // 메시지 출력을 처리하는 객체 생성 + ResultPrinter printer = new ResultPrinter(); + + // 문자열 계산기를 사용하여 합계 계산 + try { + // 사용자 입력 받기 Console.readLine() 사용 + String userInput = inputHandler.getInput(); + // "\n" 이스케이프 처리 + userInput = userInput.replace("\\n", "\n"); + + //덧셈 계산 + int result = calculator.calculate(userInput); + + // 결과 출력 + printer.printResult(result); + } catch (IllegalArgumentException e) { + // 오류 재던짐으로 인해 종료 + printer.printError(e.getMessage()); + throw e; + } } } diff --git a/src/main/java/calculator/DelimiterHandler.java b/src/main/java/calculator/DelimiterHandler.java new file mode 100644 index 0000000000..b43882446e --- /dev/null +++ b/src/main/java/calculator/DelimiterHandler.java @@ -0,0 +1,70 @@ +package calculator; + +import java.util.regex.Pattern; + +public class DelimiterHandler { + + private static final String DEFAULT_DELIMITER = ",:"; + private static final String CUSTOM_DELIMITER_PREFIX = "//"; + private static final String LINE_SEPARATOR = "\n"; + + + /** + * 구분자와 숫자 부분을 담는 레코드 클래스 + * + * @param delimiters 모든 구분자를 포함한 문자열 + * @param numbersPart 실제 계산할 숫자들이 포함된 부분 + */ + private record ParseResult(String delimiters, String numbersPart) { + } + + + /** + * 입력 문자열을 구분자와 숫자 부분으로 파싱 + * + * @param input 파싱할 입력 문자열 + * @return 구분자와 숫자 부분이 분리된 객체 + * @throws IllegalArgumentException 구분자 형식이 잘못된 경우 + */ + private ParseResult parseInput(String input) { + String delimiters = DEFAULT_DELIMITER; + String numbersPart = input; + + // 사용자 정의 구분자가 있는 경우 처리 + if (input.startsWith(CUSTOM_DELIMITER_PREFIX)) { + int newlineIndex = input.indexOf(LINE_SEPARATOR); + if (newlineIndex == -1) { + throw new IllegalArgumentException("잘못된 구분자 형식입니다."); + } + // 사용자 정의 구분자 추출 + String customDelimiter = input.substring(CUSTOM_DELIMITER_PREFIX.length(), newlineIndex); + // 사용자 정의 구분자와 기본 구분자 결합 + delimiters = customDelimiter + delimiters; + // 숫자 부분만 추출 + numbersPart = input.substring(newlineIndex + 1); + } + + return new ParseResult(delimiters, numbersPart); + } + + /** + * 입력 문자열에서 구분자를 파싱하고 숫자 부분을 분리하여 배열로 반환 + * @param input 처리할 입력 문자열 + * @return 구분자로 분리된 숫자 문자열 배열 + */ + public String[] parseAndSplitInput(String input) { + + // 구분자와 숫자 부분 분리 + ParseResult result = parseInput(input); + // 구분자 문자들을 OR 연산자(|)로 연결된 정규식으로 변환 + StringBuilder regexBuilder = new StringBuilder(); + for (char ch : result.delimiters.toCharArray()) { + // 특수문자를 정규식에서 사용할 수 있도록 이스케이프 처리 + regexBuilder.append(Pattern.quote(String.valueOf(ch))).append("|"); + } + // 마지막 | 문자 제거 + String regex = regexBuilder.substring(0, regexBuilder.length() - 1); + // 정규식 패턴으로 문자열 분할하여 반환 + return result.numbersPart.split(regex); + } +} diff --git a/src/main/java/calculator/InputHandler.java b/src/main/java/calculator/InputHandler.java new file mode 100644 index 0000000000..17021ef4dd --- /dev/null +++ b/src/main/java/calculator/InputHandler.java @@ -0,0 +1,17 @@ +package calculator; + +import camp.nextstep.edu.missionutils.Console; + +public class InputHandler { + // 문자열 입력 + private static final String PROMPT_MESSAGE = "덧셈할 문자열을 입력해 주세요."; + // 안내 메시지 출력 + private void printPrompt() { + System.out.println(PROMPT_MESSAGE); + } + public String getInput() { + printPrompt(); + return Console.readLine(); + } + +} diff --git a/src/main/java/calculator/ResultPrinter.java b/src/main/java/calculator/ResultPrinter.java new file mode 100644 index 0000000000..05c8fb3f0e --- /dev/null +++ b/src/main/java/calculator/ResultPrinter.java @@ -0,0 +1,16 @@ +package calculator; + +public class ResultPrinter { + private static final String RESULT_FORMAT = "결과 : %d"; + + // 계산 결과 출력 + public void printResult(int result) { + System.out.printf(RESULT_FORMAT + "\n", result); + } + + // 오류 메시지 출력 + public void printError(String message) { + System.out.println(message); + } + +} diff --git a/src/main/java/calculator/StringCalculator.java b/src/main/java/calculator/StringCalculator.java new file mode 100644 index 0000000000..b3b853c50d --- /dev/null +++ b/src/main/java/calculator/StringCalculator.java @@ -0,0 +1,52 @@ +package calculator; + +public class StringCalculator { + + private final DelimiterHandler delimiterHandler; + + //DelimiterHandler 객체 생성(커스텀/기본 구분자 처리를 위해) + public StringCalculator() { + this.delimiterHandler = new DelimiterHandler(); + } + + //입력 문자열을 받아 숫자를 합산 + public int calculate(String input) { + if (input == null || input.trim().isEmpty()) { + throw new IllegalArgumentException("Empty string cannot be calculated."); + } + + String[] numbers = delimiterHandler.parseAndSplitInput(input); + return sumNumbers(numbers); + } + + // 문자열 배열을 숫자로 변환하고 합산 + private int sumNumbers(String[] numbers) { + int sum = 0; + + for (String numStr : numbers) { + if (numStr.isEmpty()) continue; + + int number = parseToPositiveInteger(numStr); + sum += number; + } + + return sum; + } + + // 문자열을 양수 정수로 변환 + private int parseToPositiveInteger(String numStr) { + int number; + try { + number = Integer.parseInt(numStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("잘못된 숫자 형식: " + numStr); + } + + if (number < 0) { + throw new IllegalArgumentException("음수는 허용되지 않습니다: " + number); + } + + return number; + } + +} diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 93771fb011..ba036cdc73 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -22,8 +22,55 @@ class ApplicationTest extends NsTest { assertThatThrownBy(() -> runException("-1,2,3")) .isInstanceOf(IllegalArgumentException.class) ); + }@Test + void 기본_구분자_테스트() { + assertSimpleTest(() -> { + run("1,2:3"); + assertThat(output()).contains("결과 : 6"); + }); + } + + @Test + void 빈_문자열_테스트() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 숫자형식_오류_테스트() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("1,2,a")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 잘못된_구분자_형식_테스트() { + assertSimpleTest(() -> + assertThatThrownBy(() -> runException("//;1,2,3")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 복합_구분자_사용_테스트() { + assertSimpleTest(() -> { + run("1,2:3"); + assertThat(output()).contains("결과 : 6"); + }); } + @Test + void 커스텀_구분자와_기본구분자_혼합_테스트() { + assertSimpleTest(() -> { + run("//;\\n1;2,3:4"); + assertThat(output()).contains("결과 : 10"); + }); + } + + @Override public void runMain() { Application.main(new String[]{});