Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 46 additions & 1 deletion README.md
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

- 입력을 받는다
- 구분자가 있을 경우 추출한다
- 모두 더한다

## 구현할 기능 목록

- [ ] 입출력
- [ ] 구분자 추출
- [ ] 수식 구성
- [ ] 수식 계산
28 changes: 28 additions & 0 deletions src/main/java/calculator/AppConfig.java

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일은 왜 필요한건가요?? 당신의 지식을 쪽쪽 뽑아가고 싶습니다 친절히 알려주시면 정말 감사할것 같습니다 ^0^
@moongua404

Copy link
Copy Markdown
Contributor Author

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와 이에 필요한 의존성을 관리할 수 있게 됩니다.

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()
);
}
}
10 changes: 9 additions & 1 deletion src/main/java/calculator/Application.java
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();
}
}
26 changes: 26 additions & 0 deletions src/main/java/calculator/controller/CalculatorController.java

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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);
}
}
70 changes: 70 additions & 0 deletions src/main/java/calculator/model/Expression.java
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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static factory method 패턴 맞나요? 긴가민가해서.. 이 파일 코드도 다 깔끔하고 잘 보입니다~~

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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));
}
}
16 changes: 16 additions & 0 deletions src/main/java/calculator/utils/ExceptionConstants.java

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일은 왜 필요한건가요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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;
}
}
16 changes: 16 additions & 0 deletions src/main/java/calculator/utils/MessageConstants.java
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;
}
}
15 changes: 15 additions & 0 deletions src/main/java/calculator/utils/Validator.java
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);
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/calculator/view/InputView.java
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.");
}
}
}
9 changes: 9 additions & 0 deletions src/main/java/calculator/view/OutputView.java
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);
}
}
10 changes: 5 additions & 5 deletions src/test/java/calculator/ApplicationTest.java
Original file line number Diff line number Diff line change
@@ -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 커스텀_구분자_사용() {
Expand All @@ -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)
);
}

Expand Down
34 changes: 34 additions & 0 deletions src/test/java/calculator/ExpressionTest.java
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('\\');
});
}
}