diff --git a/src/main/java/com/programmers/App.java b/src/main/java/com/programmers/App.java index 3b2e00b5d..bb538a331 100644 --- a/src/main/java/com/programmers/App.java +++ b/src/main/java/com/programmers/App.java @@ -1,14 +1,15 @@ package com.programmers; import com.programmers.engine.JavaCalculator; -import com.programmers.engine.model.Result; +import com.programmers.engine.model.ResultManager; +import com.programmers.engine.module.BasicCalculator; public class App { public static void main(String[] args) { Console console = new Console(); - Result result = new Result(); + ResultManager resultManager = new ResultManager(); BasicCalculator bc = new BasicCalculator(); - new JavaCalculator(console, console, result, bc).run(); + new JavaCalculator(console, console, resultManager, bc).run(); } } diff --git a/src/main/java/com/programmers/BasicCalculator.java b/src/main/java/com/programmers/BasicCalculator.java deleted file mode 100644 index 4bb03b5df..000000000 --- a/src/main/java/com/programmers/BasicCalculator.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.programmers; - -import java.util.ArrayList; -import java.util.Stack; - -public class BasicCalculator { - public String doCalculate(String expression) { - int answer = toPostfix(expression); - System.out.println(answer); - return expression + " = " + String.valueOf(answer); - } - - private int getPriority(String operator) { - if (operator.equals("(")) return 0; - else if (operator.equals("+") || operator.equals("-")) return 1; - else return 2; - } - - private int toPostfix(String str) { - String[] strArr = str.split(" "); - ArrayList output = new ArrayList<>(); - Stack st = new Stack<>(); - - for (int i = 0; i < strArr.length; i++) { - String now = strArr[i]; - switch (now) { - case "(" -> st.push(now); - case ")" -> { - while (!st.isEmpty()) { - if (st.peek().equals("(")) { - st.pop(); - break; - } - output.add(st.pop()); - } - } - case "+", "-", "*", "/" -> { - while (!st.isEmpty() && getPriority(st.peek()) >= getPriority(now)) { - output.add(st.pop()); - } - st.push(now); - } - default -> output.add(now); - } - } - while (!st.isEmpty()) output.add(st.pop()); - return getResult(output); - } - - public int getResult(ArrayList list) { - Stack st = new Stack<>(); - for (int i = 0; i < list.size(); i++) { - String now = list.get(i); - - if (!now.equals("+") && !now.equals("-") && !now.equals("*") && !now.equals("/")){ - st.push(Integer.parseInt(now)); - } else { - int x = st.pop(); - int y = st.pop(); - switch (now) { - case "+" -> st.push(y + x); - case "-" -> st.push(y - x); - case "*" -> st.push(y * x); - case "/" -> st.push(y / x); - } - } - } - return st.pop(); - } -} diff --git a/src/main/java/com/programmers/Console.java b/src/main/java/com/programmers/Console.java index bf162d8bb..3d3feefb4 100644 --- a/src/main/java/com/programmers/Console.java +++ b/src/main/java/com/programmers/Console.java @@ -3,6 +3,7 @@ import com.programmers.engine.io.Input; import com.programmers.engine.io.Output; +import java.util.Map; import java.util.Scanner; public class Console implements Input, Output { @@ -28,4 +29,20 @@ public String getExpression() { System.out.println("계산하고자 하는 식을 입력해주세요!"); return sc.nextLine(); } + + @Override + public void readAllResults(Map map) { + if (map.isEmpty()) { + System.out.println("계산 이력이 없습니다."); + return; + } + System.out.println(); + map.forEach((key, value) -> System.out.println(value)); + System.out.println(); + } + + @Override + public void printAnswer(int answer) { + System.out.println(answer); + } } diff --git a/src/main/java/com/programmers/engine/JavaCalculator.java b/src/main/java/com/programmers/engine/JavaCalculator.java index 3a54b7b1a..ff4ccfa15 100644 --- a/src/main/java/com/programmers/engine/JavaCalculator.java +++ b/src/main/java/com/programmers/engine/JavaCalculator.java @@ -1,29 +1,40 @@ package com.programmers.engine; -import com.programmers.BasicCalculator; +import com.programmers.engine.module.BasicCalculator; import com.programmers.engine.io.Input; import com.programmers.engine.io.Output; -import com.programmers.engine.model.Result; +import com.programmers.engine.model.Menu; +import com.programmers.engine.model.ResultManager; import lombok.AllArgsConstructor; @AllArgsConstructor public class JavaCalculator implements Runnable{ private final Input input; private final Output output; - private final Result result; + private final ResultManager resultManager; private final BasicCalculator bc; @Override public void run() { - boolean isExecutable = true; - while (isExecutable) { + while (true) { output.showMenu(); - switch (input.selectMenu()) { - case 1 -> result.readAllResults(); - case 2 -> result.save(bc.doCalculate(input.getExpression())); - case 3 -> isExecutable = false; + Menu menu = Menu.matchMenu(input.selectMenu()); + + switch (menu) { + case LOOK_UP -> output.readAllResults(resultManager.readAllResults()); + case CALCULATE -> calculate(); + case EXIT -> { + return; + } default -> output.inputError(); } } } + + private void calculate() { + String expression = input.getExpression(); + int answer = bc.doCalculate(expression); + output.printAnswer(answer); + resultManager.save(expression, answer); + } } diff --git a/src/main/java/com/programmers/engine/io/Output.java b/src/main/java/com/programmers/engine/io/Output.java index 9b1a0303b..3d280928a 100644 --- a/src/main/java/com/programmers/engine/io/Output.java +++ b/src/main/java/com/programmers/engine/io/Output.java @@ -1,6 +1,10 @@ package com.programmers.engine.io; +import java.util.Map; + public interface Output { void showMenu(); void inputError(); + void readAllResults(Map map); + void printAnswer(int answer); } diff --git a/src/main/java/com/programmers/engine/model/Menu.java b/src/main/java/com/programmers/engine/model/Menu.java new file mode 100644 index 000000000..c13bb2901 --- /dev/null +++ b/src/main/java/com/programmers/engine/model/Menu.java @@ -0,0 +1,22 @@ +package com.programmers.engine.model; + +import java.util.Arrays; + +public enum Menu { + LOOK_UP("1"), + CALCULATE("2"), + EXIT("3"); + + private final String menu; + + Menu(String menu) { + this.menu = menu; + } + + public static Menu matchMenu(int number) { + return Arrays.stream(values()) + .filter(v -> number == Integer.parseInt(v.menu)) + .findFirst() + .orElseThrow(IllegalStateException::new); + } +} diff --git a/src/main/java/com/programmers/engine/model/Operator.java b/src/main/java/com/programmers/engine/model/Operator.java new file mode 100644 index 000000000..6b5dccef1 --- /dev/null +++ b/src/main/java/com/programmers/engine/model/Operator.java @@ -0,0 +1,35 @@ +package com.programmers.engine.model; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.function.BiFunction; + +@Getter +public enum Operator { + ADD("+", 1, (a, b) -> a + b), + SUBTRACTION("-", 1, (a, b) -> a - b), + MULTIPLY("*", 2, (a, b) -> a * b), + DIVIDE("/", 2, (a, b) -> b / a); + + private final String symbol; + private final int priority; + private final BiFunction action; + + Operator(String symbol, int priority, BiFunction action) { + this.symbol = symbol; + this.priority = priority; + this.action = action; + } + + public static Operator matchOperator(String operator) { + return Arrays.stream(values()) + .filter(v -> operator.equals(v.symbol)) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + + public int calculate(int a, int b) { + return action.apply(a, b); + } +} diff --git a/src/main/java/com/programmers/engine/model/Result.java b/src/main/java/com/programmers/engine/model/Result.java deleted file mode 100644 index 334b90c02..000000000 --- a/src/main/java/com/programmers/engine/model/Result.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.programmers.engine.model; - -import java.util.HashMap; -import java.util.Map; - -public class Result { - private final Map results = new HashMap<>(); - - public void save(String result) { - results.put(results.size() + 1, result); - } - - public void readAllResults() { - if (results.size() == 0) { - System.out.println("계산 이력이 없습니다."); - return; - } - System.out.println(); - results.forEach((key, value) -> System.out.println(value)); - System.out.println();; - } -} diff --git a/src/main/java/com/programmers/engine/model/ResultManager.java b/src/main/java/com/programmers/engine/model/ResultManager.java new file mode 100644 index 000000000..3785802b5 --- /dev/null +++ b/src/main/java/com/programmers/engine/model/ResultManager.java @@ -0,0 +1,17 @@ +package com.programmers.engine.model; + +import java.util.HashMap; +import java.util.Map; + +public class ResultManager { + private final Map results = new HashMap<>(); + private int key = 0; + + public void save(String expression, int answer) { + results.put(++key, expression + " = " + String.valueOf(answer)); + } + + public Map readAllResults() { + return results; + } +} diff --git a/src/main/java/com/programmers/engine/module/BasicCalculator.java b/src/main/java/com/programmers/engine/module/BasicCalculator.java new file mode 100644 index 000000000..cd202a395 --- /dev/null +++ b/src/main/java/com/programmers/engine/module/BasicCalculator.java @@ -0,0 +1,16 @@ +package com.programmers.engine.module; + +import com.programmers.engine.module.convert.AnswerConverter; +import com.programmers.engine.module.convert.PostfixConverter; +import java.util.List; + +public class BasicCalculator { + private final PostfixConverter postfixConverter = new PostfixConverter(); + private final AnswerConverter answerConverter = new AnswerConverter(); + + public int doCalculate(String expression) { + List postfixList = postfixConverter.convertInfixToPostfix(expression); + return answerConverter.convertPostfixToAnswer(postfixList); + } + +} diff --git a/src/main/java/com/programmers/engine/module/convert/AnswerConverter.java b/src/main/java/com/programmers/engine/module/convert/AnswerConverter.java new file mode 100644 index 000000000..d8543f478 --- /dev/null +++ b/src/main/java/com/programmers/engine/module/convert/AnswerConverter.java @@ -0,0 +1,23 @@ +package com.programmers.engine.module.convert; + +import com.programmers.engine.model.Operator; + +import java.util.List; +import java.util.Stack; + +public class AnswerConverter { + public int convertPostfixToAnswer(List list) { + Stack st = new Stack<>(); + for (String now : list) { + if (!now.equals("+") && !now.equals("-") && !now.equals("*") && !now.equals("/")) { + st.push(Integer.parseInt(now)); + continue; + } + Operator operator = Operator.matchOperator(now); + int x = st.pop(); + int y = st.pop(); + st.push(operator.calculate(x, y)); + } + return st.pop(); + } +} diff --git a/src/main/java/com/programmers/engine/module/convert/PostfixConverter.java b/src/main/java/com/programmers/engine/module/convert/PostfixConverter.java new file mode 100644 index 000000000..a34d9e40b --- /dev/null +++ b/src/main/java/com/programmers/engine/module/convert/PostfixConverter.java @@ -0,0 +1,28 @@ +package com.programmers.engine.module.convert; + +import com.programmers.engine.model.Operator; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class PostfixConverter { + + public List convertInfixToPostfix(String expression) { + String[] strArr = expression.split(" "); + List output = new ArrayList<>(); + Stack st = new Stack<>(); + + for (String now: strArr) { + if (now.equals("+") || now.equals("-") || now.equals("*") || now.equals("/")) { + while (!st.isEmpty() && Operator.matchOperator(st.peek()).getPriority() >= Operator.matchOperator(now).getPriority()) { + output.add(st.pop()); + } + st.push(now); + continue; + } + output.add(now); + } + while (!st.isEmpty()) output.add(st.pop()); + return output; + } +} diff --git a/src/test/java/BasicCalculatorTest.java b/src/test/java/BasicCalculatorTest.java deleted file mode 100644 index 688e95984..000000000 --- a/src/test/java/BasicCalculatorTest.java +++ /dev/null @@ -1,15 +0,0 @@ -import com.programmers.BasicCalculator; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; -public class BasicCalculatorTest { - BasicCalculator bc = new BasicCalculator(); - @DisplayName("계산기 테스트") - @Test - void BasicCalculatorTest() { - String str = "3 + 2 + 4 * 5 + 3 / 1"; - String result = bc.doCalculate(str); - assertThat(result).isEqualTo("3 + 2 + 4 * 5 + 3 / 1 = 28"); - } - -} diff --git a/src/test/java/CalculatorTest.java b/src/test/java/CalculatorTest.java new file mode 100644 index 000000000..f95b7e65b --- /dev/null +++ b/src/test/java/CalculatorTest.java @@ -0,0 +1,42 @@ +import com.programmers.engine.module.BasicCalculator; +import com.programmers.engine.module.convert.AnswerConverter; +import com.programmers.engine.module.convert.PostfixConverter; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +public class CalculatorTest { + BasicCalculator bc = new BasicCalculator(); + AnswerConverter ac = new AnswerConverter(); + PostfixConverter pc = new PostfixConverter(); + + @DisplayName("계산기 테스트") + @Test + void basicCalculatorTest() { + String str = "3 + 2 + 4 * 5 + 3 / 1"; + int result = bc.doCalculate(str); + assertThat(result).isEqualTo(28); + } + + @DisplayName("정답 변환기 테스트") + @Test + void answerConverterTest() { + List list = new ArrayList<>(Arrays.asList("3", "2", "+", "4", "5", "*", "+", "3", "1", "/", "+")); + int answer = ac.convertPostfixToAnswer(list); + assertThat(answer).isEqualTo(28); + } + + @DisplayName("후위표기 변환기 테스트") + @Test + void postfixConverterTest() { + String str = "3 + 2 + 4 * 5 + 3 / 1"; + List list = pc.convertInfixToPostfix(str); + List ans = new ArrayList<>(Arrays.asList("3", "2", "+", "4", "5", "*", "+", "3", "1", "/", "+")); + assertThat(list).isEqualTo(ans); + } + +} diff --git a/src/test/java/MenuTest.java b/src/test/java/MenuTest.java new file mode 100644 index 000000000..24a5f6b37 --- /dev/null +++ b/src/test/java/MenuTest.java @@ -0,0 +1,13 @@ +import com.programmers.engine.model.Menu; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +public class MenuTest { + @DisplayName("메뉴 선택 테스트") + @Test + void menuTest() { + assertThat(Menu.matchMenu(1)).isEqualTo(Menu.LOOK_UP); + assertThat(Menu.matchMenu(2)).isEqualTo(Menu.CALCULATE); + assertThat(Menu.matchMenu(3)).isEqualTo(Menu.EXIT); + } +} diff --git a/src/test/java/OperatorTest.java b/src/test/java/OperatorTest.java new file mode 100644 index 000000000..b82a6d15f --- /dev/null +++ b/src/test/java/OperatorTest.java @@ -0,0 +1,24 @@ +import com.programmers.engine.model.Operator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class OperatorTest { + @DisplayName("우선순위 테스트") + @Test + void getPriorityTest() { + assertThat(Operator.matchOperator("+").getPriority()).isEqualTo(1); + assertThat(Operator.matchOperator("-").getPriority()).isEqualTo(1); + assertThat(Operator.matchOperator("*").getPriority()).isEqualTo(2); + assertThat(Operator.matchOperator("/").getPriority()).isEqualTo(2); + } + + @DisplayName("사칙연산 테스트") + @Test + void calculateTest() { + assertThat(Operator.ADD.calculate(2, 6)).isEqualTo(8); + assertThat(Operator.SUBTRACTION.calculate(2, 6)).isEqualTo(-4); + assertThat(Operator.MULTIPLY.calculate(2, 6)).isEqualTo(12); + assertThat(Operator.DIVIDE.calculate(2, 6)).isEqualTo(3); + } +}