-
Notifications
You must be signed in to change notification settings - Fork 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
[찬인] 문자열 덧셈 계산기 #11
base: pci
Are you sure you want to change the base?
[찬인] 문자열 덧셈 계산기 #11
Changes from all commits
f524d9d
cb6cd6a
d288c45
44720b4
d9466e8
e8c9925
c5666dd
6cb55d4
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,23 @@ | ||
package calculator.domain; | ||
|
||
import calculator.domain.splitter.SplitterGroup; | ||
|
||
public class Calculator { | ||
|
||
private Calculator() { | ||
} | ||
|
||
public static int calculate(String expression) { | ||
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(); | ||
} | ||
} |
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); | ||
} | ||
} |
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()); | ||
} | ||
} |
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()); | ||
} | ||
} |
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 { | ||
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. 오와ㅣㅇ 이렇게 분리하는거 왜 생각못했지 ; .; 굿굿 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. 당근이 제법 달군요.. |
||
boolean support(String expression); | ||
|
||
List<String> split(String expression); | ||
} |
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)); | ||
} | ||
} |
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 | ||
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. 오? null 이랑 "" 두개 검사하는건가 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. 넴 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); | ||
} | ||
} |
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"); | ||
} | ||
|
||
} |
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.
박찬인씨의 정적클래스 선택기준이 궁금합니다
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.
멤버변수에 대한 의존성이 없어서..?
SplitStrategy
는 인터페이스로 작성해야 하니까 그렇게 할수 없었구..static
으로 작성하려면default
메서드가 되야했으니까..!!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.
이펙티브자바 140 페이지를 참고하여 기본생성자를 private 으로 명시하고 인스턴스화를 막아보는건 어떨까?
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.
와! 놓쳤던 부분 감사합니당