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

[찬인] 문자열 덧셈 계산기 #11

Open
wants to merge 8 commits into
base: pci
Choose a base branch
from
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
23 changes: 23 additions & 0 deletions src/main/java/calculator/domain/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package calculator.domain;

import calculator.domain.splitter.SplitterGroup;

public class Calculator {

private Calculator() {
}

public static int calculate(String expression) {
Copy link

Choose a reason for hiding this comment

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

박찬인씨의 정적클래스 선택기준이 궁금합니다

Copy link
Member Author

Choose a reason for hiding this comment

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

멤버변수에 대한 의존성이 없어서..?
SplitStrategy는 인터페이스로 작성해야 하니까 그렇게 할수 없었구..
static으로 작성하려면 default 메서드가 되야했으니까..!!

Copy link

Choose a reason for hiding this comment

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

이펙티브자바 140 페이지를 참고하여 기본생성자를 private 으로 명시하고 인스턴스화를 막아보는건 어떨까?

Copy link
Member Author

Choose a reason for hiding this comment

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

와! 놓쳤던 부분 감사합니당

if (isEmpty(expression)) {
return 0;
}
SplitterGroup strategyByExpression = SplitterGroup.findStrategyByExpression(expression);
PositiveNumbers positiveNumbers = strategyByExpression.split(expression);

return positiveNumbers.sum();
}

private static boolean isEmpty(String expression) {
return expression == null || expression.trim().isEmpty();
}
}
47 changes: 47 additions & 0 deletions src/main/java/calculator/domain/PositiveNumbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package calculator.domain;

import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class PositiveNumbers {
private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+)");
private final List<Integer> numbers;

public PositiveNumbers(List<String> numbers) {
validate(numbers);
this.numbers = numbers.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
}

private void validate(List<String> numbers) {
boolean canParse = numbers.stream()
.map(NUMBER_PATTERN::matcher)
.allMatch(Matcher::matches);

if (!canParse) {
throw new IllegalArgumentException(numbers + "양수가 아닌 입력값이 존재합니다.");
}
}

public int sum() {
return numbers.stream()
.reduce(0, Integer::sum);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PositiveNumbers positiveNumbers = (PositiveNumbers) o;
return Objects.equals(numbers, positiveNumbers.numbers);
}

@Override
public int hashCode() {
return Objects.hash(numbers);
}
}
28 changes: 28 additions & 0 deletions src/main/java/calculator/domain/splitter/CustomSplitStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package calculator.domain.splitter;

import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class CustomSplitStrategy implements SplitStrategy {
private static final Pattern CUSTOM_PATTERN = Pattern.compile("//(.)\n(.*)");

@Override
public boolean support(String expression) {
return CUSTOM_PATTERN.matcher(expression)
.find();
}

@Override
public List<String> split(String expression) {
Matcher matcher = CUSTOM_PATTERN.matcher(expression);
matcher.find();

String customDelimiter = matcher.group(1);
String target = matcher.group(2);
return Arrays.stream(target.split(customDelimiter))
.collect(Collectors.toList());
}
}
27 changes: 27 additions & 0 deletions src/main/java/calculator/domain/splitter/DefaultSplitStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package calculator.domain.splitter;

import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class DefaultSplitStrategy implements SplitStrategy {
private static final Pattern DEFAULT_PATTERN = Pattern.compile("(.*)");
private static final String COMMA_AND_SEMICOLON = "[,:]";

@Override
public boolean support(String expression) {
return DEFAULT_PATTERN.matcher(expression)
.find();
}

@Override
public List<String> split(String expression) {
Matcher matcher = DEFAULT_PATTERN.matcher(expression);
matcher.find();
String target = matcher.group(0);
return Arrays.stream(target.split(COMMA_AND_SEMICOLON))
.collect(Collectors.toList());
}
}
9 changes: 9 additions & 0 deletions src/main/java/calculator/domain/splitter/SplitStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package calculator.domain.splitter;

import java.util.List;

public interface SplitStrategy {
Copy link

Choose a reason for hiding this comment

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

오와ㅣㅇ 이렇게 분리하는거 왜 생각못했지 ; .; 굿굿

Copy link
Member Author

Choose a reason for hiding this comment

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

당근이 제법 달군요..

boolean support(String expression);

List<String> split(String expression);
}
27 changes: 27 additions & 0 deletions src/main/java/calculator/domain/splitter/SplitterGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package calculator.domain.splitter;

import calculator.domain.PositiveNumbers;

import java.util.Arrays;

public enum SplitterGroup {
CUSTOM(new CustomSplitStrategy()),
DEFAULT(new DefaultSplitStrategy());

private final SplitStrategy splitStrategy;

SplitterGroup(SplitStrategy splitStrategy) {
this.splitStrategy = splitStrategy;
}

public static SplitterGroup findStrategyByExpression(String expression) {
return Arrays.stream(values())
.filter(aSplitter -> aSplitter.splitStrategy.support(expression))
.findFirst()
.orElseThrow(AssertionError::new);
}

public PositiveNumbers split(String expression) {
return new PositiveNumbers(this.splitStrategy.split(expression));
}
}
Empty file removed src/main/java/calculator/empty.txt
Empty file.
40 changes: 40 additions & 0 deletions src/test/java/calculator/domain/CalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package calculator.domain;

import org.junit.jupiter.api.DisplayName;
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.Assertions.assertThat;

class CalculatorTest {

private static Stream<Arguments> expressionProvider() {
return Stream.of(
Arguments.of("1,2:3", 6),
Arguments.of("//#\n1#4#10", 15),
Arguments.of("1", 1)
);
}

@DisplayName("null 또는 공백을 입력할 경우 0을 반환")
@ParameterizedTest
@NullAndEmptySource
Copy link

Choose a reason for hiding this comment

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

오? null 이랑 "" 두개 검사하는건가

Copy link
Member Author

Choose a reason for hiding this comment

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

넴 Stringd에 두 가지가 들어가지요!

void calculateNullAndEmpty(String expression) {
int expect = Calculator.calculate(expression);

assertThat(expect).isEqualTo(0);
}

@DisplayName("입력식에 따른 결과 반환")
@ParameterizedTest
@MethodSource("expressionProvider")
void calculate(String expression, int result) {
int expect = Calculator.calculate(expression);

assertThat(expect).isEqualTo(result);
}
}
47 changes: 47 additions & 0 deletions src/test/java/calculator/domain/PositiveNumbersTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package calculator.domain;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class PositiveNumbersTest {

@DisplayName("음수 입력시 exception 발생")
@Test
void negativeNumber() {
assertThatThrownBy(() -> new PositiveNumbers(Arrays.asList("1", "22", "-1")))
.isInstanceOf(IllegalArgumentException.class);
}

@DisplayName("숫자가 아닌 문자를 입력받으면 exception 발생")
@Test
void parseException() {
assertThatThrownBy(() -> new PositiveNumbers(Arrays.asList("1", "22", "2;")))
.isInstanceOf(IllegalArgumentException.class);
}

@DisplayName("입력받은 값으로 합을 출력한다.")
@Test
void sum() {
//given
PositiveNumbers positiveNumbers = new PositiveNumbers(Arrays.asList("1", "2", "3"));

//when
int sum = positiveNumbers.sum();

//then
assertThat(sum).isEqualTo(6);
}

@DisplayName("숫자 일급 컬렉션 생성 테스트")
@Test
void equals() {
PositiveNumbers positiveNumbers = new PositiveNumbers(Arrays.asList("1", "2", "3"));

assertThat(positiveNumbers).isEqualTo(new PositiveNumbers(Arrays.asList("1", "2", "3")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package calculator.domain.splitter;

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.List;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;

class CustomSplitStrategyTest {

private static Stream<Arguments> customSupportProvider() {
return Stream.of(
Arguments.of("//?\n1?2?3", true),
Arguments.of("1?2?3", false)
);
}

@DisplayName("커스텀식을 나타내는 (// \\n)이 존재해야 지원한다.")
@ParameterizedTest
@MethodSource("customSupportProvider")
void support(String expression, boolean result) {
//given
CustomSplitStrategy customSplitStrategy = new CustomSplitStrategy();

//when
boolean expect = customSplitStrategy.support(expression);

//then
assertThat(expect).isEqualTo(result);
}

@DisplayName("커스텀식은 커스텀 구분자로 구분한다.")
@Test
void split() {
//given
CustomSplitStrategy customSplitStrategy = new CustomSplitStrategy();
String expression = "//#\n1#2#3";

//when
List<String> expect = customSplitStrategy.split(expression);

//then
assertThat(expect).containsExactly("1", "2", "3");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package calculator.domain.splitter;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class DefaultSplitStrategyTest {

@DisplayName("기본전략은 모든 식을 수용한다.")
@ParameterizedTest
@CsvSource(value = {
"1,2:3",
"1",
"1 2"}, delimiter = '|')
void support(String expression) {
//given
DefaultSplitStrategy defaultSplitStrategy = new DefaultSplitStrategy();

//then
assertThat(defaultSplitStrategy.support(expression)).isTrue();
}

@DisplayName("기본전략은 연산자로 ,와 :를 이용한여 식을 분리한다.")
@ParameterizedTest
@CsvSource(value = {
"1,2,3",
"1:2:3",
"1,2:3",
}, delimiter = '|')
void split(String expression) {
//given
DefaultSplitStrategy defaultSplitStrategy = new DefaultSplitStrategy();

//when
List<String> split = defaultSplitStrategy.split(expression);

//then
assertThat(split).containsExactly("1", "2", "3");
}

}
Loading