-
Notifications
You must be signed in to change notification settings - Fork 9
[문자열 계산기] 박호건 미션 제출합니다. #4
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
base: main
Are you sure you want to change the base?
Changes from all commits
da7800f
e20b19c
49309e8
5a2e45d
b02eec9
aa7503d
d80f0e0
5e30aca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,46 @@ | ||
| # java-calculator-precourse | ||
| # java-calculator-precourse | ||
|
|
||
| 다시 시작하는 우테코 프리코스의 꿈...이랄까..... | ||
|
|
||
| <details> | ||
| <summary>과제 세부 내용</summary> | ||
|
|
||
| ## 과제 | ||
|
|
||
| 입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다. | ||
|
|
||
| - 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다. | ||
| - 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6 | ||
| - 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다. | ||
| - 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다. | ||
| - 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다. | ||
|
|
||
| ### 입출력 | ||
|
|
||
| - 입력 | ||
| - 구분자와 양수로 구성된 문자열 | ||
| - 출력 | ||
| - 덧셈 결과 | ||
|
|
||
| ex) | ||
|
|
||
| ``` | ||
| 덧셈할 문자열을 입력해 주세요. | ||
| 1,2:3 | ||
| 결과 : 6 | ||
| ``` | ||
|
|
||
| </details> | ||
|
|
||
| ## Business Logic | ||
|
|
||
| - 입력을 받는다 | ||
| - 구분자가 있을 경우 추출한다 | ||
| - 모두 더한다 | ||
|
|
||
| ## 구현할 기능 목록 | ||
|
|
||
| - [ ] 입출력 | ||
| - [ ] 구분자 추출 | ||
| - [ ] 수식 구성 | ||
| - [ ] 수식 계산 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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() | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
| } |
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 이런식으로 디렉터리 설계 커밋을 하기도 하는군요 배워갑니다~~^^ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Character> operators; | ||
| private String expression; | ||
|
|
||
| private static final List<Character> 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(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Static factory method 패턴 맞나요? 긴가민가해서.. 이 파일 코드도 다 깔끔하고 잘 보입니다~~
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 정적 팩터리 메서드로 생각하고 구현했습니다! |
||
|
|
||
| line = line.trim(); | ||
| line = expression.extractOperators(line); | ||
| expression.expression = line; | ||
|
|
||
| return expression; | ||
| } | ||
|
|
||
| public HashSet<Character> getOperators() { | ||
| return operators; | ||
| } | ||
|
|
||
| public String getExpression() { | ||
| return expression; | ||
| } | ||
|
|
||
| public int calculate() { | ||
| List<String> 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)); | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 파일은 왜 필요한건가요?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 잘못 만든 것 같네요....ㅋㅋㅋㅋ 예외가 발생할 상황이 많을 경우 Enum으로 관리하려고 했는데 지우는걸 깜빡했습니다... |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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."); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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('\\'); | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 파일은 왜 필요한건가요?? 당신의 지식을 쪽쪽 뽑아가고 싶습니다 친절히 알려주시면 정말 감사할것 같습니다 ^0^
@moongua404
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제 지식이라기에는... 그냥 저도 이전 스터디에서 다른 사람이 잘 짜 보이는거 비슷하게 짠거긴 합니다...ㅋㅋㅋ
의존성을 통합적으로 관리하기 위함입니다. 이 파일을 만들지 않으면 Conroller와 이에 필요한 의존성(Service, View ...)을 Application에서 직접 생성한 후 이에 대한 의존성을 주입해서 사용해야하는데, AppConfig 파일을 만든 후 여기서 의존성을 만들고 반환하게 된다면 Application에서는 AppConfig 만으로 Controller와 이에 필요한 의존성을 관리할 수 있게 됩니다.