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
130 changes: 129 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,129 @@
# java-calculator-precourse
# java-calculator-precourse
# 문자열 덧셈 계산기

입력된 문자열을 구분자를 기준으로 분리하여 합계를 계산하는 프로그램입니다.
절차지향적으로 기능을 완성한 뒤, 객체지향 원칙에 따라 리팩토링하며 구조를 개선했습니다.

---

## 미션 개요

- 우아한테크코스 프리코스 1주차 과제
- 문자열 입력을 받아 구분자 기준으로 숫자를 분리하고, 합을 계산하는 프로그램
- 기본 구분자( `,` , `:` )와 커스텀 구분자(`//;\n`) 모두 지원
- 잘못된 입력(음수, 문자 등)에 대해 예외 발생

---

## 기능 요구사항

- 입력된 문자열에서 숫자를 추출하여 덧셈 결과를 반환한다.
- 기본 구분자(쉼표 `,` , 콜론 `:`)를 사용한다.
- 커스텀 구분자를 지정할 수 있다. (예: `//;\n1;2;3`)
- 잘못된 입력(문자, 음수, 공백 등)은 `IllegalArgumentException`을 발생시킨다.

---

## 구현 단계별 정리

이 프로젝트는 **절차지향적으로 구현한 뒤**,
**리팩토링을 통해 객체지향적으로 개선한 과정**을 담고 있습니다.
핵심 로직을 세분화하고, 역할에 따라 클래스를 분리하며 점진적으로 구조를 발전시켰습니다.

---

### 1단계: 핵심 로직 구현 (절차지향 → 기능 분리)

- 문자열을 입력받아 구분자 기준으로 분리하고, 숫자를 더하는 기본 기능 구현
- 커스텀 구분자(`//;\n`) 처리 및 음수 검증 등 비즈니스 규칙을 절차적으로 구현
- 기능 단위로 메서드를 분리하여 테스트 용이성 확보

---

### 2단계: 구조화 및 객체지향 리팩토링

Application / InputHandler: 사용자 I/O 처리 및 모든 객체를 조립하여 계산 흐름을 실행합니다. (조립 및 I/O 분리)

Calculator (I): calculate(String input) 메서드로 계산 행위의 규약을 정의합니다. (최상위 규약)

ValidationStrategy (I): 검증 로직의 규약을 정의하며, StringCalculator가 검증 방식을 유연하게 바꿀 수 있게 합니다. (전략 패턴 인터페이스)

StringCalculator: 모든 구성 요소를 주입받아 순차적으로 호출하며 계산 흐름을 총괄하고, 오직 숫자 합산 책임만 가집니다. (지휘자/서비스)

CollectAllValidator: ValidationStrategy를 구현하여, 모든 음수를 수집하고 한 번에 예외를 던지는 복잡한 검증 전략을 수행합니다. (검증 전략 구현체)

DefaultDelimiterParser: 입력 문자열을 분석하여 **구분자 정보(DelimiterInfo)**와 숫자 문자열을 추출합니다. (\r 문자 정규화 처리 포함) (분석 전문가)

DelimiterInfo (DTO): 구분자 정규식 및 숫자 문자열을 담는 데이터 전달 객체입니다. (데이터 홀더)

NumberParser: 개별 토큰을 BigInteger로 변환하고 NumberFormatException을 처리합니다. (변환 전문가)의

---
## 예외 상황

- 문자가 포함된 입력값 (예: `"a,2,3"`, `"1b:4"`)
- 음수 입력값 (예: `"-1,2,3"`)
- 구분자가 연속된 경우 (예: `"1,,2"`)
- 소수 입력 (예: `"1.5,2"`)
- 빈 문자열 또는 공백 입력 (예: `""`, `" "`)
- `null` 또는 입력이 존재하지 않는 경우
- 숫자만 단독 입력된 경우

---

## 미션 진행 방향

이 미션은 **절차지향적으로 작동하는 코드를 먼저 작성한 뒤**,
객체지향 원칙(OOP)에 따라 **역할과 책임 중심으로 구조를 개선**하는 것을 목표로 합니다.

1. 절차지향적으로 기능을 완성한다.
2. 역할과 책임을 분리하여 구조를 개선한다.
3. 테스트 가능한 구조로 리팩토링한다.

---

## 커밋 컨벤션

AngularJS Commit Message 규칙을 참고했습니다.
커밋은 **기능 단위**로 나누어 작성합니다. (예: 기본 구분자 처리, 커스텀 구분자 추가, 음수 예외 처리 등)

**Allowed `<type>`**

- feat: 새로운 기능 추가
- fix: 버그 수정
- docs: 문서 수정
- style: 코드 포맷팅
- refactor: 코드 리팩토링
- test: 테스트 코드 추가
- chore: 빌드, 설정 등 유지보수 작업

> 콜론(`:`) 뒤에는 반드시 공백 한 칸을 둡니다.

---

## 어떤 점에 집중했는가

- 절차지향적 설계로 기본 동작 완성 능력 향상
- 예외 처리와 입력 검증을 체계적으로 구현
- 객체지향적 리팩토링을 통한 책임 분리 연습
- 클린 코드 원칙을 적용하고 유지보수성을 고려한 설계

---

## 요약

이 프로젝트는 문자열 계산기를 절차지향적으로 구현한 뒤,
객체지향 설계 원칙에 따라 구조를 개선하며 **깨끗하고 확장 가능한 코드**로 발전시키는 과정을 담고 있습니다.

---
과제 소감 및 회고

이번 과제를 진행하며 처음에는 절차지향적으로 구현을 한 뒤, 점차 리팩토링을 통해 설계를 개선하는 과정을 보여주고 싶었습니다. 초기에는 차례대로 기능을 구현하는 데 집중했지만, 리드미에 추가된 새로운 모듈들이 등장하면서 예상치 못한 상황에 당황하기도 했습니다. 이를 통해 구현 과정에서 수정 가능성을 감안한 설계의 중요성을 깨달았습니다.

특히 객체지향 설계 패턴을 적용하면서, StringCalculator가 모든 책임을 지는 구조에서 CollectAllValidator와 같은 전략 구현체로 책임을 위임하는 전략 패턴에 익숙해지는 데 시간이 필요했습니다. 이를 통해 인터페이스의 본질과 조립의 책임을 체감할 수 있었습니다.

또한, 우테코 미션에서는 기능 구현만큼이나 요구사항에 명시된 출력 형식이 중요함을 배웠습니다. 최종 실패 원인이 논리적 오류가 아닌 띄어쓰기 불일치였다는 점에서 출력 형식의 중요성을 절실히 느꼈습니다. 이번 과제에서는 System.out.print()로 직접 확인하던 방식 대신 단위 테스트와 통합 테스트를 통해 안정성을 검증하며, 테스트 코드 작성의 중요성과 오류를 찾아가는 과정의 즐거움도 경험할 수 있었습니다.

환경 독립적인 코드 작성의 중요성도 깨달았습니다. DefaultDelimiterParser에서 \r 문자를 제거하여 운영체제나 테스트 환경의 사소한 차이에도 코드가 흔들리지 않도록 입력을 정규화한 경험이 그 예입니다.

마지막으로, 과제 수행 과정에서 겪은 고난과 역경은 코드 결과에는 그대로 드러나지 않았지만, 이를 커밋 메시지(feat, refactor, fix 등)로 기록하며 과정의 의미를 남기려 노력했습니다. 이번 경험을 통해 단순 구현을 넘어 설계, 테스트, 환경 고려, 기록까지 완성하는 과정을 배우게 되었으며, 다음 과제에서는 한층 더 발전된 나를 기대합니다.
29 changes: 27 additions & 2 deletions src/main/java/calculator/Application.java

Choose a reason for hiding this comment

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

파일 한 곳에 여러 기능들이 몰려있는 느낌입니다..! 클래스의 인스턴스 생성, Caclulator의 동작 메서드 등 다양하게 역할을 분배해도 좋을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다! 2주차때 적용해보도록 하겠습니다 ㅎㅎ

Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
package calculator;

Choose a reason for hiding this comment

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

패키지 구조를 더 나눠서 클래스들을 모아놓는다면 더 직관적으로 구조가 잘 보일 것 같습니다! 어떻게 생각하시나요??

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다 2주차때 적용해보도록 하겠습니다!


import java.math.BigInteger;

public class Application {

public static void main(String[] args) {
// TODO: 프로그램 구현
InputHandler inputHandler = new InputHandler();
String input = inputHandler.getUserInput();

try {
DelimiterParser delimiterParser = new DefaultDelimiterParser();
NumberParser numberParser = new NumberParser();
ValidationStrategy validationStrategy = new CollectAllValidator();

Calculator calculator = new StringCalculator(delimiterParser, numberParser, validationStrategy);

Comment on lines +11 to +17

Choose a reason for hiding this comment

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

제 개인 적인 생각을 적자면 StringCalculator 생성이 상당히 복잡해 보이네요!

개인적인 생각으로는 StringCalculator의 생성 책임을 Factory같은 생성전용 객체로 옴겨보는건 어떨까요?
그렇게 되면 이 코드는

Calculator calculator = CalculatorFactory.createStringCaclulator();

위 처럼 사용 할 수 있을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

객체를 생성하는것도 책임이라고 생각하지 못했던 것 같아요
따로 검색해보니 팩토리 디자인 패턴이라는 것도 있네요
저의 식견을 넓혀주셔서 감사합니다 ㅎㅎ

BigInteger result = calculator.calculate(input);

System.out.println("결과 : " + result);

} catch (IllegalArgumentException e) {
System.out.println("오류: " + e.getMessage());
throw e; // 예외를 다시 던져서 테스트에서 감지 가능하도록 처리
} catch (Exception e) {
System.out.println("예상치 못한 오류가 발생했습니다.");
throw e;
} finally {
inputHandler.close();

Choose a reason for hiding this comment

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

input을 위에서 입력받고 넘기는데 close를 가장 뒤에서 처리한 이유가 궁금합니다.

Copy link
Author

Choose a reason for hiding this comment

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

저의 설계의 실수인 것 같습니다!
제 코드를 다시 보니까 입력을 받고, 받은 입력을 바로 사용하는데 저는 예외 발생 여부와 상관 없이 InputHandler.close() 자원 해제를 보장 받고 싶었습니다.

그런데 Application.main에서 입력을 받고 바로 종료하는 구조인데 매번 close()를 호출하는게 비효율적이라고 느껴졌네요

try-catch 문에 inputHandler 객체를 넣었으면 finally 문이 필요 없었을 것 같네요 피드백 감사합니다!

}
}
Comment on lines +28 to 31

Choose a reason for hiding this comment

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

close있는걸 확인 못했는데 꼼꼼한게 너무 좋네요! 심지어 final에 넣으셔서 catch와 try의 정상과 비정상흐름에 적절히 닫힐 수 있게 만드시 부분 너무 좋네요!

Copy link
Author

Choose a reason for hiding this comment

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

ㅎㅎ 감사합니다 !!

}
}
7 changes: 7 additions & 0 deletions src/main/java/calculator/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package calculator;
import java.math.BigInteger;

public interface Calculator {
BigInteger calculate(String input);

}
25 changes: 25 additions & 0 deletions src/main/java/calculator/CollectAllValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package calculator;

import java.math.BigInteger;
import java.util.List;

public class CollectAllValidator implements ValidationStrategy {

@Override
public void validate(List<BigInteger> numbers) {
StringBuilder negativeNumbers = new StringBuilder();

for (BigInteger number : numbers) {

Choose a reason for hiding this comment

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

for문을 사용하는 것 보다 stream을 사용해보는 게 어떨까요? stream 자체적인 filter 함수를 통해 if문을 대체할 수 있어 가독성 부분에서 좋아질 것 같습니다!

Copy link
Author

Choose a reason for hiding this comment

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

stream 사용에 미숙해서 for문으로 돌려버렸네요 ... 이번 2주차 미션때 미숙한 저의 실력을 더 발전시키고자
사용해보도록 하겠습니다 감사합니다!

if (number.compareTo(BigInteger.ZERO) < 0) {
if (negativeNumbers.length() > 0) {

Choose a reason for hiding this comment

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

위 처럼 작성하신 이유는 부정문 때문일까요!?

if(!negativeNumbers.isEmtpy()){

}

위 처럼 isEmtpy로 좀더 읽기 편하게 만들 수도 있을 것 같습니다!(개인적인 의견입니다)

Copy link
Author

Choose a reason for hiding this comment

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

부정문 때문에 사용을 했습니다
생각을 해보니 이렇게 사용하면 다른 사람이 대충 보면
객체인지, 일반 변수인지 헷갈릴수도도 있겠네요
피드백 감사합니다!

negativeNumbers.append(", ");
}
negativeNumbers.append(number);
}
}

if (negativeNumbers.length() > 0) {

Choose a reason for hiding this comment

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

14번째 줄에 똑같은 함수가 존재하는데, 그곳에 exception을 던져도 될 것 같습니다

throw new IllegalArgumentException("음수는 입력이 불가능합니다 :" + negativeNumbers.toString()); }
}

}
34 changes: 34 additions & 0 deletions src/main/java/calculator/DefaultDelimiterParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package calculator;

import java.util.regex.Pattern;

public class DefaultDelimiterParser implements DelimiterParser {

private static final String BASIC_DELIMITER_REGEX = "[,:]";

Choose a reason for hiding this comment

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

정규표현식 입력을 실수할 수도 있으니, 기본 구분자는 리스트로 만들어두고, 이를 정규표현식 패턴으로 만들어주는 함수를 구현하는 것도 고려해볼 수 있을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

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

리스트로 만들면 확실히 구분자가 추가 되거나 변경되어도 리스트만 수정하면 좋겠네요 감사합니다!

Copy link

Choose a reason for hiding this comment

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

변수명에 대해 몇 가지 궁금한 점이 있습니다!

  1. finalDelimiterRegex에서 final을 붙이신 이유가 궁금해요.
    변수명에 final을 명시하는 것보다는 실제로 final 키워드를 사용하거나,
    delimiters 또는 delimiterPattern 같은 이름이 더 명확할 것 같습니다.

  2. numbersToSplit은 "분리할 숫자들"이라는 동사구 형태인데,
    아직 분리되지 않은 문자열이라 의미가 혼란스러워 보여요.
    inputText, numberString 같은 명사형이 더 자연스럽지 않을까요?


@Override
public DelimiterInfo parse(String input) {
// 테스트 입력의 "\\n" 문자열을 실제 개행 문자 '\n'으로 변환
String sanitizedInput = input.replace("\r", "").replace("\\n", "\n");

String finalDelimiterRegex = BASIC_DELIMITER_REGEX;
String numbersToSplit;

if (sanitizedInput.startsWith("//")) {

Choose a reason for hiding this comment

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

커스텀 구분자를 하드코딩으로 처리하였는데 이도 위와 마찬가지로 상수로 빼두는 것이 좋을 것 같습니다.
커스텀 구분자를 변경해달라는 요청에 대응할 수 있도록

Choose a reason for hiding this comment

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

startWith대신 정규표현식으로 한번에 커스텀 구분자 영역을 추출해보는 것도 고려해보면 좋을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

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

하드코딩 된 것이 많네요
oops
피드백 감사합니다

int delimiterEndIndex = sanitizedInput.indexOf("\n");

if (delimiterEndIndex == -1) {
throw new IllegalArgumentException("커스텀 구분자 선언 후 반드시 줄 바꿈(\\n)을 해야합니다.");
}

String customDelimiter = sanitizedInput.substring(2, delimiterEndIndex);

Choose a reason for hiding this comment

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

만일 여기서 customDelimiter가 ""거나, 숫자라면 정상 작동하는지 궁금합니다!!

Choose a reason for hiding this comment

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

2로 되어있는 부분도 하드코딩되어있어서 요구사항 변경에 대응이 안될 것 같습니다. length 등 함수를 통해서 하는 것이 더 좋을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

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

"" 이거나 숫자여도, 정상 작동이 되네요
관련 테스트 코드를 작성 안한 저의 미스인것 같습니다!
다음에는 세밀하게 작성을 해야겠어요

그리고 하드 코딩 부분 피드백 감사합니다!

Choose a reason for hiding this comment

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

커스텀구분자가 2문자 이상으로 들어오면, 커스텀 구분자가 2문자로 들어오게 되나요?

Copy link
Author

Choose a reason for hiding this comment

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

아닙니다. 2문자로 고정되지 않습니다!!

제시된 코드는 //와 \n 사이의 모든 문자열을 커스텀 구분자로 정확하게 추출합니다. 구분자가 2문자 이상(예: $$, ***, abcde)이더라도 그 길이 그대로 추출됩니다!

시작: substring(2, ...)는 //를 건너뛰고 구분자 첫 글자부터 시작합니다

종료: delimiterEndIndex는 \n의 위치이며, substring은 이 위치 직전까지 자릅니다

따라서 시작과 끝 경계 사이에 있는 문자는 길이에 관계없이 모두 구분자가 됩니다.

혹시 제가 의도한 바를 잘못 이해해서 대답했다면 다시 말씀해주세요

Choose a reason for hiding this comment

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

아 제가 잘못 질문했네요!
커스텀 구분자가 2문자 이상으로 들어오면 똑같이 커스텀 구분자가 2글자 이상으로 들어오는지 궁금했습니다 -> 위 답변에 이미 해결됐네요!

finalDelimiterRegex += "|" + Pattern.quote(customDelimiter);

numbersToSplit = sanitizedInput.substring(delimiterEndIndex + 1);
} else {
numbersToSplit = sanitizedInput;
}
Comment on lines +17 to +30

Choose a reason for hiding this comment

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

early return을 사용해보는 건 어떨까요!?

if (sanitizedInput.startsWith("//")) {
            int delimiterEndIndex = sanitizedInput.indexOf("\n");

            if (delimiterEndIndex == -1) {
                throw new IllegalArgumentException("커스텀 구분자 선언 후 반드시 줄 바꿈(\\n)을 해야합니다.");
            }

            String customDelimiter = sanitizedInput.substring(2, delimiterEndIndex);
            String delimiterRegex += BASIC_DELIMITER_REGEX + "|" + Pattern.quote(customDelimiter);

            String numbersToSplit = sanitizedInput.substring(delimiterEndIndex + 1);
            return new DelimiterInfo(delimiterRegex,numbersToSplit);
        } 
        return new DelimiterInfo(BASIC_DELIMITER_REGEX,numbersToSplit);

이런식으로 하면 좀더 잘 보이지 않을까 싶습니다!

Copy link
Author

Choose a reason for hiding this comment

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

코드가 너무 이뻐졌네요
감사합니닷... !!


return new DelimiterInfo(finalDelimiterRegex, numbersToSplit);
}
}
21 changes: 21 additions & 0 deletions src/main/java/calculator/DelimiterInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package calculator;


public class DelimiterInfo {

Choose a reason for hiding this comment

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

구분자에 대한 내용을 패키지로 한번 더 묶으면 가독성이 좋아질 것 같아요.

Choose a reason for hiding this comment

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

Info같은 경우 전부 객체를 생성할 필요없이 클래스 래벨에서 처리할 수 있을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

좋은 피드백 감사합니다!
구분자 관련 부분은 말씀처럼 패키지로 한번 더 묶으면 가독성이 훨씬 좋아질 것 같네요!
공통으로 사용하는 기능이나 상수만 있다면 굳이 New로 매번 객체를 생성할 필요가 없겠네요
오히려 코드의 가독성을 떨어트리고 성능도 좋지 않겠네요
다음 2주차 미션때 개선 해보도록 하겠습니다!


final private String delimiterRegex;
final private String numbersString;

public DelimiterInfo(String delimiterRegex, String numbersString) {
this.delimiterRegex = delimiterRegex;
this.numbersString = numbersString;
}

public String getDelimiterRegex() {
return delimiterRegex;
}

public String getNumbersString() {
return numbersString;
}
}
8 changes: 8 additions & 0 deletions src/main/java/calculator/DelimiterParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package calculator;

public interface DelimiterParser {

DelimiterInfo parse(String input);

}

19 changes: 19 additions & 0 deletions src/main/java/calculator/InputHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package calculator;

import camp.nextstep.edu.missionutils.Console;

public class InputHandler {

private static final String INPUT_MESSAGE = "덧셈할 문자열을 입력해 주세요.";


public String getUserInput() {
System.out.println(INPUT_MESSAGE);
return Console.readLine();
}


public void close() {
Console.close();
}
}
20 changes: 20 additions & 0 deletions src/main/java/calculator/NumberParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package calculator;

import java.math.BigInteger;

public class NumberParser {

public BigInteger parse(String token) {

Choose a reason for hiding this comment

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

BigInteger쓰면 오버플로우를 방지할 수 있겠네요!
배워갑니다!!!

String trimmedToken = token.trim();

if (trimmedToken.isEmpty()) {
return BigInteger.ZERO;
}

try {
return new BigInteger(trimmedToken);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("숫자 이외의 문자열이 포함되어 변환할 수 없습니다: " + trimmedToken);

Choose a reason for hiding this comment

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

Exception 상황 -> Validate하는 상황이라 예외 상황이 발생할 때 따로 메서드로 분리하면 좋을 것 같습니다.
validate만 하는 클래스를 생성해서 static으로 메서드를 생성한뒤 필요한 순간에만 불러오게 하는 건 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

단일책임원칙에 어긋나는 코드인 것 같네요
좋은 의견 감사합니다!

}
}
}
50 changes: 50 additions & 0 deletions src/main/java/calculator/StringCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package calculator;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class StringCalculator implements Calculator {

private final DelimiterParser delimiterParser;
private final NumberParser numberParser;
private final ValidationStrategy validationStrategy; // ⭐️ ValidationStrategy 필드 추가

public StringCalculator(DelimiterParser delimiterParser, NumberParser numberParser, ValidationStrategy validationStrategy) {
this.delimiterParser = delimiterParser;
this.numberParser = numberParser;
this.validationStrategy = validationStrategy; // ⭐️ Strategy 주입
}

@Override
public BigInteger calculate(String input) {
if (input == null || input.trim().isEmpty()) {
return BigInteger.ZERO;
}

DelimiterInfo info = delimiterParser.parse(input);

String[] parts = info.getNumbersString().split(info.getDelimiterRegex());

Choose a reason for hiding this comment

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

info를 받아와서 getDelimiter를 하는 것 대신 Delimiter 자체에서 split을 처리하는 방향에 대해서는 어떻게 생각하시나요??
그렇게 하면 Info 없이도 처리가 가능하고 getter를 없앨 수 있어서 더 명확해질 것 같다고 생각해요!

Copy link
Author

Choose a reason for hiding this comment

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

확실히 그렇게 하면 코드가 더 직관적이어서 보기 편할 것 같네요
좋은 의견 감사합니다.


return sum(parts);
}

private BigInteger sum(String[] parts) {

List<BigInteger> numbers = new ArrayList<>();
for (String token : parts) {
numbers.add(numberParser.parse(token));
}


validationStrategy.validate(numbers);

BigInteger sum = BigInteger.ZERO;
for (BigInteger number : numbers) {
// 검증이 완료된 숫자만 합산합니다.
sum = sum.add(number);
}

return sum;
}
}
9 changes: 9 additions & 0 deletions src/main/java/calculator/ValidationStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package calculator;

import java.math.BigInteger;
import java.util.List;

public interface ValidationStrategy {

void validate(List<BigInteger> number);
}
Loading