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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 다이어그램
<img width="838" alt="image" src="https://user-images.githubusercontent.com/77106988/215942113-a09c8587-8ca6-431b-9552-4c974f11ceec.png">

## 구현 기능
- [X] 입력을 받았을 경우 사칙연산(+, -, *, /)이 가능해야 한다.

- [ ] 숫자에 소수를 적용해본다.

- [ ] 사칙연산 우선순위를 적용한다.

## 깃허브

**[java-calculator](https://github.com/programmers-lecture/java-calculator)**
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/stringCalculator/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package stringCalculator;

import java.util.regex.Pattern;

public class Calculator {

private static final Pattern regExp = Pattern.compile("^[0-9]*$"); //operand, operator 구분 용도
Copy link
Contributor

Choose a reason for hiding this comment

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

👍


public int calculate(String[] inputExp){

int result = 0;
// 맨처음 숫자 더해짐
Copy link
Contributor

Choose a reason for hiding this comment

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

공부의 목적으로 주석이 좋지만, 코드를 이용해 의도를 나타내는게 중요한듯 합니다.


코드로 의도를 표현하라!
// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if((employee.flags & HOURLY_FLAG) && (employee.age > 65))
주석도 필요없이 함수 이름만으로 충분히 깔끔하게 표현되었다.
if (employee.isEligibleForFullBenefits())

이런식으로 말이죠 ㅎㅎ

Operator currentOperator = Operator.PLUS;

for(String input : inputExp){
if(regExp.matcher(input).find()){
result = currentOperator.operate(result, Integer.parseInt(input));
continue;
}
// 연산자 매칭
for(Operator operator : Operator.values()){
if(operator.getSymbol().equals(input)){
currentOperator = operator;
}
Comment on lines +21 to +24
Copy link
Contributor

Choose a reason for hiding this comment

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

연산자 매칭을 values를 stream을 돌면서 findFirst 메서드를 이용해 해보면 어떨까요?

}
}

return result;
}
}
19 changes: 19 additions & 0 deletions src/main/java/stringCalculator/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package stringCalculator;

import java.util.Scanner;

public class Main {

public static void main(String[] args) {
Calculator calculator = new Calculator();
String[] input = receiveInput();
int result = calculator.calculate(input);
System.out.println("결과 : " + result);
}

public static String[] receiveInput() {
Copy link

Choose a reason for hiding this comment

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

프로그램 실행 시 을 입력 받는 기능을 메서드로 분리 하셨군요. 좋습니다.!

몇가지 고민해볼 만한 것들이 있는 것 같습니다.
만약에 지속적으로 재시도가 가능해야 한다면? 입력 방식에 대한 규칙이 생겨서 유효성 검사 같은 것을 해야 한다면?

이미 작성되어 있는 코드를 수정하지 않고 추가적인 요구사항에 대해서 추가 및 수정이 가능하도록
기존 클래스에서 분리하여 입력과 관련된 책임을 갖는 클래스로 분리해보시는 것은 어떠하신지요?

Scanner sc = new Scanner(System.in);
String input = sc.nextLine().replaceAll("\\s+", " ");
return input.split(" ");
}
}
26 changes: 26 additions & 0 deletions src/main/java/stringCalculator/Operator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package stringCalculator;

import java.util.function.BiFunction;

public enum Operator {
Copy link

Choose a reason for hiding this comment

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

Operator 같은 정의된 속성과 행위를 함께 관리하고 있는 클래스를 구현했다면
테스트 코드는 필히 들어가야 할 것 같습니다. 2주차 내용이 테스트 코드이니

build.gradle 파일 내에 아래 의존성 추가하신 후에 코드를 실행해보시는 것을 권장합니다.

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
    testImplementation 'org.assertj:assertj-core:3.20.2'
}
class OperatorTest {
	
	private Stream<Arguments> operatorProvider() {
		return Stream.of(
			Arguments.of("+", Operator.PLUS),
			Arguments.of("-", Operator.MINUS),
			Arguments.of("*", Operator.MULTIPLY),
			Arguments.of("/", Operator.DIVIDE)
		);
	}
	
	@DisplayName("연산자 확인 테스트")
	@MethodSource("operatorProvider")
	@Test
	void testCase1(String actual, Operator expected) {
		Operator operator = Operator.findSymbol(actual);
		assertThat(operator).isEqualTo(Operator.PLUS);
	}
	
}

연산자를 찾는 검증 테스트를 수행했으니 연산자와 피연산자를 전달하여 연산처리하는 테스트도 작성해보실 수도 있을 것 같네요. 한번 시도해보시죠!!

PLUS("+", (first, second) -> first + second),
MINUS("-", (first, second) -> first - second),
MULTIPLY("*", (first, second) -> first * second),
DIVIDE("/", (first, second) -> first / second);
Copy link

Choose a reason for hiding this comment

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

Suggested change
DIVIDE("/", (first, second) -> first / second);
DIVIDE("/", (first, second) -> {
if(second == 0){
throw new IllegalArgumentException("0으로 나눌 수 없습니다.");
}
return first / second;
});

나누기는 예외가 발생할 수 있는 케이스가 존재하는 것 같습니다.!


private String symbol;
private BiFunction<Integer, Integer, Integer> operation;

Operator(String symbol, BiFunction<Integer, Integer, Integer> operation) {
Copy link

Choose a reason for hiding this comment

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

함수형 인터페이스를 사용할 때 몇 가지 고민해볼 수 있는 내용이 있습니다.

  1. 표준 함수형 인터페이스를 사용한다.
  2. 표준 함수형 인터페이스에서도 제네릭의 타입이 모두 같은 경우 이를 하나로 관리할 수 있는 인터페이스를 사용한다.(BinaryOperator)
  3. primitive 타입의 wrapper 클래스인 Integer 타입은 형변환의 효율을 높일 수 있는 인터페이스가 존재한다.(IntBinaryOperator)
  4. 인터페이스의 명칭이라던가 메서드를 보다 명시적으로 관리하기 위해 @FunctionalInterface 어노테이션을 사용하여 사용자 정의 인터페이스를 작성한다.

this.symbol = symbol;
this.operation = operation;
}

public String getSymbol(){ return symbol; }
Copy link

@SeokRae SeokRae Feb 1, 2023

Choose a reason for hiding this comment

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

Suggested change
public String getSymbol(){ return symbol; }
public static Operator findSymbol(String input) {
return Arrays.stream(values())
.filter(operator -> operator.symbol.equals(input))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("연산자가 아닙니다."));
}

현재 Operator 에 정의된 내용을 이 Operator를 사용하는 클라이언트 클래스에서 가져다가 로직을 만드는게 아니라
내부적으로 연산자를 찾아서 반환하는 코드를 작성하면 밖에서 반복문 및 조건문을 더 줄일 수 있을 것 같네요.!


public int operate(int first, int second) {
return operation.apply(first, second);
}


}