diff --git a/README.md b/README.md index bd90ef0..71a20c9 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ -# java-calculator-precourse \ No newline at end of file +# java-calculator-precourse + +다시 시작하는 우테코 프리코스의 꿈...이랄까..... + +
+ 과제 세부 내용 + +## 과제 + +입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다. + +- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다. + - 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6 +- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다. + - 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다. +- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다. + +### 입출력 + +- 입력 + - 구분자와 양수로 구성된 문자열 +- 출력 + - 덧셈 결과 + +ex) + +``` +덧셈할 문자열을 입력해 주세요. +1,2:3 +결과 : 6 +``` + +
+ +## Business Logic + +- 입력을 받는다 +- 구분자가 있을 경우 추출한다 +- 모두 더한다 + +## 구현할 기능 목록 + +- [ ] 입출력 +- [ ] 구분자 추출 +- [ ] 수식 구성 +- [ ] 수식 계산 diff --git a/src/main/java/calculator/AppConfig.java b/src/main/java/calculator/AppConfig.java new file mode 100644 index 0000000..40692a5 --- /dev/null +++ b/src/main/java/calculator/AppConfig.java @@ -0,0 +1,28 @@ +package calculator; + +import calculator.controller.CalculatorController; +import calculator.model.Expression; +import calculator.view.InputView; +import calculator.view.OutputView; + +public class AppConfig { + public InputView inputView() { + return new InputView(); + } + + public OutputView outputView() { + return new OutputView(); + } + + public Expression expression() { + return new Expression(); + } + + public CalculatorController calculatorController() { + return new CalculatorController( + this.inputView(), + this.outputView(), + this.expression() + ); + } +} diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580f..f9e2f88 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,7 +1,15 @@ package calculator; +import calculator.controller.CalculatorController; + public class Application { + static AppConfig appConfig; + static CalculatorController calculatorController; + public static void main(String[] args) { - // TODO: 프로그램 구현 + appConfig = new AppConfig(); + calculatorController = appConfig.calculatorController(); + + calculatorController.run(); } } diff --git a/src/main/java/calculator/controller/CalculatorController.java b/src/main/java/calculator/controller/CalculatorController.java new file mode 100644 index 0000000..075fccd --- /dev/null +++ b/src/main/java/calculator/controller/CalculatorController.java @@ -0,0 +1,26 @@ +package calculator.controller; + +import calculator.model.Expression; +import calculator.view.InputView; +import calculator.view.OutputView; + +public class CalculatorController { + InputView inputView; + OutputView outputView; + Expression expression; + + public CalculatorController( + InputView inputView, OutputView outputView, Expression expression + ) { + this.inputView = inputView; + this.outputView = outputView; + this.expression = expression; + } + + public void run() { + String line = inputView.getExpression(); + expression = Expression.parse(line); + int result = expression.calculate(); + outputView.printResult(result); + } +} diff --git a/src/main/java/calculator/model/Expression.java b/src/main/java/calculator/model/Expression.java new file mode 100644 index 0000000..2a22aee --- /dev/null +++ b/src/main/java/calculator/model/Expression.java @@ -0,0 +1,70 @@ +package calculator.model; + +import calculator.utils.ExceptionConstants; +import calculator.utils.Validator; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class Expression { + private HashSet operators; + private String expression; + + private static final List DEFAULT_SEPARATOR = Arrays.asList(':', ','); + private static final String OR_DELIMITER = "|"; + private static final String CUSTOM_SEPARATOR_REGEX = "^//(.)\\\\n(.*)"; + + private static final int CUSTOM_SEPARATOR_INDEX = 1; + private static final int EXPRESSION_INDEX = 2; + + public static Expression parse(String line) { + Expression expression = new Expression(); + + line = line.trim(); + line = expression.extractOperators(line); + expression.expression = line; + + return expression; + } + + public HashSet getOperators() { + return operators; + } + + public String getExpression() { + return expression; + } + + public int calculate() { + List results = Arrays.stream(expression.split(getOperatorsRegex())).toList(); + results.forEach(Validator::validatePositiveInt); + try { + return results.stream() + .mapToInt(Integer::parseInt) + .reduce(0, Math::addExact); + } catch (Exception exception) { + throw new IllegalArgumentException( + ExceptionConstants.INVALID_EXPRESSION.getMessage() + expression, exception); + } + } + + private String extractOperators(String line) { + this.operators = new HashSet<>(); + this.operators.addAll(DEFAULT_SEPARATOR); + Matcher matcher = Pattern.compile(CUSTOM_SEPARATOR_REGEX).matcher(line); + if (matcher.find()) { + operators.add(matcher.group(CUSTOM_SEPARATOR_INDEX).charAt(0)); + return matcher.group(EXPRESSION_INDEX); + } + return line; + } + + private String getOperatorsRegex() { + return operators.stream() + .map((ch) -> Pattern.quote(ch.toString())) // \같은 문자 처리 + .collect(Collectors.joining(OR_DELIMITER)); + } +} \ No newline at end of file diff --git a/src/main/java/calculator/utils/ExceptionConstants.java b/src/main/java/calculator/utils/ExceptionConstants.java new file mode 100644 index 0000000..6773e42 --- /dev/null +++ b/src/main/java/calculator/utils/ExceptionConstants.java @@ -0,0 +1,16 @@ +package calculator.utils; + +public enum ExceptionConstants { + INVALID_EXPRESSION("덧셈할 문자열을 입력해 주세요."), + INVALID_NUMBER_COMPOSITION("결과 : %d"); + + private final String message; + + ExceptionConstants(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } +} diff --git a/src/main/java/calculator/utils/MessageConstants.java b/src/main/java/calculator/utils/MessageConstants.java new file mode 100644 index 0000000..25d5e9e --- /dev/null +++ b/src/main/java/calculator/utils/MessageConstants.java @@ -0,0 +1,16 @@ +package calculator.utils; + +public enum MessageConstants { + GUIDE_MESSAGE("덧셈할 문자열을 입력해 주세요."), + RESULT_MESSAGE("결과 : %d"); + + private final String message; + + MessageConstants(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } +} diff --git a/src/main/java/calculator/utils/Validator.java b/src/main/java/calculator/utils/Validator.java new file mode 100644 index 0000000..73e1217 --- /dev/null +++ b/src/main/java/calculator/utils/Validator.java @@ -0,0 +1,15 @@ +package calculator.utils; + +public class Validator { + + + public static void validatePositiveInt(String number) { + try { + if (Integer.parseInt(number) < 0) { + throw new NumberFormatException(); + } + } catch (Exception exception) { + throw new IllegalArgumentException(ExceptionConstants.INVALID_NUMBER_COMPOSITION.getMessage() + number); + } + } +} diff --git a/src/main/java/calculator/view/InputView.java b/src/main/java/calculator/view/InputView.java new file mode 100644 index 0000000..0d5b7ae --- /dev/null +++ b/src/main/java/calculator/view/InputView.java @@ -0,0 +1,15 @@ +package calculator.view; + +import calculator.utils.MessageConstants; +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + public String getExpression() { + try { + System.out.println(MessageConstants.GUIDE_MESSAGE.getMessage()); + return Console.readLine(); + } catch (Exception exception) { + throw new IllegalArgumentException("Something went wrong on input process."); + } + } +} diff --git a/src/main/java/calculator/view/OutputView.java b/src/main/java/calculator/view/OutputView.java new file mode 100644 index 0000000..94df46b --- /dev/null +++ b/src/main/java/calculator/view/OutputView.java @@ -0,0 +1,9 @@ +package calculator.view; + +import calculator.utils.MessageConstants; + +public class OutputView { + public void printResult(int v) { + System.out.printf((MessageConstants.RESULT_MESSAGE.getMessage()) + "%n", v); + } +} diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 93771fb..1b2db04 100644 --- a/src/test/java/calculator/ApplicationTest.java +++ b/src/test/java/calculator/ApplicationTest.java @@ -1,12 +1,12 @@ package calculator; -import camp.nextstep.edu.missionutils.test.NsTest; -import org.junit.jupiter.api.Test; - import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import camp.nextstep.edu.missionutils.test.NsTest; +import org.junit.jupiter.api.Test; + class ApplicationTest extends NsTest { @Test void 커스텀_구분자_사용() { @@ -19,8 +19,8 @@ class ApplicationTest extends NsTest { @Test void 예외_테스트() { assertSimpleTest(() -> - assertThatThrownBy(() -> runException("-1,2,3")) - .isInstanceOf(IllegalArgumentException.class) + assertThatThrownBy(() -> runException("-1,2,3")) + .isInstanceOf(IllegalArgumentException.class) ); } diff --git a/src/test/java/calculator/ExpressionTest.java b/src/test/java/calculator/ExpressionTest.java new file mode 100644 index 0000000..c0d8a05 --- /dev/null +++ b/src/test/java/calculator/ExpressionTest.java @@ -0,0 +1,34 @@ +package calculator; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; + +import calculator.model.Expression; +import org.junit.jupiter.api.Test; + +public class ExpressionTest { + @Test + void Extract_Separator() { + assertSimpleTest(() -> { + Expression expression = Expression.parse("//*\\n1*2*3"); + assertThat(expression.getOperators()).contains('*'); + assertThat(expression.getExpression()).isEqualTo("1*2*3"); + }); + } + + @Test + void No_Separator() { + assertSimpleTest(() -> { + Expression expression = Expression.parse("1,2,3"); + assertThat(expression.getExpression()).isEqualTo("1,2,3"); + }); + } + + @Test + void Special_Separator() { + assertSimpleTest(() -> { + Expression expression = Expression.parse("//\\\\n1*2*3"); + assertThat(expression.getOperators()).contains('\\'); + }); + } +}