diff --git a/README.md b/README.md index bd90ef0247..cadd33b4f1 100644 --- a/README.md +++ b/README.md @@ -1 +1,58 @@ -# java-calculator-precourse \ No newline at end of file +# java-calculator-precourse + +# Project Directory + calculator + ┣ config + ┃ ┗ Configuration.java + ┣ constant + ┃ ┗ CalConst.java + ┣ controller + ┃ ┗ CalculatorController.java + ┣ dto + ┃ ┗ CheckSeparatorDto.java + ┣ service + ┃ ┣ separator + ┃ ┃ ┗ Separator.java + ┃ ┣ validator + ┃ ┃ ┗ Validator.java + ┃ ┗ CalculatorService.java + ┣ view + ┃ ┣ InputView.java + ┃ ┗ OutputView.java + ┗ Application.java + +## 구현할 기능 목록 + + 1. 문자열을 입력 받는 기능 + 2. 문자열에 추가 구분자 입력이 있는지 확인하는 기능 + 3. 문자열을 구분자를 기준으로 나누는 기능 + 4. 구분자로 나눠진 문자열에 숫자 외의 다른 문자가 있는지 확인하는 기능 있으면 IllegalArgumentException + 5. 나눠진 숫자를 더하는 기능 + +## 테스트 필요 하다 생각 되는 것들 + +- [x] 문자열을 아무 것도 주지 않았을 때 +- [x] 추가 구분자로 숫자를 줬을 때 +- [x] 소수 넣어 보기(추가 구분자로 .을 줬을 때와 아닐 때) +- [x] 역슬래시(이스케이프 문자를 구분자로 줬을 때) +- [x] 구분자 사이에 숫자가 없을 때 + +## 확인 필요 + +### 1. 커스텀 구분자가 여러 개라면? + 현재 만들어진 로직은 커스텀 구분자가 한 개인 경우만을 받고 있으나 그렇지 않을 수도 있음. + 만약에 //와 \n 사이에 두 개가 들어가면 그건 따로 떨어뜨려서 구분을 해야하나? + //ea\n1ea2ea3ea 이 주어지면 ea로 묶어서 구분자로 사용해야 하나? + +### 2. 커스텀 구분자에 숫자가 들어온다면? + 당연히 계산기라면 커스텀 구분자에 숫자가 들어오면 안되긴 함. + 커스텀 구분자에 숫자가 들어오는 경우 IllegalArgumentException 해야하는가? + 아니면 구분자로 인식하고 계산시켜야 하는가? + 지금은 계산하도록 만들어둠. + +### 3. 아무 입력도 하지 않았다면? + 입력이 없으면 0을 출력하도록 했음. 근데 IllegalArgumentException 해야하는지? + +### 4. 구분자 사이에 숫자가 없다면? + 현재는 사이에 숫자가 없다면 0으로 인식하고 계산을 진행하도록 하였음. + 이것도 throw 해야하는 경우인가? \ No newline at end of file diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580fb40..88ba3001de 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -1,7 +1,13 @@ package calculator; +import calculator.config.Configuration; +import calculator.controller.CalculatorController; + public class Application { + private static final Configuration CONFIGURATION = Configuration.getInstance(); + public static void main(String[] args) { - // TODO: 프로그램 구현 + CalculatorController calculatorController = CONFIGURATION.getCalculatorController(); + calculatorController.input(); } } diff --git a/src/main/java/calculator/config/Configuration.java b/src/main/java/calculator/config/Configuration.java new file mode 100644 index 0000000000..2a9e172937 --- /dev/null +++ b/src/main/java/calculator/config/Configuration.java @@ -0,0 +1,19 @@ +package calculator.config; + +import calculator.controller.CalculatorController; + +public class Configuration { + private static final Configuration CONFIGURATION = new Configuration(); + private final CalculatorController calculatorController = new CalculatorController(); + + private Configuration() { + } + + public static Configuration getInstance() { + return CONFIGURATION; + } + + public CalculatorController getCalculatorController() { + return calculatorController; + } +} diff --git a/src/main/java/calculator/constant/CalConst.java b/src/main/java/calculator/constant/CalConst.java new file mode 100644 index 0000000000..aa667dc854 --- /dev/null +++ b/src/main/java/calculator/constant/CalConst.java @@ -0,0 +1,19 @@ +package calculator.constant; + +public enum CalConst { + MINIMUM_LENGTH_IF_EXIST_CUSTOM_SEPARATOR(5), + START_INDEX_OF_CALCULATING(5), + END_INDEX_OF_CUSTOM_SEPARATOR(3), + INDEX_OF_CUSTOM_SEPARATOR(2), + ; + + private final int value; + + CalConst(int value) { + this.value = value; + } + + public int value() { + return value; + } +} diff --git a/src/main/java/calculator/controller/CalculatorController.java b/src/main/java/calculator/controller/CalculatorController.java new file mode 100644 index 0000000000..202f34b428 --- /dev/null +++ b/src/main/java/calculator/controller/CalculatorController.java @@ -0,0 +1,29 @@ +package calculator.controller; + +import calculator.service.CalculatorService; +import calculator.view.InputView; +import calculator.view.OutputView; + +public class CalculatorController { + private final CalculatorService calculatorService; + private final InputView inputView; + private final OutputView outputView; + + public CalculatorController() { + this.inputView = new InputView(); + this.outputView = new OutputView(); + this.calculatorService = new CalculatorService(); + } + + public void input() { + String s = inputView.inputString(); + inputView.close(); + int addedResult = calculatorService.add(s); + output(addedResult); + } + + private void output(int i) { + outputView.outputInteger(i); + } + +} diff --git a/src/main/java/calculator/dto/CheckSeparatorDto.java b/src/main/java/calculator/dto/CheckSeparatorDto.java new file mode 100644 index 0000000000..532693dda6 --- /dev/null +++ b/src/main/java/calculator/dto/CheckSeparatorDto.java @@ -0,0 +1,22 @@ +package calculator.dto; + +public class CheckSeparatorDto { + private String inputString; + private String customSeparator = null; + + public String getInputString() { + return inputString; + } + + public void setInputString(String inputString) { + this.inputString = inputString; + } + + public String getCustomSeparator() { + return customSeparator; + } + + public void setCustomSeparator(String customSeparator) { + this.customSeparator = customSeparator; + } +} diff --git a/src/main/java/calculator/service/CalculatorService.java b/src/main/java/calculator/service/CalculatorService.java new file mode 100644 index 0000000000..9fa2344e99 --- /dev/null +++ b/src/main/java/calculator/service/CalculatorService.java @@ -0,0 +1,40 @@ +package calculator.service; + +import calculator.dto.CheckSeparatorDto; +import calculator.service.separator.Separator; +import calculator.service.validator.Validator; + +public class CalculatorService { + private final Validator validator = new Validator(); + private final Separator separator = new Separator(); + + + /** + * 덧셈 로직을 순차적으로 진행한다. + */ + public int add(String inputString) { + CheckSeparatorDto checkSeparatorDto = new CheckSeparatorDto(); + checkSeparatorDto.setInputString(inputString); + separator.checkCustomSeparator(checkSeparatorDto); + String[] separatedString = separator.separate(checkSeparatorDto); + + validator.hasNaN(separatedString); + return sumSeparatedStringArr(separatedString); + } + + /** + * 문자열을 int 형으로 바꾼 뒤 합한다. + * + * @param separatedStringArr 숫자로만 이루어진 문자열 배열을 입력한다. + * @return 합한다. + */ + private int sumSeparatedStringArr(String[] separatedStringArr) { + int sum = 0; + for (String separatedString : separatedStringArr) { + if (!separatedString.isEmpty()) { + sum += Integer.parseInt(separatedString); + } + } + return sum; + } +} diff --git a/src/main/java/calculator/service/separator/Separator.java b/src/main/java/calculator/service/separator/Separator.java new file mode 100644 index 0000000000..ae6f1203e2 --- /dev/null +++ b/src/main/java/calculator/service/separator/Separator.java @@ -0,0 +1,47 @@ +package calculator.service.separator; + +import calculator.constant.CalConst; +import calculator.dto.CheckSeparatorDto; + +public class Separator { + + /** + * 커스텀 구분자가 있는지 확인한다. 만약 있다면, checkSeparatorDto.customSeparator 에 null 대신 커스텀 구분자를 넣는다. 또한, + * checkSeparatorDto.inputString 에 커스텀 구분자를 확인하는 부분을 제거한 문자열을 넣는다. + * + * @param checkSeparatorDto inputString 만 set 되어 있는 dto. + */ + public void checkCustomSeparator(CheckSeparatorDto checkSeparatorDto) { + String inputString = checkSeparatorDto.getInputString(); + String customSeparator; + + if (inputString.length() >= CalConst.MINIMUM_LENGTH_IF_EXIST_CUSTOM_SEPARATOR.value() + && inputString.startsWith("//") + && inputString.startsWith("\\n", CalConst.END_INDEX_OF_CUSTOM_SEPARATOR.value())) { + customSeparator = String.valueOf(inputString.charAt(CalConst.INDEX_OF_CUSTOM_SEPARATOR.value())); + inputString = inputString.substring(CalConst.START_INDEX_OF_CALCULATING.value()); + + checkSeparatorDto.setCustomSeparator(customSeparator); + checkSeparatorDto.setInputString(inputString); + } + } + + /** + * 문자열을 구분자를 이용해 나눈다. + * + * @param checkSeparatorDto 커스텀 구분자(or null)와 커스텀 구분자 확인 부분을 제거한 문자열을 넣은 Dto. + * @return 구분자를 이용해 나눠진 문자열을 반환한다. 이 문자열에는 숫자 외의 문자가 존재할 수 있다. 계산에 이용하려면 추가적인 확인 과정이 필요하다. + */ + public String[] separate(CheckSeparatorDto checkSeparatorDto) { + String customSeparator = checkSeparatorDto.getCustomSeparator(); + String inputString = checkSeparatorDto.getInputString(); + + if (customSeparator == null) { + return inputString.split("[:,]"); + } + if (customSeparator.equals("\\")) { + return inputString.split("[:," + customSeparator.repeat(2) + "]"); + } + return inputString.split("[:," + customSeparator + "]"); + } +} diff --git a/src/main/java/calculator/service/validator/Validator.java b/src/main/java/calculator/service/validator/Validator.java new file mode 100644 index 0000000000..82fbc01ba3 --- /dev/null +++ b/src/main/java/calculator/service/validator/Validator.java @@ -0,0 +1,19 @@ +package calculator.service.validator; + +import java.util.Arrays; + +public class Validator { + + /** + * 숫자가 아닌 문자열이 있는지 확인한다. 만약 있다면 IllegalArgumentException 으로 처리한다. + * + * @param separatedStringArr 숫자 외의 문자가 있는지 확인하고 싶은 문자열을 입력한다. + */ + public void hasNaN(String[] separatedStringArr) { + if (Arrays.stream(separatedStringArr) + .flatMapToInt(String::chars) + .anyMatch(it -> it < '0' || it > '9')) { + throw new IllegalArgumentException("입력한 문자열에 구분자, 숫자 외의 문자가 존재합니다."); + } + } +} diff --git a/src/main/java/calculator/view/InputView.java b/src/main/java/calculator/view/InputView.java new file mode 100644 index 0000000000..1032659f10 --- /dev/null +++ b/src/main/java/calculator/view/InputView.java @@ -0,0 +1,14 @@ +package calculator.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + public String inputString() { + System.out.println("덧셈할 문자열을 입력해주세요."); + return Console.readLine(); + } + + public void close() { + Console.close(); + } +} diff --git a/src/main/java/calculator/view/OutputView.java b/src/main/java/calculator/view/OutputView.java new file mode 100644 index 0000000000..1dfd3c9b5d --- /dev/null +++ b/src/main/java/calculator/view/OutputView.java @@ -0,0 +1,7 @@ +package calculator.view; + +public class OutputView { + public void outputInteger(int i) { + System.out.println("결과 : " + i); + } +} diff --git a/src/test/java/calculator/ApplicationTest.java b/src/test/java/calculator/ApplicationTest.java index 93771fb011..0ad411b78d 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) ); } @@ -28,4 +28,66 @@ class ApplicationTest extends NsTest { public void runMain() { Application.main(new String[]{}); } + + @Test + public void 숫자_구분자_사용() { + assertSimpleTest(() -> { + run("//1\\n2:213"); + assertThat(output()).contains("결과 : 7"); + }); + } + + @Test + public void 탈출_문자_사용() { + assertSimpleTest(() -> { + assertThatThrownBy(() -> runException("1\\2:3")) + .isInstanceOf(IllegalArgumentException.class); + }); + } + + @Test + public void 탈출_문자_구분자_사용() { + assertSimpleTest(() -> { + run("//\\\\n1\\2:3,4"); + assertThat(output()).contains("결과 : 10"); + }); + } + + @Test + public void 마침표_구분자_사용() { + assertSimpleTest(() -> { + run("//.\\n1.2.4:3"); + assertThat(output()).contains("결과 : 10"); + }); + } + + @Test + public void 마침표_사용(){ + assertThatThrownBy(() -> runException("1.2.3:4")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void 따옴표_구분자_사용() { + assertSimpleTest(() -> { + run("//\"\\n1\"2\"4:3"); + assertThat(output()).contains("결과 : 10"); + }); + } + + @Test + public void 구분자_사이에_문자열_없음() { + assertSimpleTest(() -> { + run("1,:2"); + assertThat(output()).contains("결과 : 3"); + }); + } + + @Test + public void 문자열_없음(){ + assertSimpleTest(() -> { + run("\n"); + assertThat(output()).contains("결과 : 0"); + }); + } }