-
Notifications
You must be signed in to change notification settings - Fork 6
문자열 계산기 구현 #4
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
base: jjanggyu
Are you sure you want to change the base?
문자열 계산기 구현 #4
Changes from all commits
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,64 @@ | ||
| import domain.calculator.Calculator; | ||
| import domain.calculator.exception.InvalidInputException; | ||
| import domain.expression.operator.exception.DivideByZeroException; | ||
| import ui.printer.ConsolePrinter; | ||
| import ui.printer.Printer; | ||
| import ui.receiver.ConsoleReceiver; | ||
| import ui.receiver.Receiver; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| public class CalculatorApplication { | ||
| private static final boolean CONTINUE_PROGRAM = true; | ||
| private static final String EXIT_REQUEST = "exit"; | ||
|
|
||
| private Printer printer; | ||
| private Receiver receiver; | ||
| private Calculator calculator; | ||
|
Comment on lines
+15
to
+17
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. 한번 할당 후 변경이 없는 값들은 |
||
|
|
||
| public CalculatorApplication() { | ||
| printer = new ConsolePrinter(); | ||
| receiver = new ConsoleReceiver(); | ||
| calculator = new Calculator(); | ||
| } | ||
|
|
||
| public void run() { | ||
| printer.greet(); | ||
|
|
||
| while (CONTINUE_PROGRAM) { | ||
| printer.printWaitingForInputText(); | ||
| String expression = receiver.receiveExpression(); | ||
| if (expression.equalsIgnoreCase(EXIT_REQUEST)) { | ||
|
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. String api를 잘 활용하셨네요 👍🏻 |
||
| break ; | ||
| } | ||
|
|
||
| Optional<Integer> optionalResult = calculateExpression(expression); | ||
|
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. Optional은 생성 비용이 비싼 객체에요. 또한 비즈니스 로직이 복잡해지는 경우 메소드를 타고 다니면서 Integer와 Optional이 혼용되어 쓰여서 불편함을 낳기도 한답니다. 사용하는 부분이 null 체크용도 뿐이라면 예외를 던지는 방식으로도 충분히 처리할 수 있을 것 같네요. |
||
| if (optionalResult.isEmpty()) { | ||
| continue ; | ||
| } | ||
|
|
||
| int result = optionalResult.get(); | ||
| printer.printResult(result); | ||
| } | ||
| } | ||
|
|
||
| private Optional<Integer> calculateExpression(String expression) { | ||
| Integer result; | ||
| try { | ||
| result = calculator.calculate(expression); | ||
| } catch (DivideByZeroException e) { | ||
| System.out.println(e.getMessage()); | ||
| return Optional.empty(); | ||
| } catch (InvalidInputException e) { | ||
| System.out.println(e.getMessage()); | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| return Optional.of(result); | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| CalculatorApplication app = new CalculatorApplication(); | ||
| app.run(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package domain.calculator; | ||
|
|
||
| import domain.expression.Expression; | ||
|
|
||
| public class Calculator { | ||
|
|
||
| public int calculate(String strExpression) { | ||
| Parser parser = new Parser(strExpression); | ||
|
|
||
| int left = Integer.parseInt(parser.nextToken()); | ||
| Expression expression = Expression.from(left); | ||
|
|
||
| while (parser.hasNext()) { | ||
| String operator = parser.nextToken(); | ||
| int right = Integer.parseInt(parser.nextToken()); | ||
| expression = Expression.of(expression, right, operator); | ||
| } | ||
|
|
||
| return expression.evaluate(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package domain.calculator; | ||
|
|
||
| import domain.calculator.exception.InvalidInputException; | ||
|
|
||
| import java.util.List; | ||
| import java.util.regex.Pattern; | ||
|
|
||
| public class ExpressionValidator { | ||
|
|
||
| public void validateTokenList(List<String> tokenList) { | ||
| long invalidTokenCount = tokenList.stream() | ||
| .filter(token -> !isNumber(token) && !isOperator(token)) | ||
| .count(); | ||
|
|
||
| if (invalidTokenCount > 0) { | ||
| throw new InvalidInputException("올바르지 않은 입력입니다."); | ||
| } | ||
| } | ||
|
|
||
| public void validateTokenSequence(List<String> tokenList) { | ||
| throwIfConditionIsTrue(hasNotAnyToken(tokenList)); | ||
|
|
||
| String firstToken = tokenList.get(0); | ||
| throwIfConditionIsTrue(!isNumber(firstToken)); | ||
|
|
||
| for (int i = 1; i < tokenList.size(); i += 2) { | ||
| String operatorToken = tokenList.get(i); | ||
| throwIfConditionIsTrue(!isOperator(operatorToken)); | ||
|
|
||
| if (isNotLastOperation(i, tokenList.size())) { | ||
| String rightOperandToken = tokenList.get(i + 1); | ||
| throwIfConditionIsTrue(!isNumber(rightOperandToken)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void throwIfConditionIsTrue(boolean isConditionTrue) { | ||
| if (isConditionTrue) { | ||
| throw new InvalidInputException("올바르지 않은 입력입니다."); | ||
| } | ||
| } | ||
|
|
||
| private boolean hasNotAnyToken(List<String> tokenList) { | ||
| return (tokenList.size() == 0); | ||
| } | ||
| private boolean isNumber(String token) { | ||
| return Pattern.matches("^[0-9]+$", token); | ||
| } | ||
| private boolean isOperator(String token) { | ||
| return Pattern.matches("\\+|-|\\*|/", token); | ||
| } | ||
| private boolean isNotLastOperation(int index, int tokenListSize) { | ||
| return (index + 1 < tokenListSize); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package domain.calculator; | ||
|
|
||
| import java.util.*; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class Parser { | ||
|
|
||
| private final Queue<String> tokenQueue; | ||
|
|
||
| public Parser(String expression) { | ||
| tokenQueue = new LinkedList<>(); | ||
| ExpressionValidator validator = new ExpressionValidator(); | ||
|
|
||
| List<String> tokenList = splitExpressionToList(expression); | ||
| validator.validateTokenList(tokenList); | ||
| validator.validateTokenSequence(tokenList); | ||
| tokenQueue.addAll(tokenList); | ||
|
Comment on lines
+11
to
+17
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. 처음에 생각하신 Parser의 책임은 무엇인가요? |
||
| } | ||
|
|
||
| private List<String> splitExpressionToList(String expression) { | ||
| String[] tokens = expression.split(" +"); | ||
| return filterMeaningfulToken(tokens); | ||
| } | ||
|
|
||
| private List<String> filterMeaningfulToken(String[] tokens) { | ||
| return Arrays.stream(tokens) | ||
| .filter(token -> !token.equals("")) | ||
| .collect(Collectors.toList()); | ||
|
Comment on lines
+26
to
+28
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. 스트림을 잘 활용하시네요 👍🏻 |
||
| } | ||
|
|
||
| public String nextToken() { | ||
| return tokenQueue.poll(); | ||
| } | ||
| public boolean hasNext() { | ||
| return !tokenQueue.isEmpty(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package domain.calculator.exception; | ||
|
|
||
| public class InvalidInputException extends RuntimeException { | ||
|
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. 커스텀 익셉션을 정의하셨네요! |
||
|
|
||
| private String message; | ||
|
|
||
| public InvalidInputException(String message) { | ||
| this.message = message; | ||
| } | ||
|
|
||
| @Override | ||
| public String getMessage() { | ||
| return message; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
|
|
||
| package domain.expression; | ||
|
|
||
| import domain.expression.operator.Operator; | ||
|
|
||
| public class Expression { | ||
|
|
||
| private int left; | ||
| private int right; | ||
| private Operator operator; | ||
|
|
||
| private Expression(int left, int right, Operator operator) { | ||
| this.left = left; | ||
| this.right = right; | ||
| this.operator = operator; | ||
| } | ||
|
|
||
| public static Expression of(int left, int right, String operator) { | ||
| return new Expression(left, right, Operator.from(operator)); | ||
| } | ||
| public static Expression of(Expression left, int right, String operator) { | ||
| int leftResult = left.evaluate(); | ||
| return new Expression(leftResult, right, Operator.from(operator)); | ||
| } | ||
| public static Expression from(int left) { | ||
| return of(left, 0, "+"); | ||
| } | ||
|
|
||
| public int evaluate() { | ||
| return operator.operate(left, right); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package domain.expression.operator; | ||
|
|
||
| public class Addition extends Operator { | ||
|
|
||
| public int operate(int left, int right) { | ||
| return left + right; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package domain.expression.operator; | ||
|
|
||
| import domain.expression.operator.exception.DivideByZeroException; | ||
|
|
||
| public class Division extends Operator { | ||
|
|
||
| public int operate(int left, int right) { | ||
| if (right == 0) { | ||
| throw new DivideByZeroException("0으로 나눌 수 없습니다."); | ||
| } | ||
| return left / right; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package domain.expression.operator; | ||
|
|
||
| public class Multiplication extends Operator { | ||
|
|
||
| public int operate(int left, int right) { | ||
| return left * right; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package domain.expression.operator; | ||
|
|
||
| public abstract class Operator { | ||
|
|
||
| public static Operator from(String operator) { | ||
| if (operator.equals("+")) { | ||
| return new Addition(); | ||
| } else if (operator.equals("-")) { | ||
| return new Subtraction(); | ||
| } else if (operator.equals("*")) { | ||
| return new Multiplication(); | ||
| } | ||
| return new Division(); | ||
|
Comment on lines
+6
to
+13
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. 지금은 연산자가 4개 뿐이지만 연산자와 비례해서 이 메소드의 길이가 증가할것같아요. |
||
| } | ||
|
|
||
| public abstract int operate(int left, int right); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package domain.expression.operator; | ||
|
|
||
| public class Subtraction extends Operator { | ||
|
|
||
| public int operate(int left, int right) { | ||
| return left - right; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package domain.expression.operator.exception; | ||
|
|
||
| public class DivideByZeroException extends RuntimeException { | ||
|
|
||
| private String message; | ||
|
|
||
| public DivideByZeroException(String message) { | ||
| this.message = message; | ||
| } | ||
|
|
||
| @Override | ||
| public String getMessage() { | ||
| return message; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package ui.printer; | ||
|
|
||
| public class ConsolePrinter implements Printer { | ||
| private final String GREET_MESSAGE = "계산기 프로그램입니다.\n" + | ||
| "숫자와 사칙연산 기호를 공백으로 구분하여 입력해주세요.\n" + | ||
| "프로그램을 종료하시려면 exit을 입력해주세요."; | ||
|
|
||
| private final String INPUT_REQUEST_MESSAGE = "식 입력: "; | ||
|
|
||
| private final String OUTPUT_MESSAGE = "answer: %d\n"; | ||
| private final String EXIT_MESSAGE = "프로그램을 종료합니다."; | ||
|
Comment on lines
+4
to
+11
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. 상수처리 👍🏻 |
||
|
|
||
| @Override | ||
| public void greet() { | ||
| System.out.println(GREET_MESSAGE); | ||
| } | ||
|
|
||
| @Override | ||
| public void printWaitingForInputText() { | ||
| System.out.print(INPUT_REQUEST_MESSAGE); | ||
| } | ||
|
|
||
| @Override | ||
| public void printResult(int result) { | ||
| System.out.printf(OUTPUT_MESSAGE, result); | ||
| } | ||
|
|
||
| @Override | ||
| public void printExitMessage() { | ||
| System.out.println(EXIT_MESSAGE); | ||
| } | ||
| } | ||
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.
오 11버전을 사용하셨군요! 이유가 있을까요?