Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
823468f
init : 프로젝트 초기화
heenahan Jun 5, 2023
9f99b95
feat : 중위 표기식을 후위 표기식으로 변환해서 계산
heenahan Jun 9, 2023
c78db4c
test : 사칙 연산 테스트
heenahan Jun 9, 2023
4e840f1
test : 후위표기식 변환 후 계산 테스트
heenahan Jun 9, 2023
27c0031
feat : 연산 결과 맵에 저장
heenahan Jun 9, 2023
bb2ba06
feat : 문자열 연산을 리스트 연산으로 변환
heenahan Jun 9, 2023
104c54b
test : 저장소, 문자열 연산을 리스트 연산으로 변환 테스트
heenahan Jun 9, 2023
91ae6e6
chore : 출력문 삭제
heenahan Jun 10, 2023
dd89153
feat : 유효하지 않은 입력값은 예외 던짐
heenahan Jun 10, 2023
73a262d
feat : DB은 불변해야 하므로 final
heenahan Jun 10, 2023
e4a7702
test : 잘못된 수식이 입력되는 예외 테스트
heenahan Jun 10, 2023
88acf06
feat : 입력과 출력 추가
heenahan Jun 10, 2023
46aed93
feat : 컨트롤러 추가
heenahan Jun 10, 2023
ef913ec
docs : gradle 파일 추가
heenahan Jun 15, 2023
679a107
refactor : 우선순위 enum에서 관리 및 enum 안에서 사칙 연산 수행
heenahan Jun 15, 2023
b00a4fb
refactor : 자료구조 변경 stack -> deque
heenahan Jun 15, 2023
9a70dfa
feat : custom exception 만듦
heenahan Jun 15, 2023
0abe54e
refactor : Operator를 ParameterizedTest를 사용해 여러 개를 테스트 및 Calculator 메서…
heenahan Jun 15, 2023
fa9691b
feat : 동일한 수식도 저장 및 시간 순으로 정렬한 리스트 반환
heenahan Jun 15, 2023
91ee3a9
feat : 자주 사용되는 정규식 패턴 미리 컴파일 후 사용
heenahan Jun 15, 2023
a8161ac
feat : 메뉴 enum에서 관리
heenahan Jun 15, 2023
6b41677
feat : 서비스 레이어로 분리
heenahan Jun 15, 2023
f0aff99
refactor : 컨트롤러 레이어 리팩토링
heenahan Jun 15, 2023
5e74365
refactor : 애플리케이션 클래스 이름 변경
heenahan Jun 15, 2023
752d38d
chore : import문 정리
heenahan Jun 15, 2023
d2e0f24
refactor : 후위 표현식 변환과 계산 분리
heenahan Jun 17, 2023
26995f4
refactor : Formula가 스스로 계산하도록 변경
heenahan Jun 17, 2023
8984cd5
feat : 저장소 List로 자료구조 변경
heenahan Jun 17, 2023
6ce753c
refactor : 저장과 계산 테스트 코드 변경
heenahan Jun 17, 2023
3a2acbf
refactor : 메뉴 번호 String으로 변경
heenahan Jun 17, 2023
8ec3a90
refactor : 서비스 레이어 리팩토링
heenahan Jun 17, 2023
d59e1c0
refactor : 컨트롤러 레이어 리팩토링
heenahan Jun 17, 2023
b813f7c
fix : 마이너스 부호 입력 시 유효하지 않는 입력 예외 발생
heenahan Jun 17, 2023
f781b5f
refactor :인자로 Formula를 받음
heenahan Jun 19, 2023
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
218 changes: 218 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
### Java template
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Java template
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Gradle template
.gradle
**/build/
!src/**/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties

15 changes: 15 additions & 0 deletions app/src/main/java/com/programmers/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package com.programmers;

import com.programmers.controller.CalculatorController;

public class App {
Copy link

Choose a reason for hiding this comment

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

아주 사소한 의견입니다만,
CalculatorApplication 과 같은 구체 네이밍은 어떨까요?


public static void main(String[] args) {
CalculatorController calculatorController = new CalculatorController();
calculatorController.run();
}

}
63 changes: 63 additions & 0 deletions app/src/main/java/com/programmers/calculator/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.programmers.calculator;

import java.util.*;
Copy link

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

아 ide에서 자동으로 와일드카드로 변경되지 못하도록 바꾸겠습니다!!


public class Calculator {

public List<String> changeInfixToPostfix(List<String> infix) {
Copy link

Choose a reason for hiding this comment

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

여기서 List infix를 받는게 아니라 Expression을 받아서 처리하는 방향에 대해서 어떻게 생각하시나요 ?

Copy link
Author

@heenahan heenahan Jun 19, 2023

Choose a reason for hiding this comment

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

수식을 클래스로 감쌌으니 해당 클래스를 파라미터로 넘겨주는게 좋다는 말씀이시죠?? 그렇게 한다면 메시지가 분명해져 좋을 것 같아요..!!

List<String> postfix = new ArrayList<>();
Stack<String> stack = new Stack<>();
Copy link

Choose a reason for hiding this comment

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


for (String s : infix) {
if (s.matches("\\d+")) { // 숫자일 경우
postfix.add(s);
} else { // 숫자 아닐 경우
int priority = getPriority(s);
// 스택이 비었거나
while (!stack.isEmpty()) {
String topOperator = stack.peek();
int topPriority = getPriority(topOperator);

// top의 우선 순위가 높거나 같을 경우
if (topPriority >= priority) postfix.add(stack.pop());
// top의 우선 순위가 낮을 경우
else break;
}
stack.push(s); // 연산자 넣음
}
}

// 스택에 남아있는 연산자 모두 집어 넣음
while (!stack.isEmpty()) postfix.add(stack.pop());

return postfix;
}

public Integer calcPostfix(List<String> postfix) {
Copy link

Choose a reason for hiding this comment

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

calcPostfix 보다 풀네이밍을 사용하는건 어떨까요?
축약어는 배경지식에 따라 해석이 다르게 되기도해요.

Stack<Integer> stack = new Stack<>();

for (String s : postfix) {
// 숫자일 경우 스택에 넣음
if (s.matches("\\d+")) stack.push(Integer.parseInt(s));

Choose a reason for hiding this comment

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

숫자인지 판단하는 로직을 메서드로 추출하면 좀 더 읽기 편해질 것 같아요. 또한, 판단하는 정규식을 상수로 만들어줘도 좋구요.
추가로 matches()의 내부 구현을 보면 Pattern을 계속 생성하기 때문에 비효율적이게 됩니다.
미리 Pattern 객체를 만들어놓고 재사용하면 어떨까요??

참고 자료 : Effective Java 아이템 6. 불필요한 객체 생성을 피하라

else { // 연산자 일 경우
Integer op1 = stack.pop();
Integer op2 = stack.pop();

Operator calc = Operator.of(s);
Integer result = calc.getFunc().apply(op2, op1);

Choose a reason for hiding this comment

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

get을 사용해서 다시 꺼낼 필요없이 calc.doCalculate(op2, op1);처럼
해당 객체에게 메시지를 보내는 방식으로 바꾸면 좋을 것 같아요.
굳이 내부 구현을 외부에 노출해줄 필요는 없어보입니다.


stack.push(result);
}
}

return stack.pop();
}

public Integer getPriority(String operator) {
// * 와 / 는 우선순위 높음
if (operator.equals("*") || operator.equals("/")) return 1;
// 그 외 낮음
return -1;
}

Choose a reason for hiding this comment

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

Operator라는 객체를 통해 상태와 행위를 모아주신 점 아주 좋습니다. 💯
다만 제 생각에 우선순위도 하나의 상태라고 생각할 수 있을 것 같은데요.
Operator에게 책임을 주는 것 어떠실까요~?

}
42 changes: 42 additions & 0 deletions app/src/main/java/com/programmers/calculator/Operator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.programmers.calculator;

import java.util.Arrays;
import java.util.function.BiFunction;

public enum Operator {

PLUS("+", (a, b) -> { return a + b; }),
MINUS("-", (a, b) -> { return a - b; }),
DIVIDE("/", (a, b) -> {
try {
return a / b;
} catch (ArithmeticException e) {
throw e;
}
}),
MUTIPLY("*", (a, b) -> { return a * b; });

Choose a reason for hiding this comment

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

현재 예외가 발생하면 잡아서 다시 throw를 해주고 있는데요.
굳이 똑같은 예외를 발생시킬거면 잡을 필요가 있을까요?
어떤 목적으로 구현하셨는지 궁금합니다.

Copy link
Author

Choose a reason for hiding this comment

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

멘토님 말씀을 보고 의미가 없는 코드라고 생각해 ArithmeticException이 발생하면 custom exception인 DivisionByZeroException에 메시지를 담아 던지도록 바꾸었습니다..!!


private String operator;
private BiFunction<Integer, Integer, Integer> func;

Operator(String operator, BiFunction<Integer, Integer, Integer> func) {
this.operator = operator;
this.func = func;
}

public String getOperator() {
return operator;
}

public BiFunction<Integer, Integer, Integer> getFunc() {
return func;
}

public static Operator of(String operator) {
return Arrays.stream(Operator.values())
.filter(o -> o.getOperator().equals(operator))
.findFirst()
.orElseThrow(NullPointerException::new);
Copy link

Choose a reason for hiding this comment

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

Suggested change
return Arrays.stream(Operator.values())
.filter(o -> o.getOperator().equals(operator))
.findFirst()
.orElseThrow(NullPointerException::new);
return Arrays.stream(Operator.values())
.filter(o -> o.operator.equals(operator))
.findFirst()
.orElseThrow(NullPointerException::new);

로 사용해도 괜찮지 않을까요 ? getOperator()가 없어도 operator에 접근할 수 있으니까요.
추가적으로 operator를 찾지못했을 때 NullPointerException 보단 다른 exception이 좋지않을까 싶습니다. (@가 들어올 경우에 NullPointerException이 적절한가? 의 관점에서)

Copy link
Author

Choose a reason for hiding this comment

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

그러네요 바로 operator에 접근할 수 있네요!! 그리고 NotFoundOperatorException이라는 custom exception을 만들어 직관적으로 예외 상황을 나타냈습니다

}

}
Loading