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
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# java-calculator-precourse
![문제 원문1](calculator-description-1.png)
![문제 원문2](calculator-description-2.png)
## 구현 기능 목록

### 입력 문자열 처리

- 빈 문자열인 경우
- [x] null 또는 빈 문자열("") 또는 1개 이상의 공백 입력 시 0을 반환

- 커스텀 구분자를 지정한 경우
- [x] 커스텀 구분자가 `//`로 시작해 `\n`가 포함되는지 확인
- [x] 입력 문자열에서 커스텀 구분자를 추출
- 1개 이상의 구분자이며, 문자와 숫자 모두 가능하다.
- [x] 추출한 커스텀 구분자를 기본 구분자 목록에 추가
- [x] 커스텀 구분자 지정 패턴(\n으로 끝나는 문자열) 이후의 문자열 추출

- 공통
- [x] 구분자로 입력받은 문자열을 분리한다.
- 계산기에서 모든 예외를 처리 하므로 일단 구분자로 모든 문자열을 분리한다.

### 계산기

- [x] 분리한 문자열을 양수 값으로 변환해 더해준다.
- [x] 양수 값 이외의 값이 배열에 들어있는 경우 예외 발생
- [x] 합계가 정수 최댓값을 초과하는 경우 예외 발생
- [x] 추출한 숫자의 합 반환

### 리펙토링
- [x] CustomController 클래스를 내부클래스로 변경, static 메서드를 인스턴스 메서드로 변경
- [x] 예외 발생 시 IllegalArgumentException(e)를 던져 stacktrace에 에러메시지 출력
- [x] 변수 이름 변경 VALID_CUSTOM_INPUT -> CUSTOM_DELIMITER_PATTERN
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 camp.nextstep.edu.missionutils.Console;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
System.out.println("덧셈할 문자열을 입력헤주세요.");
String input = Console.readLine();
Copy link
Member

Choose a reason for hiding this comment

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

input도 따로 인스턴스로 빼서 만들어주시면 좋을거 같아요!

Copy link
Member

Choose a reason for hiding this comment

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

output도 동일!!!

Copy link
Member

Choose a reason for hiding this comment

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

전체적으로 구조를 나눠서 하시면 좋을거 같습니다! 우테코 프리코스 합격비법이 출력 정답도 중요하지만 코드 구조를 엄청 세심하게 평가한다고 해서 되도록 이면 OOP 특징을 잘활용해서 조립식으로 쪼개야 될거 같네요!! 고생하셨어요!

Copy link
Author

Choose a reason for hiding this comment

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

프로그램을 패키지별로 나누는 방법을 배우게 되는 계기가 되었네요 ㅎㅎ


Calculator calculator = new Calculator();
int result = calculator.calculate(input);
System.out.println("결과 : " + result);
}
}
}
73 changes: 73 additions & 0 deletions src/main/java/calculator/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package calculator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Calculator {
private Delimiter delimiter;
private String[] numbers;
private int result;

public int calculate(String input) {
if (input == null || input.isBlank()) {
return 0;
}

CustomController customController = new CustomController();
input = customController.getNewInputIfCustom(input);

if (delimiter == null) {
delimiter = new Delimiter();
}

numbers = delimiter.split(input);
calculateResult();

return result;
}

private void calculateResult() {
for (String number : numbers) {
// 만약 s가 숫자(양수)가 아니면 IllegalArgumentException 예외 발생
// 오버플로우 발생 시 예외 발생
try {
result = safeAdd(result, Integer.parseUnsignedInt(number));
} catch (RuntimeException e) {
throw new IllegalArgumentException(e);
}
}
}

private int safeAdd(int result, int number) {
if (result > Integer.MAX_VALUE - number) {
throw new ArithmeticException("Overflow");
}
return result + number;
}

private class CustomController {
Copy link
Member

Choose a reason for hiding this comment

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

이건 따로 클래스 만들어서 해주면 좋을거 같아요!

private static final String CUSTOM_DELIMITER_PATTERN = "^//(.*)\\\\n(.*)";
private static final int CUSTOM_DELIMITER_GROUP = 1;
private static final int CUSTOM_PURE_INPUT_GROUP = 2;
Copy link
Member

Choose a reason for hiding this comment

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

그룹이 두 개로만 나뉘어서 customDelimiter=group(1) / customPureInput=group(2) 처럼 변수로 구분해도 될 것 같은데,
이 부분은 상수화 하신 이유가 궁금합니다!

Copy link
Author

Choose a reason for hiding this comment

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

극한으로 하드코딩을 피하고자 이렇게 구현했습니다!
이번에 코드를 짜면서 과연 어디까지가 하드코딩인지 궁금해졌습니다ㅎㅎ
지영님은 어느정도까지가 하드코딩이라고 생각하시나요?


private String getNewInputIfCustom(String input) {
Pattern pattern = Pattern.compile(CUSTOM_DELIMITER_PATTERN);
Matcher matcher = pattern.matcher(input);

if (matcher.matches()) {
String customDelimiter = extractCustomDelimiter(matcher);
delimiter = new Delimiter(customDelimiter);
input = extractPureInput(matcher);
}
return input;
}

private String extractCustomDelimiter(Matcher matcher) {
return matcher.group(CUSTOM_DELIMITER_GROUP);
}

private String extractPureInput(Matcher matcher) {
return matcher.group(CUSTOM_PURE_INPUT_GROUP);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/calculator/Delimiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package calculator;

public class Delimiter {
private final String delimiter;

public Delimiter() {
delimiter = ",:";
}

public Delimiter(String customDelimiter) {
this.delimiter = ",:" + customDelimiter;
}
Copy link
Member

Choose a reason for hiding this comment

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

생성자 오버로딩을 이용해서, 객체를 다르게 생성할 수도 있군요. 사용자 지정 구분자가 있는지 파악하는 로직 대신 생성자 오버로딩을 사용하면, 코드도 간결해져서 좋은 것 같습니다!

Copy link
Member

Choose a reason for hiding this comment

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

메타문자들은 Pattern.quote()로 감싸주면 좋을 것 같습니다!


public String[] split(String input) {
return input.split("[" + delimiter + "]");
}
}
8 changes: 8 additions & 0 deletions src/test/java/calculator/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ApplicationTest extends NsTest {
@Test
void 기본_구분자_사용() {
assertSimpleTest(() -> {
run("1:2,3");
assertThat(output()).contains("결과 : 6");
});
}

@Test
void 커스텀_구분자_사용() {
assertSimpleTest(() -> {
Expand Down