Skip to content
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

[문자열 덧셈 계산기] 민경태 미션 제출합니다. #1914

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e81bf33
docs: 기능 구현 목록 작성
GO-TE Oct 16, 2024
2047172
feat(Input): 유저 입력 받는 기능 구현
GO-TE Oct 16, 2024
d6b8224
feat(Validator): 커스텀 구분자의 포함 확인 기능 구현
GO-TE Oct 16, 2024
54a553d
feat(Validator): 시작값이 숫자인지 판별 기능 구현
GO-TE Oct 16, 2024
e482abc
feat(SeparatorManager): 구분자가 이미 선언 되었는지 확인 기능 구현
GO-TE Oct 16, 2024
bbe1a9b
feat(SeparatorManager): 구분자 추가 기능 구현
GO-TE Oct 16, 2024
34ab00f
feat(SeparatorManager): 수정 불가한 구분자 리스트 전달 기능 구현
GO-TE Oct 17, 2024
5d7cf65
chore: SeparatorManager 오타 수정
GO-TE Oct 17, 2024
05084c0
feat(StringHandler): 커스텀 구분자 분리해서 넘겨주는 기능 구현
GO-TE Oct 17, 2024
85820c0
feat(StringHandler): 입력 값에서 구분자만 제거하는 기능 구현
GO-TE Oct 17, 2024
3db0f08
feat(Adder): 전달 받은 값을 더하는 기능 구현
GO-TE Oct 17, 2024
685a57a
feat(Adder): 결과값 전달 기능 구현
GO-TE Oct 17, 2024
987c91c
feat(Validator): 커스텀 구분자에 대한 예외처리 구현
GO-TE Oct 17, 2024
c932d2c
docs: 검사기의 범위 초과 예외처리 기능 뎃셈기로 이동
GO-TE Oct 17, 2024
0f01bc4
feat(Validator): 포멧에 맞지 않는 입력일 시 예외 처리 기능 구현
GO-TE Oct 18, 2024
eb937f1
feat(Adder): 오버 플로우 발생 시 예외 처리 기능 구현
GO-TE Oct 18, 2024
d6a3161
feat(Output): 시작 메세지 출력 기능 구현
GO-TE Oct 18, 2024
5efe9f2
feat(Output): 결과값 출력 기능 구현
GO-TE Oct 18, 2024
bebc033
fix(StringHandler): getNumbers() 모든 수가 1의 자리로 되는 버그 수정
GO-TE Oct 19, 2024
f192a39
fix(StringHandler): 커스텀 구분자 선언문 추출, 제거 기능 수정
GO-TE Oct 19, 2024
93dc649
docs: 문자열 핸들러 기능 구현 목록 수정
GO-TE Oct 19, 2024
dde4daa
fix(Validator): 커스텀 구분자 유무, 입력값 검증 기능 수정
GO-TE Oct 19, 2024
1026689
feat(Controller): 시스템 로직 기능 구현
GO-TE Oct 19, 2024
9253888
feat(Application): 어플리케이션 기능 구현
GO-TE Oct 19, 2024
1d200a7
fix(test): 개행 문자로 인식하지 않도록 수정 (\n -> \n)
GO-TE Oct 19, 2024
a801593
refactor: 유틸성 띄는 클래스 util package로 이동
GO-TE Oct 19, 2024
050b645
chore(Controller): util 패키지 이동으로 인한 import 수정
GO-TE Oct 19, 2024
f9c80d1
fix(util): Validator 공백 입력시 에러 수정
GO-TE Oct 19, 2024
c90a512
feat(Adder): 숫자만 입력 되었는지 확인하는 기능 구현
GO-TE Oct 21, 2024
9aad1c3
style: import 와일드 카드 제거
GO-TE Oct 21, 2024
c3dbbc0
refactor(SeparatorManage): 커스텀 구분자 추가 이전 이미 존재하는지 검증
GO-TE Oct 21, 2024
bdc59bb
refactor: 하드 코딩 제거
GO-TE Oct 21, 2024
4115bc9
docs: 구현 완료 체크
GO-TE Oct 21, 2024
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
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,57 @@
# java-calculator-precourse
# java-calculator-precourse
## 기능 구현 목록
입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.

### 입력
- [X] 유저의 입력을 전달한다.
Copy link

Choose a reason for hiding this comment

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

문서도 꼼꼼하게 적어주셨고,
기능마다 체크 표시도 해주셨네요 👍

살아있는 문서화 좋습니다!


### 입력값 검사기
- 커스텀 구분자가 있는 입력일 때
- [X] 커스텀 구분자가 `//`로 시작해 `\n`가 포함되는지 확인한다.
- [X] 커스텀 구분자가 길이가 1이 아니라면 예외 처리한다.
- [X] 커스텀 구분자가 숫자라면 예외 처리한다.
- [X] 커스텀 구분자가 실수를 표현하는 `.`라면 예외 처리한다.
- [X] 커스텀 구분자가 기본 구분자라면 예외 처리 한다.


- 커스텀 구분자가 없을 입력일 때
- [X] 입력값이 숫자로 시작하는 지 확인한다.


- 공통
- [X] 커스텀 구분자를 입력하려는지 확인한다.
- [X] 음수가 입력되었다면 예외 처리 한다.
- [X] 구분자, `.`, 숫자 외 문자가 검출 된다면 예외 처리한다.

### 구분자 관리
- [X] 기본 구분자 외 커스텀 구분자를 전달받아 추가한다.
- [X] 전달받은 구분자가 기본(이미 존재하는지) 구분자인지 확인한다.
- [X] 구분자를 넘겨준다.

### 문자열 핸들러
- [X] 커스텀 구분자를 선언한다면 분리한다.
- [X] 분리한 커스텀 구분자를 넘겨준다.
- [X] 전달받은 구분자로 입력받은 문자열을 분리한다.

### 덧셈기
- [X] 전달 받은 값을 더해 저장한다.
- [X] 저장한 결과 값을 전달한다.
- [X] 입력값이 출력 범위를 초과한다면 예외 처리한다.
- [X] 더한 값이 출력 범위를 초과한다면 예외 처리한다.

### 출력
- [X] 시작 메세지를 출력한다.
- [X] 결과값을 출력한다.

---
## 리팩토링

- [X] 하드 코딩 제거
- [X] 코딩 컨벤션 준수
- [X] 이상한 수식 넣었을 때 에러
```
덧셈할 문자열을 입력해 주세요.
//;\n112kio12u328948
Exception in thread "main" java.lang.NumberFormatException: For input string: "112kio12u328948"
```
- [ ] 기능 쪼개기
Copy link

Choose a reason for hiding this comment

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

제출을 완료했는데도 체크가 안된 체크 박스가 있는 건 왜일까요? 👀

3 changes: 2 additions & 1 deletion src/main/java/calculator/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
Controller controller = new Controller();
controller.start();
}
}
46 changes: 46 additions & 0 deletions src/main/java/calculator/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package calculator;

import calculator.util.Adder;
import calculator.util.SeparatorManager;
import calculator.util.StringHandler;
import calculator.util.Validator;
import java.util.List;

public class Controller {
Input input = new Input();
Output output = new Output();
SeparatorManager separatorManager = new SeparatorManager();
StringHandler stringHandler = new StringHandler();
Validator validator = new Validator();
Adder adder = new Adder();
Comment on lines +10 to +15
Copy link

Choose a reason for hiding this comment

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

클래스 내부에서 객체를 생성하면 어떤 단점이 있을까요 ??

관련해서 의존성 주입을 참고해보시면 좋을것 같아요 ㅎㅎ

Comment on lines +10 to +15
Copy link

Choose a reason for hiding this comment

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

컨트롤러에서 많은 객체를 생성해주고 있네요.

클래스 내부에서 객체를 직접 생성하면 생기는 단점은 차치하고서라도, 다른 단점은 있을 수 없을까요?
현재 컨트롤러 클래스에 지나치게 많은 역할이 부여되고 있지는 않은가 고민해보아도 좋을 것 같습니다. 🤔


public void start() {
output.printStart();
String userInput = input.readInput();
validator.validateInput(userInput);

if (validator.hasCustomSeparator(userInput)) {
String separator = stringHandler.extractSeparator(userInput);
validator.validateSeparator(separator);
separatorManager.add(separator);
String rawNumbers = stringHandler.removeCustom(userInput);
List<String> numbers = stringHandler.getNumbers(
separatorManager.getSeparators(),
rawNumbers);
adder.add(numbers);
output.printResult(adder.getAnswer());
return;
}

if (validator.isStartWithDigit(userInput)) {
List<String> numbers = stringHandler.getNumbers(
separatorManager.getSeparators(),
userInput
);
adder.add(numbers);
output.printResult(adder.getAnswer());
return;
}
output.printResult(adder.getAnswer());
}
}
9 changes: 9 additions & 0 deletions src/main/java/calculator/Input.java
Copy link

Choose a reason for hiding this comment

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

InputView가 입력 에 대한 책임만 갖고 있네요~

저도 이러한 방식으로 구현했는데요~ 근데 피드백으로 사용자에게 입력을 받을때 출력하는 문구는 InputView의 책임이 아닌가 하는 의견을 들었습니다~

이 부분에 대해서 저는 아직도 해답을 찾지 못했는데, 경태님의 생각은 어떠신지 궁금해요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package calculator;

import camp.nextstep.edu.missionutils.Console;

public class Input {
public String readInput() {
return Console.readLine();
}
}
11 changes: 11 additions & 0 deletions src/main/java/calculator/Output.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package calculator;

public class Output {
public void printStart() {
System.out.println("덧셈할 문자열을 입력해 주세요.");
}

public void printResult(long result) {
System.out.println("결과 : " + result);
}
}
39 changes: 39 additions & 0 deletions src/main/java/calculator/util/Adder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package calculator.util;

import java.util.List;

public class Adder {
long answer;
Copy link

Choose a reason for hiding this comment

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

Adder라는 이름만 보면 단순히 덧셈 기능만을 수행할 것 같은데,
내부에 answer라는 필드를 관리하고 있네요. 또 이 값을 getter로 가져오고 있고요.

이때 발생할 수 있는 문제는 없을까요? 🤔


public void add(List<String> numbers) {
answer = 0;
for (String number: numbers) {
if (number.isBlank()) {
continue;
}
validateNumber(number);
long element = Long.parseLong(number);
validateOverflow(element);
answer += element;
}
validateOverflow(answer);
}

public long getAnswer() {
return answer;
}

private void validateOverflow(long number) {
if (number < 0) {
throw new IllegalArgumentException("허용 범위를 초과하였습니다.");
}
}

private void validateNumber(String number) {
for (int i = 0; i < number.length(); i++) {
if (!Character.isDigit(number.charAt(i))) {
throw new IllegalArgumentException("등록된 구분자가 아닙니다.");
}
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/calculator/util/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package calculator.util;

import java.util.List;

public class Constants {
public static final String CUSTOM_PREFIX = "//";
public static final String CUSTOM_SUFFIX = "\\n";
public static final int SEPARATOR_LENGTH = 1;
public static final String DECIMAL_POINT = ".";
public static final List<String> DEFAULT_SEPARATORS = List.of(":", ",");
}
29 changes: 29 additions & 0 deletions src/main/java/calculator/util/SeparatorManager.java
Copy link

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,29 @@
package calculator.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class SeparatorManager {
List<String> separators;

public SeparatorManager() {
this.separators = new ArrayList<>(Constants.DEFAULT_SEPARATORS);
}

private boolean exists(String separator) {
return separators.contains(separator);
}

public void add(String separator) {
if (this.exists(separator)) {
throw new IllegalArgumentException("이미 존재하는 구분자입니다.");
}
separators.add(separator);
}

public List<String> getSeparators() {
return Collections.unmodifiableList(separators);
}
}
40 changes: 40 additions & 0 deletions src/main/java/calculator/util/StringHandler.java
Copy link

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,40 @@
package calculator.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StringHandler {
public String extractSeparator(String input) {
int start = Constants.CUSTOM_PREFIX.length() ;
int end = input.indexOf(Constants.CUSTOM_SUFFIX);
return input.substring(start, end);
}

public List<String> getNumbers(List<String> separators, String input) {
List<String> splitInput = new ArrayList<>();
splitInput.add(input);

for (String separator : separators) {
splitInput = splitBySeparator(splitInput, separator);
}

return splitInput;
}

private List<String> splitBySeparator(List<String> inputList, String separator) {
List<String> result = new ArrayList<>();

for (String part : inputList) {
String[] splitParts = part.split(separator);
result.addAll(Arrays.asList(splitParts));
}

return result;
}

public String removeCustom(String input) {
int numberIndex = input.indexOf(Constants.CUSTOM_SUFFIX) + 2;
Copy link

Choose a reason for hiding this comment

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

여기의 "2"는 상수로 관리하지 않고 있는 이유가 있나요?

return input.substring(numberIndex);
}
}
48 changes: 48 additions & 0 deletions src/main/java/calculator/util/Validator.java
Copy link

Choose a reason for hiding this comment

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

해당 클래스에 있는 메서드들을 static으로 선언하면 재사용하기 더 좋을것 같아요~

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package calculator.util;

public class Validator {
public boolean hasCustomSeparator(String input) {
if (isEmpty(input)) {
return false;
}
return input.startsWith(Constants.CUSTOM_PREFIX) && input.contains(Constants.CUSTOM_SUFFIX);
}

public boolean isStartWithDigit(String input) {
if (isEmpty(input)) {
return false;
}
return Character.isDigit(input.charAt(0));
}

public boolean isEmpty(String input) {
return input == null || input.isBlank();
}

public void validateSeparator(String separator) {
if (separator.length() != Constants.SEPARATOR_LENGTH) {
throw new IllegalArgumentException("구분자의 길이는 1이어야 합니다.");
}
if (separator.equals(Constants.DECIMAL_POINT)) {
throw new IllegalArgumentException(".은 구분자가 될 수 없습니다.");
}
if (isStartWithDigit(separator)) {
throw new IllegalArgumentException("숫자는 구분자가 될 수 없습니다.");
}
}

public void validateInput(String input) {
if (isEmpty(input)) {
return;
}
if (hasCustomSeparator(input)) {
return;
}
if (isStartWithDigit(input)) {
if ((!input.contains(Constants.CUSTOM_PREFIX) && !input.contains(Constants.CUSTOM_SUFFIX))) {
return;
}
}
throw new IllegalArgumentException("포멧에 맞게 입력해 주세요.(ex://@\\n1@2:3,4)");
}
}
24 changes: 24 additions & 0 deletions src/test/java/calculator/AdderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package calculator;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import calculator.util.Adder;
import java.util.List;
import org.junit.jupiter.api.Test;

class AdderTest {
Adder adder = new Adder();

@Test
void 공백일때_0을_return_하는지() {
adder.add(List.of(""));
assertEquals(0, adder.getAnswer());
}

@Test
void 오버플로우_발생시_예외_처리() {
assertThatThrownBy(() ->adder.add(List.of(String.valueOf(Double.MAX_VALUE),"1")))
.isInstanceOf(IllegalArgumentException.class);
}
}
8 changes: 8 additions & 0 deletions src/test/java/calculator/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ class ApplicationTest extends NsTest {
});
}

@Test
void 공백_입력() {
assertSimpleTest(() -> {
run(" ");
assertThat(output()).contains("결과 : 0");
});
}

@Test
void 예외_테스트() {
assertSimpleTest(() ->
Expand Down
29 changes: 29 additions & 0 deletions src/test/java/calculator/SeparatorManagerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package calculator;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;

import calculator.util.SeparatorManager;
import org.junit.jupiter.api.Test;

class SeparatorManagerTest {
SeparatorManager separatorManager = new SeparatorManager();

@Test
void 이미_있는_구분자라면_예외_처리() {
assertThatThrownBy(() -> separatorManager.add(":"))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
void 없는_구분자라면_정상_작동() {
assertThatCode(() -> separatorManager.add(";"))
.doesNotThrowAnyException();
}

@Test
void 받은_구분자_리스트의_수정_불가한지_확인() {
assertThatThrownBy(() -> separatorManager.getSeparators().remove(0))
.isInstanceOf(UnsupportedOperationException.class);
}
}
Loading