Skip to content
16 changes: 16 additions & 0 deletions Strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

메시지

1. 입력을 받아라
2. 구분자를 뽑아내라 - inner class
3. 구분자에 따라 쪼개라
4. 덧셈해라


구분자로 쪼개는 애 > Splitter
구분자 뽑아내는 애 > Splitter.delimiter

커스텀 구분자가 있는지 없는지 판단하는 애? 서비스레이어에서하자


덧셈하는 > Calculator
Empty file removed src/main/java/calculator/empty.txt
Empty file.
14 changes: 14 additions & 0 deletions src/main/java/domain/Calculator.java
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)

Choose a reason for hiding this comment

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

reduce를 사용하면 한줄로 가능할거 같고 Operand의 값을 꺼내서 하는것보단 새로운 두개의 값을 더해서 새로운 오퍼랜드를 꺼내는게 좋을듯

Copy link
Author

Choose a reason for hiding this comment

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

선생님 reduce 너무 어려워요

.reduce(new Operand(), Operand::sum)
.getValue();
}
}
52 changes: 52 additions & 0 deletions src/main/java/domain/dto/Operand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package domain.dto;

public class Operand {
Copy link
Member

Choose a reason for hiding this comment

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

ㅎㅎ 이왕 VO로 만든거 final로 만들어 주는건 어떨까!
상속받을일도 없을거 같기도 하고..!!
근데 나도 안했던거같아..

Copy link
Author

Choose a reason for hiding this comment

The 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));
}
}
27 changes: 27 additions & 0 deletions src/main/java/service/CalculatorService.java
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) {

Choose a reason for hiding this comment

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

요구사항 1번 빈문자열은 0리턴, 2번 숫자 하나를 입력받으면 해당숫자반환은 없는거같은데,
위와같이 요구사항이 명확한 경우에는 테스트 코드로 해당 상황들을 먼저 정의하고 실제 구현을 하는게 좋을듯 (TDD)

Copy link
Author

Choose a reason for hiding this comment

The 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()));
Copy link
Member

Choose a reason for hiding this comment

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

이 구문은 한줄에 .이 너무 많아서 가독성이 안좋아보여요.
Splitter.split(formula.trim()) 이 친구를 한 번 래핑하는 편이 더 나아보입니다.

Copy link
Author

Choose a reason for hiding this comment

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

오홍 의견이갈리네여 ~~ 아래 의견도 있었었습니당 ㅋㅋㅋ
다시 고민해볼게여!
image

}

private boolean isNullOrEmpty(String formula) {
return formula == null || formula.isEmpty();
}

}
46 changes: 46 additions & 0 deletions src/main/java/support/Splitter.java
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
Copy link
Member

Choose a reason for hiding this comment

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

헉 구분자가 숫자가 되는 경우에 대해서도 유효성을 잡아줬구나
난 생각치도 못했어..!

}

}
52 changes: 52 additions & 0 deletions src/test/java/domain/CalculatorTest.java
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))
);
}

}
42 changes: 42 additions & 0 deletions src/test/java/domain/dto/OperandTest.java
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"));
}


}
70 changes: 70 additions & 0 deletions src/test/java/service/CalculatorServiceTest.java
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("올바르지 않은 수식 테스트")
@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);
}

}
Loading