-
Notifications
You must be signed in to change notification settings - Fork 6
[재연] 문자열 덧셈 계산기 #6
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: sjy
Are you sure you want to change the base?
Changes from all commits
b10cf9f
7ec8ac0
f9e658b
59af198
55d923c
e3309b5
7cbb28e
e22bf1e
10cab20
54ae35e
d146923
6070a64
dfcaa89
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 |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
|
|
||
| 메시지 | ||
|
|
||
| 1. 입력을 받아라 | ||
| 2. 구분자를 뽑아내라 - inner class | ||
| 3. 구분자에 따라 쪼개라 | ||
| 4. 덧셈해라 | ||
|
|
||
|
|
||
| 구분자로 쪼개는 애 > Splitter | ||
| 구분자 뽑아내는 애 > Splitter.delimiter | ||
|
|
||
| 커스텀 구분자가 있는지 없는지 판단하는 애? 서비스레이어에서하자 | ||
|
|
||
|
|
||
| 덧셈하는 > Calculator |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package domain; | ||
|
|
||
| import domain.dto.Operand; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class Calculator { | ||
| public long calculate(List<String> operands) { | ||
| return operands.stream() | ||
| .map(Operand::new) | ||
| .reduce(new Operand(), Operand::sum) | ||
| .getValue(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package domain.dto; | ||
|
|
||
| public class Operand { | ||
|
Member
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. ㅎㅎ 이왕
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. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ |
||
| private final long value; | ||
|
|
||
| public Operand(){ | ||
| this.value = 0; | ||
| } | ||
|
|
||
| public Operand(final String operand) { | ||
| try { | ||
| long parsedOperand = Long.parseLong(operand); | ||
| checkNegative(parsedOperand); | ||
| this.value = parsedOperand; | ||
| } catch (NumberFormatException ne) { | ||
| throw new IllegalArgumentException(String.format("%s 는 Long 형으로 파싱될 수 없습니다.", operand)); | ||
| } | ||
| } | ||
|
|
||
| private Operand(long value) { | ||
| this.value = value; | ||
| } | ||
|
|
||
| private void checkNegative(long parsedOperand) { | ||
| if (parsedOperand < 0) { | ||
| throw new IllegalArgumentException(String.format("음수인 %d 값은 들어올 수 없습니다", parsedOperand)); | ||
| } | ||
| } | ||
|
|
||
| public long getValue() { | ||
| return this.value; | ||
| } | ||
|
|
||
| public Operand sum(Operand operand) { | ||
| return new Operand(this.value + operand.value); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| if (o == null || getClass() != o.getClass()) return false; | ||
|
|
||
| Operand operand = (Operand) o; | ||
|
|
||
| return value == operand.value; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return (int) (value ^ (value >>> 32)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package service; | ||
|
|
||
| import domain.Calculator; | ||
| import support.Splitter; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class CalculatorService { | ||
| private final Calculator calculator; | ||
|
|
||
| public CalculatorService(Calculator calculator) { | ||
| this.calculator = calculator; | ||
| } | ||
|
|
||
| public long calculate(final String formula) { | ||
|
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. 요구사항 1번 빈문자열은 0리턴, 2번 숫자 하나를 입력받으면 해당숫자반환은 없는거같은데,
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. T..D..D.. |
||
| if (isNullOrEmpty(formula)) { | ||
| return 0; | ||
| } | ||
|
|
||
| return calculator.calculate(Splitter.split(formula.trim())); | ||
|
Member
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. 이 구문은 한줄에 .이 너무 많아서 가독성이 안좋아보여요.
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. |
||
| } | ||
|
|
||
| private boolean isNullOrEmpty(String formula) { | ||
| return formula == null || formula.isEmpty(); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package support; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.regex.Matcher; | ||
| import java.util.regex.Pattern; | ||
|
|
||
|
|
||
| public class Splitter { | ||
| private static final String DEFAULT_DELIMITER_REGEX = "[,:]"; | ||
| private static final String NUMERIC_REGEX = "-?\\d+(\\.\\d+)?"; | ||
|
|
||
| private static final int CUSTOM_DELIMITER_INDEX = 1; | ||
| private static final int REAL_FORMULA_INDEX = 2; | ||
|
|
||
| private static final Pattern CUSTOM_DELIMITER_PATTERN = Pattern.compile("//(.*)₩n(.*)"); | ||
|
|
||
| private Splitter() { | ||
| } | ||
|
|
||
| public static List<String> split(final String formula) { | ||
| Matcher customDelimiterMatcher = CUSTOM_DELIMITER_PATTERN.matcher(formula); | ||
|
|
||
| if (customDelimiterMatcher.find()) { | ||
| return split(customDelimiterMatcher); | ||
| } | ||
|
|
||
| return Arrays.asList(formula.split(DEFAULT_DELIMITER_REGEX)); | ||
| } | ||
|
|
||
| private static List<String> split(Matcher customDelimiterMatcher) { | ||
| String realFormula = customDelimiterMatcher.group(REAL_FORMULA_INDEX); | ||
| String customDelimiter = customDelimiterMatcher.group(CUSTOM_DELIMITER_INDEX); | ||
|
|
||
| validateDelimiter(customDelimiter); | ||
|
|
||
| return Arrays.asList(realFormula.split(customDelimiter)); | ||
| } | ||
|
|
||
| private static void validateDelimiter(final String customDelimiter) { | ||
| if (customDelimiter.matches(NUMERIC_REGEX) || customDelimiter.isEmpty()) { | ||
| throw new IllegalArgumentException(String.format("%s 는(은) 올바르지 않은 커스텀구분자입니다.", customDelimiter)); | ||
| } | ||
|
Comment on lines
+41
to
+43
Member
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,52 @@ | ||
| package domain; | ||
|
|
||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; | ||
| import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; | ||
|
|
||
| class CalculatorTest { | ||
|
|
||
| Calculator calculator = new Calculator(); | ||
|
|
||
| @DisplayName("calculator에 음수가 아닌 숫자가 들어가면 정상적으로 더해서 반환한다.") | ||
| @Test | ||
| void calculate() { | ||
| //given | ||
| List<String> formula = Arrays.asList("1", "2", "3"); | ||
|
|
||
| //when | ||
| double result = calculator.calculate(formula); | ||
|
|
||
| //then | ||
| assertThat(result).isEqualTo(6); | ||
| } | ||
|
|
||
| @DisplayName("숫자가 아닌 값이나 음수가 들어가면 Exception이 발생한다") | ||
| @MethodSource("exceptionCase") | ||
| @ParameterizedTest | ||
| void calculateThrow(List<String> formula, String exceptionMessage) { | ||
| //given | ||
| //when | ||
| //then | ||
| assertThatThrownBy(() -> calculator.calculate(formula)) | ||
| .isInstanceOf(IllegalArgumentException.class) | ||
| .hasMessage(exceptionMessage); | ||
| } | ||
|
|
||
| static Stream<Arguments> exceptionCase() { | ||
| return Stream.of( | ||
| Arguments.of(Arrays.asList("1", "@", "3"), String.format("%s 는 Long 형으로 파싱될 수 없습니다.", "@")), | ||
| Arguments.of(Arrays.asList("1", "-2", "3"), String.format("음수인 %d 값은 들어올 수 없습니다", -2)) | ||
| ); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package domain.dto; | ||
|
|
||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
|
|
||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.assertj.core.api.Assertions.*; | ||
| import static org.junit.jupiter.api.Assertions.*; | ||
|
|
||
| class OperandTest { | ||
|
|
||
| @DisplayName("음수나 문자가 들어가면 익셉션을 던져준다") | ||
| @MethodSource("negativeAndString") | ||
| @ParameterizedTest | ||
| void negative(String input, String exceptionMessage){ | ||
| assertThatThrownBy(() -> new Operand(input)) | ||
| .isInstanceOf(IllegalArgumentException.class) | ||
| .hasMessage(exceptionMessage); | ||
| } | ||
|
|
||
| static Stream<Arguments> negativeAndString(){ | ||
| return Stream.of( | ||
| Arguments.of("-5", "음수인 -5 값은 들어올 수 없습니다"), | ||
| Arguments.of("abc", "abc 는 Long 형으로 파싱될 수 없습니다.") | ||
| ); | ||
| } | ||
|
|
||
| @DisplayName("Operand 끼리 sum 정상 동작 수행 확인") | ||
| @Test | ||
| void sum(){ | ||
| Operand operand = new Operand("5"); | ||
| Operand operand1 = new Operand("1"); | ||
|
|
||
| assertThat(operand.sum(operand1)).isEqualTo(new Operand("6")); | ||
| } | ||
|
|
||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package service; | ||
|
|
||
| import domain.Calculator; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
| import org.junit.jupiter.params.provider.NullAndEmptySource; | ||
|
|
||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; | ||
| import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; | ||
| import static org.junit.jupiter.api.Assertions.assertAll; | ||
|
|
||
| class CalculatorServiceTest { | ||
|
|
||
| CalculatorService calculatorService = new CalculatorService(new Calculator()); | ||
|
|
||
| @DisplayName(",과 : 로 구분하거나 커스텀 구분자를 설정한 정상계산 테스트") | ||
| @MethodSource("validFormula") | ||
| @ParameterizedTest | ||
| void validFormula(String formula, long expectedValue) { | ||
| assertThat(calculatorService.calculate(formula)).isEqualTo(expectedValue); | ||
| } | ||
|
|
||
| static Stream<Arguments> validFormula() { | ||
| return Stream.of( | ||
| Arguments.of("//o₩n1o2o3", 6L), | ||
| Arguments.of("//abc₩n1abc2abc3abc", 6L), | ||
| Arguments.of("//@₩n3@1@7@1", 12L), | ||
| Arguments.of("1,2:3", 6L) | ||
| ); | ||
| } | ||
|
|
||
| @DisplayName("숫자 하나를 문자열로 입력할 경우 해당 숫자를 반환한다.") | ||
| @Test | ||
| void formulaWithoutDelimiter() { | ||
| String formula = "12345"; | ||
|
|
||
| assertThat(calculatorService.calculate(formula)).isEqualTo(Long.parseLong(formula)); | ||
| } | ||
|
|
||
| @DisplayName("올바르지 않은 수식 테스트") | ||
csbsjy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| @MethodSource("invalidFormula") | ||
| @ParameterizedTest | ||
| void inValidFormula(String formula, String exceptionMessage) { | ||
| assertThatThrownBy(() -> calculatorService.calculate(formula)) | ||
| .isInstanceOf(IllegalArgumentException.class) | ||
| .hasMessage(exceptionMessage); | ||
| } | ||
|
|
||
| static Stream<Arguments> invalidFormula() { | ||
| return Stream.of( | ||
| Arguments.of("//₩n1o2o3", " 는(은) 올바르지 않은 커스텀구분자입니다."), | ||
| Arguments.of("//abc", "//abc 는 Long 형으로 파싱될 수 없습니다."), | ||
| Arguments.of("1abc2abc3abc", "1abc2abc3abc 는 Long 형으로 파싱될 수 없습니다."), | ||
| Arguments.of("//o₩n1*2", "1*2 는 Long 형으로 파싱될 수 없습니다.") | ||
| ); | ||
| } | ||
|
|
||
| @DisplayName("빈 문자열 또는 null 값이 들어가면 0 을 반환해야 한다.") | ||
| @ParameterizedTest | ||
| @NullAndEmptySource | ||
| void emptyNull(String formula) { | ||
| assertThat(calculatorService.calculate((formula))).isEqualTo(0); | ||
| } | ||
|
|
||
| } | ||

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.
reduce를 사용하면 한줄로 가능할거 같고 Operand의 값을 꺼내서 하는것보단 새로운 두개의 값을 더해서 새로운 오퍼랜드를 꺼내는게 좋을듯
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.
선생님 reduce 너무 어려워요