From f45f23dc3fea01d93fe88314bceb60457a2a88e0 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 05:01:37 +0900 Subject: [PATCH 01/30] =?UTF-8?q?[init]:=20=EC=A0=84=EC=B2=B4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EA=B5=AC=EC=84=B1=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 28 +++ README.md | 19 +- build.gradle | 33 +++ gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 234 ++++++++++++++++++ gradlew.bat | 89 +++++++ settings.gradle | 2 + src/main/java/CalculatorApp.java | 15 ++ src/main/java/Console.java | 5 + src/main/java/calculator/Calculator.java | 26 ++ .../io/InfixToPostfixConverter.java | 7 + src/main/java/calculator/io/Input.java | 5 + src/main/java/calculator/io/Output.java | 4 + src/main/java/calculator/model/Operator.java | 41 +++ .../repository/CalculationRepository.java | 14 ++ 15 files changed, 519 insertions(+), 8 deletions(-) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/CalculatorApp.java create mode 100644 src/main/java/Console.java create mode 100644 src/main/java/calculator/Calculator.java create mode 100644 src/main/java/calculator/io/InfixToPostfixConverter.java create mode 100644 src/main/java/calculator/io/Input.java create mode 100644 src/main/java/calculator/io/Output.java create mode 100644 src/main/java/calculator/model/Operator.java create mode 100644 src/main/java/calculator/repository/CalculationRepository.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3c8cb92f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# 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* +replay_pid* + +.idea +.gradle +build \ No newline at end of file diff --git a/README.md b/README.md index e89d6ccc6..c1c982e55 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ # java_calculator 자바 계산기 구현 미션 Repository입니다. -## 이곳은 공개 Repo입니다. +### 이곳은 공개 Repo입니다. 1. 여러분의 포트폴리오로 사용하셔도 됩니다. 2. 때문에 이 repo를 fork한 뒤 3. 여러분의 개인 Repo에 작업하며 4. 이 Repo에 PR을 보내어 멘토의 코드 리뷰와 피드백을 받으세요. -## Branch 명명 규칙 -1. 여러분 repo는 알아서 해주시고 😀(본인 레포니 main으로 하셔두 되져) -2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 gituser_id을 적어주세요 :) -- base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main`또는 `github_id` -- 이 규칙은 멘토+팀원들과 정하여 진행해주세요 :) +### Branch 명명 규칙 + 팀의 PR규칙 정하기 +1. 여러분 repo는 알아서 해주시고 😀(본인 레포니 main으로 하셔두 되져) +2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 gituser_id을 적어주세요 :) + - base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main`또는 `github_id` +3. 실제 진행할 PR규칙은 멘토+팀원들과 정하여 진행해주세요 :) + - ← head repo : `여러분repo` compare : `main`로 할지 + - 또는 ← head repo : `여러분repo` compare : `github_id`로 할지 - 참고 : [Github 위치 및 피드백 기준 가이드](https://www.notion.so/backend-devcourse/Github-e1a0908a6bbf4aeaa5a62981499bb215) ### 과제를 통해 기대하는 역량 @@ -21,7 +23,7 @@ - 스스로 OOP를 생각하고 코드로 옮길 수 있는 능력해보자 ### 요구사항 - +- 콘솔로 구현입니다.(스윙으로 구현하시는 분들 계실까봐) - 객체지향적인 코드로 계산기 구현하기 - [ ] 더하기 - [ ] 빼기 @@ -29,7 +31,8 @@ - [ ] 나누기 - [ ] 우선순위(사칙연산) - [ ] 테스트 코드 구현하기 -- [ ] 계산 이력을 맵으로 데이터 저장기능 만들기(인메모리 DB) +- [ ] 계산 이력을 맵으로 데이터 저장기능 만들기 + - 애플리케이션이 동작하는 동안 데이터베이스 외에 데이터를 저장할 수 있는 방법을 고안해보세요. - (선택) 정규식 사용 ### 실행결과(콘솔) diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..a3efb3f7d --- /dev/null +++ b/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'java' +} + +group 'org.example' +version '1.0-SNAPSHOT' +sourceCompatibility = '17' + + +repositories { + mavenCentral() +} +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + + +dependencies { + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' + + implementation 'org.assertj:assertj-core:3.24.2' + + implementation group: 'org.projectlombok', name: 'lombok', version: '1.18.28' + +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..41dfb8790 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 000000000..1b6c78733 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..948943cf9 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'javacalculator' + diff --git a/src/main/java/CalculatorApp.java b/src/main/java/CalculatorApp.java new file mode 100644 index 000000000..7f833ef37 --- /dev/null +++ b/src/main/java/CalculatorApp.java @@ -0,0 +1,15 @@ +import calculator.Calculator; +import calculator.InfixToPostfixConvertorImpl; +import calculator.repository.CalculationRepository; + +public class CalculatorApp { + public static void main(String[] args) { + Console console = new Console(); + InfixToPostfixConvertorImpl infixToPostfixConvertor = new InfixToPostfixConvertorImpl(); + CalculationRepository repository = new CalculationRepository(); + + new Calculator(infixToPostfixConvertor, console, console, repository).run(); + + + } +} diff --git a/src/main/java/Console.java b/src/main/java/Console.java new file mode 100644 index 000000000..4aed92a90 --- /dev/null +++ b/src/main/java/Console.java @@ -0,0 +1,5 @@ +import calculator.io.Input; +import calculator.io.Output; + +public class Console implements Input, Output { +} diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java new file mode 100644 index 000000000..c615e57e0 --- /dev/null +++ b/src/main/java/calculator/Calculator.java @@ -0,0 +1,26 @@ +package calculator; + +import calculator.io.InfixToPostfixConverter; +import calculator.io.Input; +import calculator.io.Output; +import calculator.repository.CalculationRepository; + + +public class Calculator implements Runnable { + private final InfixToPostfixConverter infixConverter; + private final Input input; + private final Output output; + private final CalculationRepository calculationRepository; + + public Calculator(InfixToPostfixConverter infixConverter, Input input, Output output, CalculationRepository calculationRepository) { + this.infixConverter = infixConverter; + this.input = input; + this.output = output; + this.calculationRepository = calculationRepository; + } + + + @Override + public void run() { + } +} diff --git a/src/main/java/calculator/io/InfixToPostfixConverter.java b/src/main/java/calculator/io/InfixToPostfixConverter.java new file mode 100644 index 000000000..ad12f5e04 --- /dev/null +++ b/src/main/java/calculator/io/InfixToPostfixConverter.java @@ -0,0 +1,7 @@ +package calculator.io; + +import java.util.ArrayList; + +public interface InfixToPostfixConverter { + ArrayList convert(String infixExpression); +} diff --git a/src/main/java/calculator/io/Input.java b/src/main/java/calculator/io/Input.java new file mode 100644 index 000000000..1cf67f2e1 --- /dev/null +++ b/src/main/java/calculator/io/Input.java @@ -0,0 +1,5 @@ +package calculator.io; + +public interface Input { + +} diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java new file mode 100644 index 000000000..c75464cf2 --- /dev/null +++ b/src/main/java/calculator/io/Output.java @@ -0,0 +1,4 @@ +package calculator.io; + +public interface Output { +} diff --git a/src/main/java/calculator/model/Operator.java b/src/main/java/calculator/model/Operator.java new file mode 100644 index 000000000..1f604cd74 --- /dev/null +++ b/src/main/java/calculator/model/Operator.java @@ -0,0 +1,41 @@ +package calculator.model; +import java.util.Arrays; +import java.util.function.BiFunction; + +import static calculator.global.Priority.HIGH; +import static calculator.global.Priority.LOW; + +public enum Operator { + + PLUS("+", (num1, num2) -> num1 + num2, LOW), + MINUS("-", (num1, num2) -> num1 - num2, LOW), + MULTIPLY("*", (num1, num2) -> num1 * num2, HIGH), + DIVIDE("/", (num1, num2) -> num1 / num2, HIGH); + + + private final String operator; + private final BiFunction expression; + private final Integer priority; + + Operator(String operator, BiFunction expression, Integer priority) { + this.operator = operator; + this.expression = expression; + this.priority = priority; + } + + + public boolean isSameOrGrater(Operator operator){ + System.out.println(priority + " ? " + operator.priority); + return priority >= operator.priority; + } + + public double mapCalculate(String operator, double num1, double num2){ + return getOperator(operator).expression.apply(num1, num2); + } + + public static Operator getOperator(String operator) { + return Arrays.stream(values()) + .filter(o -> o.operator.equals(operator)) + .findFirst().orElseThrow(() -> new IllegalArgumentException("올바른 연산자가 아닙니다.")); + } +} diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java new file mode 100644 index 000000000..c088bc487 --- /dev/null +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -0,0 +1,14 @@ +package calculator.repository; + +import java.util.LinkedHashMap; + +public class CalculationRepository { + LinkedHashMap results = new LinkedHashMap<>(); + + public void save(String calculation) { + } + + public LinkedHashMap findAll() { + return null; + } +} From 2c657339ad68db3d1458eeb6d56dd1bbceffdc2f Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 07:11:35 +0900 Subject: [PATCH 02/30] =?UTF-8?q?[feat]:=20Input,=20Output=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorConsole.java | 37 ++++++++++ .../java/calculator/global/ErrorResponse.java | 5 ++ .../calculator/global/InputConstants.java | 12 ++++ src/main/java/calculator/global/Menu.java | 20 ++++++ src/main/java/calculator/io/Input.java | 3 +- src/main/java/calculator/io/Output.java | 5 ++ src/main/java/calculator/model/Operation.java | 67 +++++++++++++++++++ 7 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/main/java/CalculatorConsole.java create mode 100644 src/main/java/calculator/global/ErrorResponse.java create mode 100644 src/main/java/calculator/global/InputConstants.java create mode 100644 src/main/java/calculator/global/Menu.java create mode 100644 src/main/java/calculator/model/Operation.java diff --git a/src/main/java/CalculatorConsole.java b/src/main/java/CalculatorConsole.java new file mode 100644 index 000000000..891526e1c --- /dev/null +++ b/src/main/java/CalculatorConsole.java @@ -0,0 +1,37 @@ +import calculator.global.Menu; +import calculator.io.Input; +import calculator.io.Output; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Scanner; + +public class CalculatorConsole implements Input, Output { + private final Scanner scanner = new Scanner(System.in); + @Override + public void putMenu() { + Arrays.stream(Menu.values()) + .forEach(m -> System.out.println(m.toString())); + } + + @Override + public void showCalculationResult(LinkedHashMap calculationResult) { + calculationResult.values().forEach(System.out::println); + } + + @Override + public void inputError(String errorResponse) { + System.out.println(errorResponse); + } + + @Override + public String getChoice(String prompt) { + System.out.print(prompt); + return scanner.nextLine(); + } + + @Override + public String getExpression() { + return scanner.nextLine(); + } +} diff --git a/src/main/java/calculator/global/ErrorResponse.java b/src/main/java/calculator/global/ErrorResponse.java new file mode 100644 index 000000000..30d6dccdd --- /dev/null +++ b/src/main/java/calculator/global/ErrorResponse.java @@ -0,0 +1,5 @@ +package calculator.global; + +public class ErrorResponse { + public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; +} diff --git a/src/main/java/calculator/global/InputConstants.java b/src/main/java/calculator/global/InputConstants.java new file mode 100644 index 000000000..8338a5e24 --- /dev/null +++ b/src/main/java/calculator/global/InputConstants.java @@ -0,0 +1,12 @@ +package calculator.global; + +public class InputConstants { + public static final String CHOICE_PROMPT = "선택 : "; + public static final int FIRST_INDEX = 0; + public static final int MENU_INPUT_LENGTH = 1; + public static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; + public static final char REQUEST_CALCULATION = '2'; + + public static final String OPERATOR_REGEX = "[-*/+]"; + public static final String OPERAND_REGEX = "^\\d+$"; +} diff --git a/src/main/java/calculator/global/Menu.java b/src/main/java/calculator/global/Menu.java new file mode 100644 index 000000000..4b8e3e63f --- /dev/null +++ b/src/main/java/calculator/global/Menu.java @@ -0,0 +1,20 @@ +package calculator.global; + +public enum Menu { + GET(1, "조회"), + CALCULATE(2, "계산"); + + private final Integer command; + private final String explanation; + + + Menu(Integer command, String explanation) { + this.command = command; + this.explanation = explanation; + } + + @Override + public String toString() { + return command + ". " + explanation; + } +} diff --git a/src/main/java/calculator/io/Input.java b/src/main/java/calculator/io/Input.java index 1cf67f2e1..b5b284604 100644 --- a/src/main/java/calculator/io/Input.java +++ b/src/main/java/calculator/io/Input.java @@ -1,5 +1,6 @@ package calculator.io; public interface Input { - + String getChoice(String s); + String getExpression(); } diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java index c75464cf2..770386b5d 100644 --- a/src/main/java/calculator/io/Output.java +++ b/src/main/java/calculator/io/Output.java @@ -1,4 +1,9 @@ package calculator.io; +import java.util.LinkedHashMap; + public interface Output { + void putMenu(); + void showCalculationResult(LinkedHashMap calculationResult); + void inputError(String errorResponse); } diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/Operation.java new file mode 100644 index 000000000..e826a8567 --- /dev/null +++ b/src/main/java/calculator/model/Operation.java @@ -0,0 +1,67 @@ +package calculator.model; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; + +import static calculator.global.Priority.HIGH; +import static calculator.global.Priority.LOW; + +public class Operation { + private static final Map operatorMap = new HashMap<>(); + + public Operation() { + setOperatorMap(); + } + + private static void setOperatorMap(){ + Arrays.stream(Operator.values()) + .forEach(op -> operatorMap.put(op.operator, op)); + } + + + public static Map getOperatorMap() { + return operatorMap; + } + + public static double calculate(double a, String operator, double b){ + return Optional.ofNullable(operatorMap.get(operator)) + .orElseThrow(() -> new IllegalArgumentException("연산자 입력에 오류가 발생했습니다.")) + .mapCalculate(operator,a,b); + + } + public static Operator getOperator(String operator){ + return operatorMap.get(operator); + } + + public enum Operator { + + PLUS("+", (num1, num2) -> num1 + num2, LOW), + MINUS("-", (num1, num2) -> num1 - num2, LOW), + MULTIPLY("*", (num1, num2) -> num1 * num2, HIGH), + DIVIDE("/", (num1, num2) -> num1 / num2, HIGH); + + + private final String operator; + private final BiFunction expression; + private final Integer priority; + + Operator(String operator, BiFunction expression, Integer priority) { + this.operator = operator; + this.expression = expression; + this.priority = priority; + } + + + public boolean isSameOrGrater(Operator operator) { + return priority >= operator.priority; + } + + public double mapCalculate(String operator, double num1, double num2) { + return getOperator(operator).expression.apply(num1, num2); + } + } + +} From 18682d2a15c585f559573b45578c755caf0257bf Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 07:16:26 +0900 Subject: [PATCH 03/30] =?UTF-8?q?[feat]:=20InfixToPostfixConverter=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20-=20Infix=20=EC=A4=91?= =?UTF-8?q?=EC=9C=84=20=ED=91=9C=ED=98=84=20=EC=88=98=EC=8B=9D=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Postfix=20=ED=9B=84=EC=9C=84=20=ED=91=9C=ED=98=84?= =?UTF-8?q?=20=EC=88=98=EC=8B=9D(=EB=A6=AC=EC=8A=A4=ED=8A=B8)=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculator/InfixToPostfixConverter.java | 57 +++++++++++++++++++ ...onverter.java => ExpressionConverter.java} | 2 +- src/main/java/calculator/model/Operator.java | 41 ------------- .../infixtopostfix/InfixToArrayListTest.java | 24 ++++++++ 4 files changed, 82 insertions(+), 42 deletions(-) create mode 100644 src/main/java/calculator/InfixToPostfixConverter.java rename src/main/java/calculator/io/{InfixToPostfixConverter.java => ExpressionConverter.java} (71%) delete mode 100644 src/main/java/calculator/model/Operator.java create mode 100644 src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java diff --git a/src/main/java/calculator/InfixToPostfixConverter.java b/src/main/java/calculator/InfixToPostfixConverter.java new file mode 100644 index 000000000..4f8c3ddb2 --- /dev/null +++ b/src/main/java/calculator/InfixToPostfixConverter.java @@ -0,0 +1,57 @@ +package calculator; + +import calculator.io.ExpressionConverter; +import calculator.model.Operation; +import lombok.ToString; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Stack; + +import static calculator.global.InputConstants.OPERAND_REGEX; +import static calculator.global.InputConstants.OPERATOR_REGEX; + +@ToString +public class InfixToPostfixConverter implements ExpressionConverter { + private ArrayList infix; + private static final ArrayList postfix = new ArrayList<>(); + private static final Stack opStack = new Stack<>(); + + @Override + public ArrayList convert(String infixExpression) { + StringBuilder num = new StringBuilder(); + + // 중위 표기식을 리스트로 변환 + infix = new ArrayList<>(Arrays.asList(infixExpression.split(" "))); + + // 후위 표기식으로 전환 + for(String s : infix){ + if(s.matches(OPERATOR_REGEX)){ + if(!num.toString().equals("")){ + postfix.add(num.toString()); + num = new StringBuilder(); + } + if (opStack.isEmpty()) opStack.push(s); + else { + if (Operation.getOperator(opStack.peek()).isSameOrGrater(Operation.getOperator(s))) { + postfix.add(opStack.pop()); + opStack.push(s); + } + else { + opStack.push(s); + } + } + } else if (s.matches(OPERAND_REGEX)) { + num.append(s); + } + } + if(!num.toString().equals("")){ + postfix.add(num.toString()); + } + while(!opStack.isEmpty()){ + System.out.println(opStack.peek()); + postfix.add(opStack.pop()); + } + return postfix; + } +} diff --git a/src/main/java/calculator/io/InfixToPostfixConverter.java b/src/main/java/calculator/io/ExpressionConverter.java similarity index 71% rename from src/main/java/calculator/io/InfixToPostfixConverter.java rename to src/main/java/calculator/io/ExpressionConverter.java index ad12f5e04..12b780b05 100644 --- a/src/main/java/calculator/io/InfixToPostfixConverter.java +++ b/src/main/java/calculator/io/ExpressionConverter.java @@ -2,6 +2,6 @@ import java.util.ArrayList; -public interface InfixToPostfixConverter { +public interface ExpressionConverter { ArrayList convert(String infixExpression); } diff --git a/src/main/java/calculator/model/Operator.java b/src/main/java/calculator/model/Operator.java deleted file mode 100644 index 1f604cd74..000000000 --- a/src/main/java/calculator/model/Operator.java +++ /dev/null @@ -1,41 +0,0 @@ -package calculator.model; -import java.util.Arrays; -import java.util.function.BiFunction; - -import static calculator.global.Priority.HIGH; -import static calculator.global.Priority.LOW; - -public enum Operator { - - PLUS("+", (num1, num2) -> num1 + num2, LOW), - MINUS("-", (num1, num2) -> num1 - num2, LOW), - MULTIPLY("*", (num1, num2) -> num1 * num2, HIGH), - DIVIDE("/", (num1, num2) -> num1 / num2, HIGH); - - - private final String operator; - private final BiFunction expression; - private final Integer priority; - - Operator(String operator, BiFunction expression, Integer priority) { - this.operator = operator; - this.expression = expression; - this.priority = priority; - } - - - public boolean isSameOrGrater(Operator operator){ - System.out.println(priority + " ? " + operator.priority); - return priority >= operator.priority; - } - - public double mapCalculate(String operator, double num1, double num2){ - return getOperator(operator).expression.apply(num1, num2); - } - - public static Operator getOperator(String operator) { - return Arrays.stream(values()) - .filter(o -> o.operator.equals(operator)) - .findFirst().orElseThrow(() -> new IllegalArgumentException("올바른 연산자가 아닙니다.")); - } -} diff --git a/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java b/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java new file mode 100644 index 000000000..957fea2d9 --- /dev/null +++ b/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java @@ -0,0 +1,24 @@ +package calculator.infixtopostfix; + +import calculator.InfixToPostfixConverter; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +public class InfixToArrayListTest { + @Test + @DisplayName("처음 입력 받은 중위 표현 수식을 배열로 변환 하기") + void getInfixList() { + Assertions.assertThat(new InfixToPostfixConverter().convert("1 + 5 * 2")) + .isEqualTo(Arrays.asList("1", "5", "2", "*", "+")); + } + + @Test + @DisplayName("복잡한 중위 표현 수식을 후위 표현 수식으로 변환") + void getPrefix(){ + Assertions.assertThat(new InfixToPostfixConverter().convert("3 + 6 * 2 / 3")) + .isEqualTo(Arrays.asList("3", "6", "2", "*", "3", "/", "+")); + } +} From e60186f81aee0ad974d1b5ee0cd87e59763eeafa Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 08:01:20 +0900 Subject: [PATCH 04/30] =?UTF-8?q?[feat]:=20Calculator=20=EC=9E=85=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EB=8F=99=EC=9E=91=20=EA=B5=AC=ED=98=84=EA=B3=BC=20?= =?UTF-8?q?Repository=20=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20validation=20=EA=B8=B0=EB=8A=A5=20-=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=84=A0=ED=83=9D=20=EC=9E=85=EB=A0=A5=20-=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20validate=20=ED=99=95=EC=9D=B8=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorApp.java | 6 +-- src/main/java/Console.java | 5 -- src/main/java/calculator/Calculator.java | 48 +++++++++++++++++-- .../calculator/InfixToPostfixConverter.java | 1 + src/main/java/calculator/model/Operation.java | 11 +++-- .../repository/CalculationRepository.java | 5 +- 6 files changed, 58 insertions(+), 18 deletions(-) delete mode 100644 src/main/java/Console.java diff --git a/src/main/java/CalculatorApp.java b/src/main/java/CalculatorApp.java index 7f833ef37..d7ee6a4c2 100644 --- a/src/main/java/CalculatorApp.java +++ b/src/main/java/CalculatorApp.java @@ -1,11 +1,11 @@ import calculator.Calculator; -import calculator.InfixToPostfixConvertorImpl; +import calculator.InfixToPostfixConverter; import calculator.repository.CalculationRepository; public class CalculatorApp { public static void main(String[] args) { - Console console = new Console(); - InfixToPostfixConvertorImpl infixToPostfixConvertor = new InfixToPostfixConvertorImpl(); + CalculatorConsole console = new CalculatorConsole(); + InfixToPostfixConverter infixToPostfixConvertor = new InfixToPostfixConverter(); CalculationRepository repository = new CalculationRepository(); new Calculator(infixToPostfixConvertor, console, console, repository).run(); diff --git a/src/main/java/Console.java b/src/main/java/Console.java deleted file mode 100644 index 4aed92a90..000000000 --- a/src/main/java/Console.java +++ /dev/null @@ -1,5 +0,0 @@ -import calculator.io.Input; -import calculator.io.Output; - -public class Console implements Input, Output { -} diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index c615e57e0..8997a9206 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,18 +1,24 @@ package calculator; -import calculator.io.InfixToPostfixConverter; +import calculator.io.ExpressionConverter; import calculator.io.Input; import calculator.io.Output; import calculator.repository.CalculationRepository; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import static calculator.global.ErrorResponse.*; +import static calculator.global.InputConstants.*; public class Calculator implements Runnable { - private final InfixToPostfixConverter infixConverter; + private final ExpressionConverter infixConverter; private final Input input; private final Output output; private final CalculationRepository calculationRepository; - public Calculator(InfixToPostfixConverter infixConverter, Input input, Output output, CalculationRepository calculationRepository) { + public Calculator(ExpressionConverter infixConverter, Input input, Output output, CalculationRepository calculationRepository) { this.infixConverter = infixConverter; this.input = input; this.output = output; @@ -22,5 +28,41 @@ public Calculator(InfixToPostfixConverter infixConverter, Input input, Output ou @Override public void run() { + while(true) { + output.putMenu(); + String inputString = input.getChoice(CHOICE_PROMPT); + if (!validateChoiceInput(inputString)) output.inputError(MENU_INPUT_ERROR); + + switch (inputString.charAt(FIRST_INDEX)) { + case REQUEST_VIEW_CALCULATION_RESULT: + output.showCalculationResult(calculationRepository.findAll()); + + case REQUEST_CALCULATION: + String expression = input.getExpression(); + if (validateExpression(expression)) { + ArrayList postfixList = infixConverter.convert(expression); + int result = calculate(postfixList); + calculationRepository.save(expression + " = " + result); + } + } + } + } + + private int calculate(ArrayList postfixExpression){ + return 0; + } + + private boolean validateChoiceInput(String input){ + char firstChar = input.charAt(FIRST_INDEX); + return input.length() == MENU_INPUT_LENGTH + && firstChar == REQUEST_VIEW_CALCULATION_RESULT + || firstChar == REQUEST_CALCULATION; + } + private boolean validateExpression(String expression){ + AtomicInteger index = new AtomicInteger(0); + long countOfValidOps = Arrays.stream(expression.split(" ")) + .filter(e -> index.getAndIncrement() % 2 == 0 ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) + .count(); + return countOfValidOps >= MINIMUM_OPS && Arrays.stream(expression.split(" ")).count() == countOfValidOps; } } diff --git a/src/main/java/calculator/InfixToPostfixConverter.java b/src/main/java/calculator/InfixToPostfixConverter.java index 4f8c3ddb2..49d6d47af 100644 --- a/src/main/java/calculator/InfixToPostfixConverter.java +++ b/src/main/java/calculator/InfixToPostfixConverter.java @@ -20,6 +20,7 @@ public class InfixToPostfixConverter implements ExpressionConverter { @Override public ArrayList convert(String infixExpression) { StringBuilder num = new StringBuilder(); + Operation operation = new Operation(); // 중위 표기식을 리스트로 변환 infix = new ArrayList<>(Arrays.asList(infixExpression.split(" "))); diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/Operation.java index e826a8567..0fa8a7499 100644 --- a/src/main/java/calculator/model/Operation.java +++ b/src/main/java/calculator/model/Operation.java @@ -6,29 +6,30 @@ import java.util.Optional; import java.util.function.BiFunction; +import static calculator.global.ErrorResponse.OPERATOR_INPUT_ERROR; import static calculator.global.Priority.HIGH; import static calculator.global.Priority.LOW; public class Operation { - private static final Map operatorMap = new HashMap<>(); + private static Map operatorMap = new HashMap<>(); public Operation() { setOperatorMap(); } - private static void setOperatorMap(){ + private void setOperatorMap(){ Arrays.stream(Operator.values()) .forEach(op -> operatorMap.put(op.operator, op)); } - public static Map getOperatorMap() { + public Map getOperatorMap() { return operatorMap; } - public static double calculate(double a, String operator, double b){ + public double calculate(double a, String operator, double b){ return Optional.ofNullable(operatorMap.get(operator)) - .orElseThrow(() -> new IllegalArgumentException("연산자 입력에 오류가 발생했습니다.")) + .orElseThrow(() -> new IllegalArgumentException(OPERATOR_INPUT_ERROR)) .mapCalculate(operator,a,b); } diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index c088bc487..d1bb41219 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -3,12 +3,13 @@ import java.util.LinkedHashMap; public class CalculationRepository { - LinkedHashMap results = new LinkedHashMap<>(); + private final static LinkedHashMap results = new LinkedHashMap<>(); public void save(String calculation) { + results.put(results.size() + 1, calculation); } public LinkedHashMap findAll() { - return null; + return results; } } From 839015af891dfebd74982423cd987bc52ad1a4ae Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 08:03:12 +0900 Subject: [PATCH 05/30] =?UTF-8?q?[add]:=20Constant=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/global/ErrorResponse.java | 1 + src/main/java/calculator/global/InputConstants.java | 4 +++- src/main/java/calculator/global/Priority.java | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/main/java/calculator/global/Priority.java diff --git a/src/main/java/calculator/global/ErrorResponse.java b/src/main/java/calculator/global/ErrorResponse.java index 30d6dccdd..cd8b429c1 100644 --- a/src/main/java/calculator/global/ErrorResponse.java +++ b/src/main/java/calculator/global/ErrorResponse.java @@ -2,4 +2,5 @@ public class ErrorResponse { public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; + public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; } diff --git a/src/main/java/calculator/global/InputConstants.java b/src/main/java/calculator/global/InputConstants.java index 8338a5e24..ae2398ce0 100644 --- a/src/main/java/calculator/global/InputConstants.java +++ b/src/main/java/calculator/global/InputConstants.java @@ -1,7 +1,7 @@ package calculator.global; public class InputConstants { - public static final String CHOICE_PROMPT = "선택 : "; + public static final String CHOICE_PROMPT = "\n선택 : "; public static final int FIRST_INDEX = 0; public static final int MENU_INPUT_LENGTH = 1; public static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; @@ -9,4 +9,6 @@ public class InputConstants { public static final String OPERATOR_REGEX = "[-*/+]"; public static final String OPERAND_REGEX = "^\\d+$"; + + public static final int MINIMUM_OPS = 3; } diff --git a/src/main/java/calculator/global/Priority.java b/src/main/java/calculator/global/Priority.java new file mode 100644 index 000000000..c0a25defb --- /dev/null +++ b/src/main/java/calculator/global/Priority.java @@ -0,0 +1,6 @@ +package calculator.global; + +public class Priority { + public static final Integer LOW = 0; + public static final Integer HIGH = 1; +} From 431c84bdcb2d05f2bde68d9381c79808f125c032 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 08:33:03 +0900 Subject: [PATCH 06/30] =?UTF-8?q?[feat]:=20=ED=9B=84=EC=9C=84=20=ED=91=9C?= =?UTF-8?q?=EA=B8=B0=20=EC=88=98=EC=8B=9D=20=EA=B3=84=EC=82=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20-=20Integer=EB=A1=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=91=90=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 25 ++++++++++++++++--- .../java/calculator/global/ErrorResponse.java | 1 + src/main/java/calculator/model/Operation.java | 14 +++++++---- .../calculator/InputValidationTest.java | 13 ++++++++++ .../operator/OperatorApplyTest.java | 24 ++++++++++++++++++ 5 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 src/test/java/calculator/calculator/InputValidationTest.java create mode 100644 src/test/java/calculator/operator/OperatorApplyTest.java diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 8997a9206..458f0f62d 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -3,10 +3,12 @@ import calculator.io.ExpressionConverter; import calculator.io.Input; import calculator.io.Output; +import calculator.model.Operation; import calculator.repository.CalculationRepository; import java.util.ArrayList; import java.util.Arrays; +import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; import static calculator.global.ErrorResponse.*; @@ -41,15 +43,32 @@ public void run() { String expression = input.getExpression(); if (validateExpression(expression)) { ArrayList postfixList = infixConverter.convert(expression); - int result = calculate(postfixList); + Integer result = calculate(postfixList); calculationRepository.save(expression + " = " + result); } } } } - private int calculate(ArrayList postfixExpression){ - return 0; + private Integer calculate(ArrayList postfixExpression){ + Stack calcStack = new Stack<>(); + Operation operation = new Operation(); + + int op1, op2; + + for(String s : postfixExpression){ + if(s.matches(OPERATOR_REGEX)){ + op2 = Integer.parseInt(calcStack.pop()); + op1 = Integer.parseInt(calcStack.pop()); + + Integer result = operation.calculate(op1, s, op2); + calcStack.push(String.valueOf(result)); + } + else{ + calcStack.push(s); + } + } + return Integer.valueOf(calcStack.pop()); } private boolean validateChoiceInput(String input){ diff --git a/src/main/java/calculator/global/ErrorResponse.java b/src/main/java/calculator/global/ErrorResponse.java index cd8b429c1..7b4808199 100644 --- a/src/main/java/calculator/global/ErrorResponse.java +++ b/src/main/java/calculator/global/ErrorResponse.java @@ -3,4 +3,5 @@ public class ErrorResponse { public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; + public static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; } diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/Operation.java index 0fa8a7499..ac873a66c 100644 --- a/src/main/java/calculator/model/Operation.java +++ b/src/main/java/calculator/model/Operation.java @@ -6,12 +6,13 @@ import java.util.Optional; import java.util.function.BiFunction; +import static calculator.global.ErrorResponse.INVALID_OPERAND; import static calculator.global.ErrorResponse.OPERATOR_INPUT_ERROR; import static calculator.global.Priority.HIGH; import static calculator.global.Priority.LOW; public class Operation { - private static Map operatorMap = new HashMap<>(); + private static final Map operatorMap = new HashMap<>(); public Operation() { setOperatorMap(); @@ -27,7 +28,7 @@ public Map getOperatorMap() { return operatorMap; } - public double calculate(double a, String operator, double b){ + public Integer calculate(Integer a, String operator, Integer b){ return Optional.ofNullable(operatorMap.get(operator)) .orElseThrow(() -> new IllegalArgumentException(OPERATOR_INPUT_ERROR)) .mapCalculate(operator,a,b); @@ -46,10 +47,10 @@ public enum Operator { private final String operator; - private final BiFunction expression; + private final BiFunction expression; private final Integer priority; - Operator(String operator, BiFunction expression, Integer priority) { + Operator(String operator, BiFunction expression, Integer priority) { this.operator = operator; this.expression = expression; this.priority = priority; @@ -60,7 +61,10 @@ public boolean isSameOrGrater(Operator operator) { return priority >= operator.priority; } - public double mapCalculate(String operator, double num1, double num2) { + public Integer mapCalculate(String operator, Integer num1, Integer num2) { + if(Operation.getOperator(operator) == DIVIDE && num2 <= 0){ + throw new ArithmeticException(INVALID_OPERAND); + } return getOperator(operator).expression.apply(num1, num2); } } diff --git a/src/test/java/calculator/calculator/InputValidationTest.java b/src/test/java/calculator/calculator/InputValidationTest.java new file mode 100644 index 000000000..0ff1a502c --- /dev/null +++ b/src/test/java/calculator/calculator/InputValidationTest.java @@ -0,0 +1,13 @@ +package calculator.calculator; + +import calculator.Calculator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class InputValidationTest { + @Test + @DisplayName("올바른 번호를 입력했는지 검사") + void validateInputChoice(){ + + } +} diff --git a/src/test/java/calculator/operator/OperatorApplyTest.java b/src/test/java/calculator/operator/OperatorApplyTest.java new file mode 100644 index 000000000..3d07f2520 --- /dev/null +++ b/src/test/java/calculator/operator/OperatorApplyTest.java @@ -0,0 +1,24 @@ +package calculator.operator; + +import calculator.model.Operation; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class OperatorApplyTest { + @Test + @DisplayName("사칙 연산 Map 테스트") + void confirm(){ + Operation operation = new Operation(); + + int a = 10; + int b = -5; + + + operation.getOperatorMap().entrySet() + .forEach(e -> { + System.out.print(a + e.getKey() + b + " = "); + System.out.println(operation.calculate(a, e.getKey(), b)); + }); + } + +} From e8068a40b5a7201e5277961e61e9e9a632259e58 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 08:57:24 +0900 Subject: [PATCH 07/30] =?UTF-8?q?[test]:=20=EC=A0=81=ED=95=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=88=98=EC=8B=9D=20=ED=91=9C?= =?UTF-8?q?=ED=98=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorConsole.java | 2 +- src/main/java/calculator/Calculator.java | 22 +++++++----- .../calculator/InfixToPostfixConverter.java | 6 +--- .../calculator/ExpressionValidationTest.java | 35 +++++++++++++++++++ .../calculator/InputValidationTest.java | 13 ------- .../operator/OperatorApplyTest.java | 4 +-- 6 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 src/test/java/calculator/calculator/ExpressionValidationTest.java delete mode 100644 src/test/java/calculator/calculator/InputValidationTest.java diff --git a/src/main/java/CalculatorConsole.java b/src/main/java/CalculatorConsole.java index 891526e1c..490d9a9ee 100644 --- a/src/main/java/CalculatorConsole.java +++ b/src/main/java/CalculatorConsole.java @@ -21,7 +21,7 @@ public void showCalculationResult(LinkedHashMap calculationResu @Override public void inputError(String errorResponse) { - System.out.println(errorResponse); + throw new IllegalArgumentException(errorResponse); } @Override diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 458f0f62d..7beef4eb5 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -15,13 +15,13 @@ import static calculator.global.InputConstants.*; public class Calculator implements Runnable { - private final ExpressionConverter infixConverter; + private final ExpressionConverter expressionConverter; private final Input input; private final Output output; private final CalculationRepository calculationRepository; - public Calculator(ExpressionConverter infixConverter, Input input, Output output, CalculationRepository calculationRepository) { - this.infixConverter = infixConverter; + public Calculator(ExpressionConverter expressionConverter, Input input, Output output, CalculationRepository calculationRepository) { + this.expressionConverter = expressionConverter; this.input = input; this.output = output; this.calculationRepository = calculationRepository; @@ -42,15 +42,15 @@ public void run() { case REQUEST_CALCULATION: String expression = input.getExpression(); if (validateExpression(expression)) { - ArrayList postfixList = infixConverter.convert(expression); - Integer result = calculate(postfixList); + ArrayList postfixList = expressionConverter.convert(expression); + Integer result = calculateFromPostfix(postfixList); calculationRepository.save(expression + " = " + result); } } } } - private Integer calculate(ArrayList postfixExpression){ + public Integer calculateFromPostfix(ArrayList postfixExpression){ Stack calcStack = new Stack<>(); Operation operation = new Operation(); @@ -71,17 +71,21 @@ private Integer calculate(ArrayList postfixExpression){ return Integer.valueOf(calcStack.pop()); } - private boolean validateChoiceInput(String input){ + public static boolean validateChoiceInput(String input){ char firstChar = input.charAt(FIRST_INDEX); return input.length() == MENU_INPUT_LENGTH && firstChar == REQUEST_VIEW_CALCULATION_RESULT || firstChar == REQUEST_CALCULATION; } - private boolean validateExpression(String expression){ + public static boolean validateExpression(String expression){ AtomicInteger index = new AtomicInteger(0); long countOfValidOps = Arrays.stream(expression.split(" ")) - .filter(e -> index.getAndIncrement() % 2 == 0 ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) + .filter(e -> isEvenNumber(index) ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) .count(); return countOfValidOps >= MINIMUM_OPS && Arrays.stream(expression.split(" ")).count() == countOfValidOps; } + + private static boolean isEvenNumber(AtomicInteger index) { + return index.getAndIncrement() % 2 == 0; + } } diff --git a/src/main/java/calculator/InfixToPostfixConverter.java b/src/main/java/calculator/InfixToPostfixConverter.java index 49d6d47af..d86e019c4 100644 --- a/src/main/java/calculator/InfixToPostfixConverter.java +++ b/src/main/java/calculator/InfixToPostfixConverter.java @@ -36,11 +36,8 @@ public ArrayList convert(String infixExpression) { else { if (Operation.getOperator(opStack.peek()).isSameOrGrater(Operation.getOperator(s))) { postfix.add(opStack.pop()); - opStack.push(s); - } - else { - opStack.push(s); } + opStack.push(s); } } else if (s.matches(OPERAND_REGEX)) { num.append(s); @@ -50,7 +47,6 @@ public ArrayList convert(String infixExpression) { postfix.add(num.toString()); } while(!opStack.isEmpty()){ - System.out.println(opStack.peek()); postfix.add(opStack.pop()); } return postfix; diff --git a/src/test/java/calculator/calculator/ExpressionValidationTest.java b/src/test/java/calculator/calculator/ExpressionValidationTest.java new file mode 100644 index 000000000..7b27ad2c4 --- /dev/null +++ b/src/test/java/calculator/calculator/ExpressionValidationTest.java @@ -0,0 +1,35 @@ +package calculator.calculator; + +import calculator.Calculator; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ExpressionValidationTest { + @Test + @DisplayName("올바른 연산 식을 입력한 경우") + void validExpression(){ + Assertions.assertThat(Calculator.validateExpression("3 + 5 * 2 - 1")) + .isEqualTo(true); + } + @Test + @DisplayName("올바른 연산자를 입력하지 않은 경우") + void invalidOperator(){ + Assertions.assertThat(Calculator.validateExpression("3 _ 5 * 2")) + .isEqualTo(false); + } + + @Test + @DisplayName("올바른 연산 식을 입력하지 않은 경우") + void invalidExpression(){ + Assertions.assertThat(Calculator.validateExpression("- 5 / 5 + 1")) + .isEqualTo(false); + } + + @Test + @DisplayName("분모가 0 또는 음수인 경우 에러 처리") + void invalidArithmeticOperation(){ + Assertions.assertThat(Calculator.validateExpression("3 + 5 / -1")) + .isEqualTo(false); + } +} diff --git a/src/test/java/calculator/calculator/InputValidationTest.java b/src/test/java/calculator/calculator/InputValidationTest.java deleted file mode 100644 index 0ff1a502c..000000000 --- a/src/test/java/calculator/calculator/InputValidationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package calculator.calculator; - -import calculator.Calculator; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class InputValidationTest { - @Test - @DisplayName("올바른 번호를 입력했는지 검사") - void validateInputChoice(){ - - } -} diff --git a/src/test/java/calculator/operator/OperatorApplyTest.java b/src/test/java/calculator/operator/OperatorApplyTest.java index 3d07f2520..5409880b9 100644 --- a/src/test/java/calculator/operator/OperatorApplyTest.java +++ b/src/test/java/calculator/operator/OperatorApplyTest.java @@ -13,12 +13,10 @@ void confirm(){ int a = 10; int b = -5; - - operation.getOperatorMap().entrySet() + operation.getOperatorMap().entrySet() .forEach(e -> { System.out.print(a + e.getKey() + b + " = "); System.out.println(operation.calculate(a, e.getKey(), b)); }); } - } From a0fe5269dc03c5195265627a0babac1b9e5f892f Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 09:13:50 +0900 Subject: [PATCH 08/30] =?UTF-8?q?[add]:=20switch-case=20=EB=B6=84=EA=B8=B0?= =?UTF-8?q?=EB=AC=B8=20continue=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0=20=EB=B0=8F=20=EB=A6=AC=ED=8C=B0?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 15 +++++++++------ .../java/calculator/global/ErrorResponse.java | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 7beef4eb5..7e4799201 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -30,22 +30,25 @@ public Calculator(ExpressionConverter expressionConverter, Input input, Output o @Override public void run() { - while(true) { + while (true) { output.putMenu(); String inputString = input.getChoice(CHOICE_PROMPT); if (!validateChoiceInput(inputString)) output.inputError(MENU_INPUT_ERROR); switch (inputString.charAt(FIRST_INDEX)) { - case REQUEST_VIEW_CALCULATION_RESULT: - output.showCalculationResult(calculationRepository.findAll()); - - case REQUEST_CALCULATION: + case REQUEST_VIEW_CALCULATION_RESULT -> output.showCalculationResult(calculationRepository.findAll()); + case REQUEST_CALCULATION -> { String expression = input.getExpression(); - if (validateExpression(expression)) { + if (!validateExpression(expression)) output.inputError(INVALID_INPUT_EXPRESSION); + else { ArrayList postfixList = expressionConverter.convert(expression); Integer result = calculateFromPostfix(postfixList); + output.showResult(result); calculationRepository.save(expression + " = " + result); } + } + default -> { + } } } } diff --git a/src/main/java/calculator/global/ErrorResponse.java b/src/main/java/calculator/global/ErrorResponse.java index 7b4808199..78e7e7f2a 100644 --- a/src/main/java/calculator/global/ErrorResponse.java +++ b/src/main/java/calculator/global/ErrorResponse.java @@ -4,4 +4,5 @@ public class ErrorResponse { public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; public static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; + public static final String INVALID_INPUT_EXPRESSION = "올바른 수식 표현이 아닙니다."; } From 5bfc293c18c461429bd8b819102a72254a527ee4 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 09:14:48 +0900 Subject: [PATCH 09/30] =?UTF-8?q?[feat]:=20=EA=B3=84=EC=82=B0=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorConsole.java | 10 +++++++++- src/main/java/calculator/io/Output.java | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/CalculatorConsole.java b/src/main/java/CalculatorConsole.java index 490d9a9ee..6726e246b 100644 --- a/src/main/java/CalculatorConsole.java +++ b/src/main/java/CalculatorConsole.java @@ -16,12 +16,19 @@ public void putMenu() { @Override public void showCalculationResult(LinkedHashMap calculationResult) { + System.out.println(); calculationResult.values().forEach(System.out::println); + System.out.println(); } @Override public void inputError(String errorResponse) { - throw new IllegalArgumentException(errorResponse); + System.out.println(errorResponse); + } + + @Override + public void showResult(Integer calculationResult) { + System.out.println(calculationResult + "\n"); } @Override @@ -32,6 +39,7 @@ public String getChoice(String prompt) { @Override public String getExpression() { + System.out.println(); return scanner.nextLine(); } } diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java index 770386b5d..f9513951b 100644 --- a/src/main/java/calculator/io/Output.java +++ b/src/main/java/calculator/io/Output.java @@ -6,4 +6,5 @@ public interface Output { void putMenu(); void showCalculationResult(LinkedHashMap calculationResult); void inputError(String errorResponse); + void showResult(Integer calculationResult); } From a9cee87111b6875994831e5a3c6b0902ab996200 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 09:23:19 +0900 Subject: [PATCH 10/30] =?UTF-8?q?[fix]:=20menu=20Input=20=EC=98=AC?= =?UTF-8?q?=EB=B0=94=EB=A5=B8=20=EA=B0=92=20=EC=95=84=EB=8B=8C=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorConsole.java | 2 +- src/main/java/calculator/Calculator.java | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/CalculatorConsole.java b/src/main/java/CalculatorConsole.java index 6726e246b..a148e7bfc 100644 --- a/src/main/java/CalculatorConsole.java +++ b/src/main/java/CalculatorConsole.java @@ -23,7 +23,7 @@ public void showCalculationResult(LinkedHashMap calculationResu @Override public void inputError(String errorResponse) { - System.out.println(errorResponse); + System.out.println(errorResponse + "\n"); } @Override diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 7e4799201..bae9a6cf6 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -33,7 +33,10 @@ public void run() { while (true) { output.putMenu(); String inputString = input.getChoice(CHOICE_PROMPT); - if (!validateChoiceInput(inputString)) output.inputError(MENU_INPUT_ERROR); + if (!validateChoiceInput(inputString)){ + output.inputError(MENU_INPUT_ERROR); + continue; + } switch (inputString.charAt(FIRST_INDEX)) { case REQUEST_VIEW_CALCULATION_RESULT -> output.showCalculationResult(calculationRepository.findAll()); @@ -75,10 +78,9 @@ public Integer calculateFromPostfix(ArrayList postfixExpression){ } public static boolean validateChoiceInput(String input){ + if (input.length() != MENU_INPUT_LENGTH) return false; char firstChar = input.charAt(FIRST_INDEX); - return input.length() == MENU_INPUT_LENGTH - && firstChar == REQUEST_VIEW_CALCULATION_RESULT - || firstChar == REQUEST_CALCULATION; + return firstChar == REQUEST_VIEW_CALCULATION_RESULT || firstChar == REQUEST_CALCULATION; } public static boolean validateExpression(String expression){ AtomicInteger index = new AtomicInteger(0); From ee811b98f0fb7b48ad450607b0129c8eedc9812e Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sat, 10 Jun 2023 09:29:23 +0900 Subject: [PATCH 11/30] =?UTF-8?q?[refactor]:=20LinkedHashMap=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=84=EC=B2=B4=20=EA=B3=84=EC=82=B0=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20List=20?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorConsole.java | 6 +++--- src/main/java/calculator/io/Output.java | 4 ++-- .../java/calculator/repository/CalculationRepository.java | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/CalculatorConsole.java b/src/main/java/CalculatorConsole.java index a148e7bfc..1b3a22e43 100644 --- a/src/main/java/CalculatorConsole.java +++ b/src/main/java/CalculatorConsole.java @@ -3,7 +3,7 @@ import calculator.io.Output; import java.util.Arrays; -import java.util.LinkedHashMap; +import java.util.List; import java.util.Scanner; public class CalculatorConsole implements Input, Output { @@ -15,9 +15,9 @@ public void putMenu() { } @Override - public void showCalculationResult(LinkedHashMap calculationResult) { + public void showCalculationResult(List calcResults) { System.out.println(); - calculationResult.values().forEach(System.out::println); + calcResults.forEach(System.out::println); System.out.println(); } diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java index f9513951b..772c7ef87 100644 --- a/src/main/java/calculator/io/Output.java +++ b/src/main/java/calculator/io/Output.java @@ -1,10 +1,10 @@ package calculator.io; -import java.util.LinkedHashMap; +import java.util.List; public interface Output { void putMenu(); - void showCalculationResult(LinkedHashMap calculationResult); + void showCalculationResult(List calcResults); void inputError(String errorResponse); void showResult(Integer calculationResult); } diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index d1bb41219..cc785a697 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -1,6 +1,8 @@ package calculator.repository; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; public class CalculationRepository { private final static LinkedHashMap results = new LinkedHashMap<>(); @@ -9,7 +11,7 @@ public void save(String calculation) { results.put(results.size() + 1, calculation); } - public LinkedHashMap findAll() { - return results; + public List findAll() { + return new ArrayList<>(results.values()); } } From 55d942ec02cdd694f1e0f5b8a31e70dc055b600b Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sun, 11 Jun 2023 17:46:54 +0900 Subject: [PATCH 12/30] =?UTF-8?q?[add]:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=A2=85=EB=A3=8C=20=EA=B0=80=EB=8A=A5=ED=95=9C=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=B6=94=EA=B0=80=ED=95=98=EA=B8=B0=20-?= =?UTF-8?q?=20Menu=EC=9D=98=20command=EB=A5=BC=20Character=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20-=20=EC=A2=85=EB=A3=8C=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20IO=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 12 +++++++----- src/main/java/calculator/global/InputConstants.java | 2 ++ src/main/java/calculator/global/Menu.java | 13 +++++++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index bae9a6cf6..bb5792216 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,5 +1,6 @@ package calculator; +import calculator.global.Menu; import calculator.io.ExpressionConverter; import calculator.io.Input; import calculator.io.Output; @@ -30,7 +31,8 @@ public Calculator(ExpressionConverter expressionConverter, Input input, Output o @Override public void run() { - while (true) { + boolean isProgramRunnable = true; + while (isProgramRunnable) { output.putMenu(); String inputString = input.getChoice(CHOICE_PROMPT); if (!validateChoiceInput(inputString)){ @@ -39,6 +41,7 @@ public void run() { } switch (inputString.charAt(FIRST_INDEX)) { + case REQUEST_EXIT -> isProgramRunnable = false; case REQUEST_VIEW_CALCULATION_RESULT -> output.showCalculationResult(calculationRepository.findAll()); case REQUEST_CALCULATION -> { String expression = input.getExpression(); @@ -50,8 +53,6 @@ public void run() { calculationRepository.save(expression + " = " + result); } } - default -> { - } } } } @@ -79,8 +80,9 @@ public Integer calculateFromPostfix(ArrayList postfixExpression){ public static boolean validateChoiceInput(String input){ if (input.length() != MENU_INPUT_LENGTH) return false; - char firstChar = input.charAt(FIRST_INDEX); - return firstChar == REQUEST_VIEW_CALCULATION_RESULT || firstChar == REQUEST_CALCULATION; + Character firstChar = input.charAt(FIRST_INDEX); + return Arrays.stream(Menu.values()).filter(m -> m.getCommand() + .equals(firstChar)).count() == 1; } public static boolean validateExpression(String expression){ AtomicInteger index = new AtomicInteger(0); diff --git a/src/main/java/calculator/global/InputConstants.java b/src/main/java/calculator/global/InputConstants.java index ae2398ce0..8a57aa534 100644 --- a/src/main/java/calculator/global/InputConstants.java +++ b/src/main/java/calculator/global/InputConstants.java @@ -4,6 +4,8 @@ public class InputConstants { public static final String CHOICE_PROMPT = "\n선택 : "; public static final int FIRST_INDEX = 0; public static final int MENU_INPUT_LENGTH = 1; + + public static final char REQUEST_EXIT = '0'; public static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; public static final char REQUEST_CALCULATION = '2'; diff --git a/src/main/java/calculator/global/Menu.java b/src/main/java/calculator/global/Menu.java index 4b8e3e63f..c2c25d98c 100644 --- a/src/main/java/calculator/global/Menu.java +++ b/src/main/java/calculator/global/Menu.java @@ -1,18 +1,23 @@ package calculator.global; public enum Menu { - GET(1, "조회"), - CALCULATE(2, "계산"); + EXIT('0', "종료"), + CALCULATION_HISTORY('1', "조회"), + CALCULATE('2', "계산"); - private final Integer command; + private final Character command; private final String explanation; - Menu(Integer command, String explanation) { + Menu(Character command, String explanation) { this.command = command; this.explanation = explanation; } + public Character getCommand(){ + return command; + } + @Override public String toString() { return command + ". " + explanation; From 7464a0137742dd5ae9f6a7e6bc063bf3dc181e41 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sun, 11 Jun 2023 21:18:50 +0900 Subject: [PATCH 13/30] =?UTF-8?q?[refactor]:=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=AA=A8=EB=8D=B8=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?(CalculationResult=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20expression,=20result=20=EA=B2=B0=EA=B3=BC?= =?UTF-8?q?=EA=B0=92=20=EB=94=B0=EB=A1=9C=20=EC=A0=80=EC=9E=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/calculator/model/CalculationResult.java | 16 ++++++++++++++++ .../repository/CalculationRepository.java | 10 ++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/main/java/calculator/model/CalculationResult.java diff --git a/src/main/java/calculator/model/CalculationResult.java b/src/main/java/calculator/model/CalculationResult.java new file mode 100644 index 000000000..b5f5e5725 --- /dev/null +++ b/src/main/java/calculator/model/CalculationResult.java @@ -0,0 +1,16 @@ +package calculator.model; + +public class CalculationResult { + private final String expression; + private final String answer; + + public CalculationResult(String expression, String answer){ + this.expression = expression; + this.answer = answer; + } + + @Override + public String toString() { + return expression + " = " + answer; + } +} diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index cc785a697..770198fd6 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -1,17 +1,19 @@ package calculator.repository; +import calculator.model.CalculationResult; + import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; public class CalculationRepository { - private final static LinkedHashMap results = new LinkedHashMap<>(); + private final static LinkedHashMap results = new LinkedHashMap<>(); - public void save(String calculation) { - results.put(results.size() + 1, calculation); + public void save(CalculationResult result) { + results.put(results.size() + 1, result); } - public List findAll() { + public List findAll() { return new ArrayList<>(results.values()); } } From e168cbd18f203daf5796f752831b48ed8dac0809 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sun, 11 Jun 2023 21:21:02 +0900 Subject: [PATCH 14/30] =?UTF-8?q?[refactor]:=20Validator=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC,=20ExpresionConverter=EA=B3=BC=20Calculator=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=ED=99=94=20-=20=EB=8B=A4=EC=96=91=ED=95=9C?= =?UTF-8?q?=20=EB=B0=A9=EB=B2=95=EC=9D=98=20=EC=88=98=EC=8B=9D(=EC=A0=84?= =?UTF-8?q?=EC=9C=84,=EC=A4=91=EC=9C=84,=ED=9B=84=EC=9C=84=20=ED=91=9C?= =?UTF-8?q?=ED=98=84)=EC=9D=84=20convert=ED=95=98=EC=97=AC=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20str?= =?UTF-8?q?ategy=20pattern=20=EB=8F=84=EC=9E=85=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorApp.java | 16 +++-- src/main/java/calculator/Calculator.java | 70 +++++-------------- .../{ => calculator}/CalculatorConsole.java | 10 ++- src/main/java/calculator/io/Output.java | 6 +- .../calculator/model/BasicCalculator.java | 7 ++ .../calculator/model/PostfixCalculator.java | 30 ++++++++ .../calculator/util/ExpressionConverter.java | 7 ++ .../converter}/InfixToPostfixConverter.java | 7 +- .../util/validator/CalculatorValidator.java | 28 ++++++++ .../calculator/ExpressionValidationTest.java | 9 +-- .../infixtopostfix/InfixToArrayListTest.java | 2 +- 11 files changed, 119 insertions(+), 73 deletions(-) rename src/main/java/{ => calculator}/CalculatorConsole.java (78%) create mode 100644 src/main/java/calculator/model/BasicCalculator.java create mode 100644 src/main/java/calculator/model/PostfixCalculator.java create mode 100644 src/main/java/calculator/util/ExpressionConverter.java rename src/main/java/calculator/{ => util/converter}/InfixToPostfixConverter.java (92%) create mode 100644 src/main/java/calculator/util/validator/CalculatorValidator.java diff --git a/src/main/java/CalculatorApp.java b/src/main/java/CalculatorApp.java index d7ee6a4c2..bdac95824 100644 --- a/src/main/java/CalculatorApp.java +++ b/src/main/java/CalculatorApp.java @@ -1,15 +1,19 @@ import calculator.Calculator; -import calculator.InfixToPostfixConverter; +import calculator.CalculatorConsole; +import calculator.model.PostfixCalculator; +import calculator.util.converter.InfixToPostfixConverter; import calculator.repository.CalculationRepository; public class CalculatorApp { public static void main(String[] args) { CalculatorConsole console = new CalculatorConsole(); - InfixToPostfixConverter infixToPostfixConvertor = new InfixToPostfixConverter(); - CalculationRepository repository = new CalculationRepository(); - - new Calculator(infixToPostfixConvertor, console, console, repository).run(); - + new Calculator( + new PostfixCalculator(), + new InfixToPostfixConverter(), + console, + console, + new CalculationRepository() + ).run(); } } diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index bb5792216..8d439fca6 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,27 +1,27 @@ package calculator; -import calculator.global.Menu; -import calculator.io.ExpressionConverter; +import calculator.model.CalculationResult; +import calculator.util.ExpressionConverter; import calculator.io.Input; import calculator.io.Output; -import calculator.model.Operation; +import calculator.model.BasicCalculator; import calculator.repository.CalculationRepository; +import calculator.util.validator.CalculatorValidator; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Stack; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.List; import static calculator.global.ErrorResponse.*; import static calculator.global.InputConstants.*; public class Calculator implements Runnable { + private final BasicCalculator calculator; private final ExpressionConverter expressionConverter; private final Input input; private final Output output; private final CalculationRepository calculationRepository; - public Calculator(ExpressionConverter expressionConverter, Input input, Output output, CalculationRepository calculationRepository) { + public Calculator(BasicCalculator calculator, ExpressionConverter expressionConverter, Input input, Output output, CalculationRepository calculationRepository) { + this.calculator = calculator; this.expressionConverter = expressionConverter; this.input = input; this.output = output; @@ -35,7 +35,7 @@ public void run() { while (isProgramRunnable) { output.putMenu(); String inputString = input.getChoice(CHOICE_PROMPT); - if (!validateChoiceInput(inputString)){ + if (!CalculatorValidator.isValidChoice(inputString)) { output.inputError(MENU_INPUT_ERROR); continue; } @@ -45,54 +45,16 @@ public void run() { case REQUEST_VIEW_CALCULATION_RESULT -> output.showCalculationResult(calculationRepository.findAll()); case REQUEST_CALCULATION -> { String expression = input.getExpression(); - if (!validateExpression(expression)) output.inputError(INVALID_INPUT_EXPRESSION); - else { - ArrayList postfixList = expressionConverter.convert(expression); - Integer result = calculateFromPostfix(postfixList); - output.showResult(result); - calculationRepository.save(expression + " = " + result); + if (!CalculatorValidator.isValidExpression(expression)) { + output.inputError(INVALID_INPUT_EXPRESSION); + continue; } + List convertedExpression = expressionConverter.convert(expression); + Object result = calculator.calculate(convertedExpression); + output.showResult(result.toString()); + calculationRepository.save(new CalculationResult(expression, result.toString())); } } } } - - public Integer calculateFromPostfix(ArrayList postfixExpression){ - Stack calcStack = new Stack<>(); - Operation operation = new Operation(); - - int op1, op2; - - for(String s : postfixExpression){ - if(s.matches(OPERATOR_REGEX)){ - op2 = Integer.parseInt(calcStack.pop()); - op1 = Integer.parseInt(calcStack.pop()); - - Integer result = operation.calculate(op1, s, op2); - calcStack.push(String.valueOf(result)); - } - else{ - calcStack.push(s); - } - } - return Integer.valueOf(calcStack.pop()); - } - - public static boolean validateChoiceInput(String input){ - if (input.length() != MENU_INPUT_LENGTH) return false; - Character firstChar = input.charAt(FIRST_INDEX); - return Arrays.stream(Menu.values()).filter(m -> m.getCommand() - .equals(firstChar)).count() == 1; - } - public static boolean validateExpression(String expression){ - AtomicInteger index = new AtomicInteger(0); - long countOfValidOps = Arrays.stream(expression.split(" ")) - .filter(e -> isEvenNumber(index) ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) - .count(); - return countOfValidOps >= MINIMUM_OPS && Arrays.stream(expression.split(" ")).count() == countOfValidOps; - } - - private static boolean isEvenNumber(AtomicInteger index) { - return index.getAndIncrement() % 2 == 0; - } } diff --git a/src/main/java/CalculatorConsole.java b/src/main/java/calculator/CalculatorConsole.java similarity index 78% rename from src/main/java/CalculatorConsole.java rename to src/main/java/calculator/CalculatorConsole.java index 1b3a22e43..9645f0516 100644 --- a/src/main/java/CalculatorConsole.java +++ b/src/main/java/calculator/CalculatorConsole.java @@ -1,13 +1,17 @@ +package calculator; + import calculator.global.Menu; import calculator.io.Input; import calculator.io.Output; +import calculator.model.CalculationResult; import java.util.Arrays; import java.util.List; import java.util.Scanner; public class CalculatorConsole implements Input, Output { - private final Scanner scanner = new Scanner(System.in); + private final static Scanner scanner = new Scanner(System.in); + @Override public void putMenu() { Arrays.stream(Menu.values()) @@ -15,7 +19,7 @@ public void putMenu() { } @Override - public void showCalculationResult(List calcResults) { + public void showCalculationResult(List calcResults) { System.out.println(); calcResults.forEach(System.out::println); System.out.println(); @@ -27,7 +31,7 @@ public void inputError(String errorResponse) { } @Override - public void showResult(Integer calculationResult) { + public void showResult(String calculationResult) { System.out.println(calculationResult + "\n"); } diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java index 772c7ef87..936d8e1eb 100644 --- a/src/main/java/calculator/io/Output.java +++ b/src/main/java/calculator/io/Output.java @@ -1,10 +1,12 @@ package calculator.io; +import calculator.model.CalculationResult; + import java.util.List; public interface Output { void putMenu(); - void showCalculationResult(List calcResults); + void showCalculationResult(List calcResults); void inputError(String errorResponse); - void showResult(Integer calculationResult); + void showResult(String calculationResult); } diff --git a/src/main/java/calculator/model/BasicCalculator.java b/src/main/java/calculator/model/BasicCalculator.java new file mode 100644 index 000000000..e1d82f5f6 --- /dev/null +++ b/src/main/java/calculator/model/BasicCalculator.java @@ -0,0 +1,7 @@ +package calculator.model; + +import java.util.List; + +public interface BasicCalculator { + Object calculate(List expression); +} diff --git a/src/main/java/calculator/model/PostfixCalculator.java b/src/main/java/calculator/model/PostfixCalculator.java new file mode 100644 index 000000000..edaf049c7 --- /dev/null +++ b/src/main/java/calculator/model/PostfixCalculator.java @@ -0,0 +1,30 @@ +package calculator.model; + +import java.util.List; +import java.util.Stack; + +import static calculator.global.InputConstants.OPERATOR_REGEX; + +public class PostfixCalculator implements BasicCalculator{ + @Override + public Integer calculate(List expression) { + Stack calcStack = new Stack<>(); + Operation operation = new Operation(); + + int op1, op2; + + for(String s : expression){ + if(s.matches(OPERATOR_REGEX)){ + op2 = Integer.parseInt(calcStack.pop()); + op1 = Integer.parseInt(calcStack.pop()); + + Integer result = operation.calculate(op1, s, op2); + calcStack.push(String.valueOf(result)); + } + else{ + calcStack.push(s); + } + } + return Integer.valueOf(calcStack.pop()); + } +} diff --git a/src/main/java/calculator/util/ExpressionConverter.java b/src/main/java/calculator/util/ExpressionConverter.java new file mode 100644 index 000000000..fc031c8a0 --- /dev/null +++ b/src/main/java/calculator/util/ExpressionConverter.java @@ -0,0 +1,7 @@ +package calculator.util; + +import java.util.List; + +public interface ExpressionConverter { + List convert(String infixExpression); +} diff --git a/src/main/java/calculator/InfixToPostfixConverter.java b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java similarity index 92% rename from src/main/java/calculator/InfixToPostfixConverter.java rename to src/main/java/calculator/util/converter/InfixToPostfixConverter.java index d86e019c4..19a83addd 100644 --- a/src/main/java/calculator/InfixToPostfixConverter.java +++ b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java @@ -1,11 +1,12 @@ -package calculator; +package calculator.util.converter; -import calculator.io.ExpressionConverter; import calculator.model.Operation; +import calculator.util.ExpressionConverter; import lombok.ToString; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Stack; import static calculator.global.InputConstants.OPERAND_REGEX; @@ -13,7 +14,7 @@ @ToString public class InfixToPostfixConverter implements ExpressionConverter { - private ArrayList infix; + private List infix; private static final ArrayList postfix = new ArrayList<>(); private static final Stack opStack = new Stack<>(); diff --git a/src/main/java/calculator/util/validator/CalculatorValidator.java b/src/main/java/calculator/util/validator/CalculatorValidator.java new file mode 100644 index 000000000..7d732b0af --- /dev/null +++ b/src/main/java/calculator/util/validator/CalculatorValidator.java @@ -0,0 +1,28 @@ +package calculator.util.validator; + +import calculator.global.Menu; + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import static calculator.global.InputConstants.*; + +public class CalculatorValidator { + public static boolean isValidChoice(String input){ + if (input.length() != MENU_INPUT_LENGTH) return false; + Character firstChar = input.charAt(FIRST_INDEX); + return Arrays.stream(Menu.values()).filter(m -> m.getCommand() + .equals(firstChar)).count() == 1; + } + public static boolean isValidExpression(String expression){ + AtomicInteger index = new AtomicInteger(0); + long countOfValidOps = Arrays.stream(expression.split(" ")) + .filter(e -> isEvenNumber(index) ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) + .count(); + return countOfValidOps >= MINIMUM_OPS && Arrays.stream(expression.split(" ")).count() == countOfValidOps; + } + + private static boolean isEvenNumber(AtomicInteger index) { + return index.getAndIncrement() % 2 == 0; + } +} diff --git a/src/test/java/calculator/calculator/ExpressionValidationTest.java b/src/test/java/calculator/calculator/ExpressionValidationTest.java index 7b27ad2c4..5a7c4a6fb 100644 --- a/src/test/java/calculator/calculator/ExpressionValidationTest.java +++ b/src/test/java/calculator/calculator/ExpressionValidationTest.java @@ -1,6 +1,7 @@ package calculator.calculator; import calculator.Calculator; +import calculator.util.validator.CalculatorValidator; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,27 +10,27 @@ public class ExpressionValidationTest { @Test @DisplayName("올바른 연산 식을 입력한 경우") void validExpression(){ - Assertions.assertThat(Calculator.validateExpression("3 + 5 * 2 - 1")) + Assertions.assertThat(CalculatorValidator.isValidExpression("3 + 5 * 2 - 1")) .isEqualTo(true); } @Test @DisplayName("올바른 연산자를 입력하지 않은 경우") void invalidOperator(){ - Assertions.assertThat(Calculator.validateExpression("3 _ 5 * 2")) + Assertions.assertThat(CalculatorValidator.isValidExpression("3 _ 5 * 2")) .isEqualTo(false); } @Test @DisplayName("올바른 연산 식을 입력하지 않은 경우") void invalidExpression(){ - Assertions.assertThat(Calculator.validateExpression("- 5 / 5 + 1")) + Assertions.assertThat(CalculatorValidator.isValidExpression("- 5 / 5 + 1")) .isEqualTo(false); } @Test @DisplayName("분모가 0 또는 음수인 경우 에러 처리") void invalidArithmeticOperation(){ - Assertions.assertThat(Calculator.validateExpression("3 + 5 / -1")) + Assertions.assertThat(CalculatorValidator.isValidExpression("3 + 5 / -1")) .isEqualTo(false); } } diff --git a/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java b/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java index 957fea2d9..6950c81a4 100644 --- a/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java +++ b/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java @@ -1,6 +1,6 @@ package calculator.infixtopostfix; -import calculator.InfixToPostfixConverter; +import calculator.util.converter.InfixToPostfixConverter; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 2593643240c008c4ec5d6b2cb3934823cbab8755 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Sun, 11 Jun 2023 21:21:11 +0900 Subject: [PATCH 15/30] =?UTF-8?q?[refactor]:=20Validator=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC,=20ExpresionConverter=EA=B3=BC=20Calculator=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=ED=99=94=20-=20=EB=8B=A4=EC=96=91=ED=95=9C?= =?UTF-8?q?=20=EB=B0=A9=EB=B2=95=EC=9D=98=20=EC=88=98=EC=8B=9D(=EC=A0=84?= =?UTF-8?q?=EC=9C=84,=EC=A4=91=EC=9C=84,=ED=9B=84=EC=9C=84=20=ED=91=9C?= =?UTF-8?q?=ED=98=84)=EC=9D=84=20convert=ED=95=98=EC=97=AC=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20str?= =?UTF-8?q?ategy=20pattern=20=EB=8F=84=EC=9E=85=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/io/ExpressionConverter.java | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 src/main/java/calculator/io/ExpressionConverter.java diff --git a/src/main/java/calculator/io/ExpressionConverter.java b/src/main/java/calculator/io/ExpressionConverter.java deleted file mode 100644 index 12b780b05..000000000 --- a/src/main/java/calculator/io/ExpressionConverter.java +++ /dev/null @@ -1,7 +0,0 @@ -package calculator.io; - -import java.util.ArrayList; - -public interface ExpressionConverter { - ArrayList convert(String infixExpression); -} From a0e17edbe37baf95e1e2d9ff62dfa15563660ea3 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Tue, 13 Jun 2023 12:31:30 +0900 Subject: [PATCH 16/30] =?UTF-8?q?[refactor]:=20Constants=20=EC=83=81?= =?UTF-8?q?=EC=88=98=EB=93=A4=20=EA=B0=9D=EC=B2=B4=20=EB=82=B4=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99,=20Menu=20validator=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 21 +++++++++++------ .../java/calculator/global/ErrorResponse.java | 8 ------- .../calculator/global/InputConstants.java | 16 ------------- src/main/java/calculator/global/Priority.java | 6 ----- .../calculator/{global => model}/Menu.java | 13 ++++++++++- src/main/java/calculator/model/Operation.java | 23 +++++++------------ .../calculator/model/PostfixCalculator.java | 4 ++-- .../converter/InfixToPostfixConverter.java | 19 ++++++++------- .../util/validator/CalculatorValidator.java | 14 ++++------- .../operator/OperatorApplyTest.java | 22 ------------------ 10 files changed, 50 insertions(+), 96 deletions(-) delete mode 100644 src/main/java/calculator/global/ErrorResponse.java delete mode 100644 src/main/java/calculator/global/InputConstants.java delete mode 100644 src/main/java/calculator/global/Priority.java rename src/main/java/calculator/{global => model}/Menu.java (51%) delete mode 100644 src/test/java/calculator/operator/OperatorApplyTest.java diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 8d439fca6..4b7b43a43 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,5 +1,6 @@ package calculator; +import calculator.model.Menu; import calculator.model.CalculationResult; import calculator.util.ExpressionConverter; import calculator.io.Input; @@ -10,8 +11,6 @@ import java.util.List; -import static calculator.global.ErrorResponse.*; -import static calculator.global.InputConstants.*; public class Calculator implements Runnable { private final BasicCalculator calculator; @@ -20,6 +19,14 @@ public class Calculator implements Runnable { private final Output output; private final CalculationRepository calculationRepository; + public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; + public static final String INVALID_INPUT_EXPRESSION = "올바른 수식 표현이 아닙니다."; + public static final char REQUEST_EXIT = '0'; + public static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; + public static final char REQUEST_CALCULATION = '2'; + public static final String CHOICE_PROMPT = "\n선택 : "; + public static final int FIRST_INDEX = 0; + public Calculator(BasicCalculator calculator, ExpressionConverter expressionConverter, Input input, Output output, CalculationRepository calculationRepository) { this.calculator = calculator; this.expressionConverter = expressionConverter; @@ -34,13 +41,13 @@ public void run() { boolean isProgramRunnable = true; while (isProgramRunnable) { output.putMenu(); - String inputString = input.getChoice(CHOICE_PROMPT); - if (!CalculatorValidator.isValidChoice(inputString)) { + String inputMenu = input.getChoice(CHOICE_PROMPT); + if (!Menu.isValidMenu(inputMenu)) { output.inputError(MENU_INPUT_ERROR); continue; } - switch (inputString.charAt(FIRST_INDEX)) { + switch (inputMenu.charAt(FIRST_INDEX)) { case REQUEST_EXIT -> isProgramRunnable = false; case REQUEST_VIEW_CALCULATION_RESULT -> output.showCalculationResult(calculationRepository.findAll()); case REQUEST_CALCULATION -> { @@ -50,9 +57,9 @@ public void run() { continue; } List convertedExpression = expressionConverter.convert(expression); - Object result = calculator.calculate(convertedExpression); + Integer result = calculator.calculate(convertedExpression); output.showResult(result.toString()); - calculationRepository.save(new CalculationResult(expression, result.toString())); + calculationRepository.save(new CalculationResult(expression, result)); } } } diff --git a/src/main/java/calculator/global/ErrorResponse.java b/src/main/java/calculator/global/ErrorResponse.java deleted file mode 100644 index 78e7e7f2a..000000000 --- a/src/main/java/calculator/global/ErrorResponse.java +++ /dev/null @@ -1,8 +0,0 @@ -package calculator.global; - -public class ErrorResponse { - public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; - public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; - public static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; - public static final String INVALID_INPUT_EXPRESSION = "올바른 수식 표현이 아닙니다."; -} diff --git a/src/main/java/calculator/global/InputConstants.java b/src/main/java/calculator/global/InputConstants.java deleted file mode 100644 index 8a57aa534..000000000 --- a/src/main/java/calculator/global/InputConstants.java +++ /dev/null @@ -1,16 +0,0 @@ -package calculator.global; - -public class InputConstants { - public static final String CHOICE_PROMPT = "\n선택 : "; - public static final int FIRST_INDEX = 0; - public static final int MENU_INPUT_LENGTH = 1; - - public static final char REQUEST_EXIT = '0'; - public static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; - public static final char REQUEST_CALCULATION = '2'; - - public static final String OPERATOR_REGEX = "[-*/+]"; - public static final String OPERAND_REGEX = "^\\d+$"; - - public static final int MINIMUM_OPS = 3; -} diff --git a/src/main/java/calculator/global/Priority.java b/src/main/java/calculator/global/Priority.java deleted file mode 100644 index c0a25defb..000000000 --- a/src/main/java/calculator/global/Priority.java +++ /dev/null @@ -1,6 +0,0 @@ -package calculator.global; - -public class Priority { - public static final Integer LOW = 0; - public static final Integer HIGH = 1; -} diff --git a/src/main/java/calculator/global/Menu.java b/src/main/java/calculator/model/Menu.java similarity index 51% rename from src/main/java/calculator/global/Menu.java rename to src/main/java/calculator/model/Menu.java index c2c25d98c..ef346cee2 100644 --- a/src/main/java/calculator/global/Menu.java +++ b/src/main/java/calculator/model/Menu.java @@ -1,4 +1,6 @@ -package calculator.global; +package calculator.model; + +import java.util.Arrays; public enum Menu { EXIT('0', "종료"), @@ -8,6 +10,8 @@ public enum Menu { private final Character command; private final String explanation; + private static final Integer MENU_INPUT_LENGTH = 1; + private static final Integer FIRST_INDEX = 0; Menu(Character command, String explanation) { this.command = command; @@ -18,6 +22,13 @@ public Character getCommand(){ return command; } + public static boolean isValidMenu(String input){ + if (input.length() != MENU_INPUT_LENGTH) return false; + Character firstChar = input.charAt(FIRST_INDEX); + return Arrays.stream(Menu.values()).filter(m -> m.getCommand() + .equals(firstChar)).count() == 1; + } + @Override public String toString() { return command + ". " + explanation; diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/Operation.java index ac873a66c..56de19e3d 100644 --- a/src/main/java/calculator/model/Operation.java +++ b/src/main/java/calculator/model/Operation.java @@ -6,28 +6,21 @@ import java.util.Optional; import java.util.function.BiFunction; -import static calculator.global.ErrorResponse.INVALID_OPERAND; -import static calculator.global.ErrorResponse.OPERATOR_INPUT_ERROR; -import static calculator.global.Priority.HIGH; -import static calculator.global.Priority.LOW; public class Operation { private static final Map operatorMap = new HashMap<>(); - public Operation() { - setOperatorMap(); - } + public static final Integer LOW = 0; + public static final Integer HIGH = 1; - private void setOperatorMap(){ + public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; + public static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; + + public Operation() { Arrays.stream(Operator.values()) .forEach(op -> operatorMap.put(op.operator, op)); } - - public Map getOperatorMap() { - return operatorMap; - } - public Integer calculate(Integer a, String operator, Integer b){ return Optional.ofNullable(operatorMap.get(operator)) .orElseThrow(() -> new IllegalArgumentException(OPERATOR_INPUT_ERROR)) @@ -57,12 +50,12 @@ public enum Operator { } - public boolean isSameOrGrater(Operator operator) { + public boolean isPrioritySameOrGreater(Operator operator) { return priority >= operator.priority; } public Integer mapCalculate(String operator, Integer num1, Integer num2) { - if(Operation.getOperator(operator) == DIVIDE && num2 <= 0){ + if(Operation.getOperator(operator) == DIVIDE && num2 == 0){ throw new ArithmeticException(INVALID_OPERAND); } return getOperator(operator).expression.apply(num1, num2); diff --git a/src/main/java/calculator/model/PostfixCalculator.java b/src/main/java/calculator/model/PostfixCalculator.java index edaf049c7..7e75c248b 100644 --- a/src/main/java/calculator/model/PostfixCalculator.java +++ b/src/main/java/calculator/model/PostfixCalculator.java @@ -3,9 +3,9 @@ import java.util.List; import java.util.Stack; -import static calculator.global.InputConstants.OPERATOR_REGEX; - public class PostfixCalculator implements BasicCalculator{ + + public static final String OPERATOR_REGEX = "[-*/+]"; @Override public Integer calculate(List expression) { Stack calcStack = new Stack<>(); diff --git a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java index 19a83addd..4aeea418b 100644 --- a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java +++ b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java @@ -5,37 +5,36 @@ import lombok.ToString; import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Stack; -import static calculator.global.InputConstants.OPERAND_REGEX; -import static calculator.global.InputConstants.OPERATOR_REGEX; - @ToString public class InfixToPostfixConverter implements ExpressionConverter { - private List infix; private static final ArrayList postfix = new ArrayList<>(); private static final Stack opStack = new Stack<>(); + private static final String EXPRESSION_DELIMITER = " "; + public static final String OPERATOR_REGEX = "[-*/+]"; + public static final String OPERAND_REGEX = "^\\d+$"; + + @Override public ArrayList convert(String infixExpression) { StringBuilder num = new StringBuilder(); Operation operation = new Operation(); // 중위 표기식을 리스트로 변환 - infix = new ArrayList<>(Arrays.asList(infixExpression.split(" "))); + String[] infix = infixExpression.split(EXPRESSION_DELIMITER); // 후위 표기식으로 전환 for(String s : infix){ if(s.matches(OPERATOR_REGEX)){ - if(!num.toString().equals("")){ + if(!num.isEmpty()){ postfix.add(num.toString()); num = new StringBuilder(); } if (opStack.isEmpty()) opStack.push(s); else { - if (Operation.getOperator(opStack.peek()).isSameOrGrater(Operation.getOperator(s))) { + if (Operation.getOperator(opStack.peek()).isPrioritySameOrGreater(Operation.getOperator(s))) { postfix.add(opStack.pop()); } opStack.push(s); @@ -44,7 +43,7 @@ public ArrayList convert(String infixExpression) { num.append(s); } } - if(!num.toString().equals("")){ + if(!num.isEmpty()) { postfix.add(num.toString()); } while(!opStack.isEmpty()){ diff --git a/src/main/java/calculator/util/validator/CalculatorValidator.java b/src/main/java/calculator/util/validator/CalculatorValidator.java index 7d732b0af..9d58e2b7a 100644 --- a/src/main/java/calculator/util/validator/CalculatorValidator.java +++ b/src/main/java/calculator/util/validator/CalculatorValidator.java @@ -1,19 +1,15 @@ package calculator.util.validator; -import calculator.global.Menu; - import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; -import static calculator.global.InputConstants.*; public class CalculatorValidator { - public static boolean isValidChoice(String input){ - if (input.length() != MENU_INPUT_LENGTH) return false; - Character firstChar = input.charAt(FIRST_INDEX); - return Arrays.stream(Menu.values()).filter(m -> m.getCommand() - .equals(firstChar)).count() == 1; - } + public static final String OPERATOR_REGEX = "[-*/+]"; + public static final String OPERAND_REGEX = "^\\d+$"; + + public static final int MINIMUM_OPS = 3; + public static boolean isValidExpression(String expression){ AtomicInteger index = new AtomicInteger(0); long countOfValidOps = Arrays.stream(expression.split(" ")) diff --git a/src/test/java/calculator/operator/OperatorApplyTest.java b/src/test/java/calculator/operator/OperatorApplyTest.java deleted file mode 100644 index 5409880b9..000000000 --- a/src/test/java/calculator/operator/OperatorApplyTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package calculator.operator; - -import calculator.model.Operation; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class OperatorApplyTest { - @Test - @DisplayName("사칙 연산 Map 테스트") - void confirm(){ - Operation operation = new Operation(); - - int a = 10; - int b = -5; - - operation.getOperatorMap().entrySet() - .forEach(e -> { - System.out.print(a + e.getKey() + b + " = "); - System.out.println(operation.calculate(a, e.getKey(), b)); - }); - } -} From 6738c30b2d7855f7cd7908c4706d13f711e036a4 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Tue, 13 Jun 2023 12:33:13 +0900 Subject: [PATCH 17/30] =?UTF-8?q?[refactor]=20CaclulationResult=20-=20Conc?= =?UTF-8?q?urrentHashMap=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20Object?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Integer=EB=A1=9C=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/CalculatorConsole.java | 8 ++++---- src/main/java/calculator/io/Output.java | 4 ++-- src/main/java/calculator/model/BasicCalculator.java | 2 +- src/main/java/calculator/model/CalculationResult.java | 7 ++++--- .../calculator/repository/CalculationRepository.java | 11 +++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/calculator/CalculatorConsole.java b/src/main/java/calculator/CalculatorConsole.java index 9645f0516..db7bc30d6 100644 --- a/src/main/java/calculator/CalculatorConsole.java +++ b/src/main/java/calculator/CalculatorConsole.java @@ -1,12 +1,12 @@ package calculator; -import calculator.global.Menu; +import calculator.model.Menu; import calculator.io.Input; import calculator.io.Output; import calculator.model.CalculationResult; import java.util.Arrays; -import java.util.List; +import java.util.Map; import java.util.Scanner; public class CalculatorConsole implements Input, Output { @@ -19,9 +19,9 @@ public void putMenu() { } @Override - public void showCalculationResult(List calcResults) { + public void showCalculationResult(Map result) { System.out.println(); - calcResults.forEach(System.out::println); + result.forEach((key, value) -> System.out.println(value)); System.out.println(); } diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java index 936d8e1eb..be66b4339 100644 --- a/src/main/java/calculator/io/Output.java +++ b/src/main/java/calculator/io/Output.java @@ -2,11 +2,11 @@ import calculator.model.CalculationResult; -import java.util.List; +import java.util.Map; public interface Output { void putMenu(); - void showCalculationResult(List calcResults); + void showCalculationResult(Map result); void inputError(String errorResponse); void showResult(String calculationResult); } diff --git a/src/main/java/calculator/model/BasicCalculator.java b/src/main/java/calculator/model/BasicCalculator.java index e1d82f5f6..96d20b3b6 100644 --- a/src/main/java/calculator/model/BasicCalculator.java +++ b/src/main/java/calculator/model/BasicCalculator.java @@ -3,5 +3,5 @@ import java.util.List; public interface BasicCalculator { - Object calculate(List expression); + Integer calculate(List expression); } diff --git a/src/main/java/calculator/model/CalculationResult.java b/src/main/java/calculator/model/CalculationResult.java index b5f5e5725..0a8bf7464 100644 --- a/src/main/java/calculator/model/CalculationResult.java +++ b/src/main/java/calculator/model/CalculationResult.java @@ -2,15 +2,16 @@ public class CalculationResult { private final String expression; - private final String answer; + private final Integer answer; + private static final String CALCULATION_EQUALS_SIGN = " = "; - public CalculationResult(String expression, String answer){ + public CalculationResult(String expression, Integer answer){ this.expression = expression; this.answer = answer; } @Override public String toString() { - return expression + " = " + answer; + return expression + CALCULATION_EQUALS_SIGN + answer; } } diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index 770198fd6..9db898182 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -2,18 +2,17 @@ import calculator.model.CalculationResult; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class CalculationRepository { - private final static LinkedHashMap results = new LinkedHashMap<>(); + private final static Map results = new ConcurrentHashMap<>(); public void save(CalculationResult result) { results.put(results.size() + 1, result); } - public List findAll() { - return new ArrayList<>(results.values()); + public Map findAll() { + return results; } } From 1127caeca26a08fd289b105f65247b5dfdea49b2 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Tue, 13 Jun 2023 13:44:40 +0900 Subject: [PATCH 18/30] =?UTF-8?q?[refactor]:=20Operation=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20singleton=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/model/Operation.java | 10 +++++++++- src/main/java/calculator/model/PostfixCalculator.java | 3 ++- .../util/converter/InfixToPostfixConverter.java | 10 +++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/Operation.java index 56de19e3d..7a0af5570 100644 --- a/src/main/java/calculator/model/Operation.java +++ b/src/main/java/calculator/model/Operation.java @@ -16,11 +16,19 @@ public class Operation { public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; public static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; - public Operation() { + private Operation(){ Arrays.stream(Operator.values()) .forEach(op -> operatorMap.put(op.operator, op)); } + public static Operation getInstance(){ + return LazyHolder.INSTANCE; + } + + private static class LazyHolder{ + private static final Operation INSTANCE = new Operation(); + } + public Integer calculate(Integer a, String operator, Integer b){ return Optional.ofNullable(operatorMap.get(operator)) .orElseThrow(() -> new IllegalArgumentException(OPERATOR_INPUT_ERROR)) diff --git a/src/main/java/calculator/model/PostfixCalculator.java b/src/main/java/calculator/model/PostfixCalculator.java index 7e75c248b..427ecc6c2 100644 --- a/src/main/java/calculator/model/PostfixCalculator.java +++ b/src/main/java/calculator/model/PostfixCalculator.java @@ -6,10 +6,11 @@ public class PostfixCalculator implements BasicCalculator{ public static final String OPERATOR_REGEX = "[-*/+]"; + @Override public Integer calculate(List expression) { Stack calcStack = new Stack<>(); - Operation operation = new Operation(); + Operation operation = Operation.getInstance(); int op1, op2; diff --git a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java index 4aeea418b..33d1f922e 100644 --- a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java +++ b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java @@ -9,18 +9,22 @@ @ToString public class InfixToPostfixConverter implements ExpressionConverter { - private static final ArrayList postfix = new ArrayList<>(); - private static final Stack opStack = new Stack<>(); + private final ArrayList postfix; + private final Stack opStack; private static final String EXPRESSION_DELIMITER = " "; public static final String OPERATOR_REGEX = "[-*/+]"; public static final String OPERAND_REGEX = "^\\d+$"; + public InfixToPostfixConverter(){ + postfix = new ArrayList<>(); + opStack = new Stack<>(); + } + @Override public ArrayList convert(String infixExpression) { StringBuilder num = new StringBuilder(); - Operation operation = new Operation(); // 중위 표기식을 리스트로 변환 String[] infix = infixExpression.split(EXPRESSION_DELIMITER); From b2b75662b3d0e14c645f1ddfee00f0dd2c414e6a Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Tue, 13 Jun 2023 20:08:28 +0900 Subject: [PATCH 19/30] =?UTF-8?q?[add]:=20AtomicInteger=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EB=A9=80=ED=8B=B0=20=EC=8A=A4=EB=A0=88?= =?UTF-8?q?=EB=93=9C=20=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=EC=9D=98=20Map?= =?UTF-8?q?=EC=9D=98=20id=EA=B0=92=20=EB=8F=99=EB=93=B1=EC=84=B1=20?= =?UTF-8?q?=EB=B3=B4=EC=9E=A5=20=EB=B0=8F=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculator/repository/CalculationRepository.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index 9db898182..b94209b0c 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -4,12 +4,20 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; public class CalculationRepository { - private final static Map results = new ConcurrentHashMap<>(); + private static Map results; + private static AtomicInteger idCounter; + + + public CalculationRepository() { + results = new ConcurrentHashMap<>(); + idCounter = new AtomicInteger(0); + } public void save(CalculationResult result) { - results.put(results.size() + 1, result); + results.put(idCounter.getAndIncrement(), result); } public Map findAll() { From a8193d1c2fb3950a9c0841f3b28efc78c68969f4 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Tue, 13 Jun 2023 22:43:00 +0900 Subject: [PATCH 20/30] =?UTF-8?q?[test]:=20CalculationRepository=EC=97=90?= =?UTF-8?q?=20clearStore()=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CalculationRepository.java | 4 ++ .../repository/CalculationRepositoryTest.java | 63 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/test/java/calculator/repository/CalculationRepositoryTest.java diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index b94209b0c..240bb644c 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -23,4 +23,8 @@ public void save(CalculationResult result) { public Map findAll() { return results; } + + void clearStore(){ + results.clear(); + } } diff --git a/src/test/java/calculator/repository/CalculationRepositoryTest.java b/src/test/java/calculator/repository/CalculationRepositoryTest.java new file mode 100644 index 000000000..0859b3e5e --- /dev/null +++ b/src/test/java/calculator/repository/CalculationRepositoryTest.java @@ -0,0 +1,63 @@ +package calculator.repository; + +import calculator.model.CalculationResult; +import org.assertj.core.api.Assertions; +import org.assertj.core.data.Index; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class CalculationRepositoryTest { + + private static CalculationRepository repository; + + @BeforeEach + void beforeEach() { + repository = new CalculationRepository(); + } + + @AfterEach + void afterEach(){ + repository.clearStore(); + } + + @Test + @DisplayName("CalculationRepository에 CaclulationResult가 잘 저장되었는지 확인하는 테스트") + void save() { + String expression = "3 + 5 * 2"; + Integer answer = 13; + + repository.save(new CalculationResult(expression, answer)); + + List results = repository.findAll().values().stream().map(CalculationResult::toString).toList(); + Assertions.assertThat(results) + .hasSize(1) + .contains("3 + 5 * 2 = 13", Index.atIndex(0)); + } + + @Test + @DisplayName("CalculationRepository에서 CalculationResult 값들을 잘 가져오는지 확인하는 테스트") + void findAll() { + String expression1 = "3 + 5 * 2"; + Integer answer1 = 13; + + String expression2 = "5 - 1 * 2"; + Integer answer2 = 3; + + repository.save(new CalculationResult(expression1, answer1)); + repository.save(new CalculationResult(expression2, answer2)); + + List results = repository.findAll().values().stream().map(CalculationResult::toString).toList(); + assertEquals(2, results.size()); + Assertions.assertThat(results) + .hasSize(2) + .contains("3 + 5 * 2 = 13") + .contains("5 - 1 * 2 = 3"); + + } +} \ No newline at end of file From 4cefceb31a98dc2955cc1b262a3e89b08358c1bd Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 01:16:30 +0900 Subject: [PATCH 21/30] =?UTF-8?q?[test]:=20=EC=82=AC=EC=B9=99=20=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20=EC=88=98=ED=96=89=20=ED=85=8C=EC=8A=A4=ED=8A=B8,?= =?UTF-8?q?=20=EC=88=98=EC=8B=9D=20=ED=91=9C=ED=98=84=20convert=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../infixtopostfix/InfixToArrayListTest.java | 24 --------- .../model/CalculationResultTest.java | 28 ++++++++++ .../java/calculator/model/OperationTest.java | 51 +++++++++++++++++++ .../InfixToPostfixConverterTest.java | 38 ++++++++++++++ 5 files changed, 118 insertions(+), 24 deletions(-) delete mode 100644 src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java create mode 100644 src/test/java/calculator/model/CalculationResultTest.java create mode 100644 src/test/java/calculator/model/OperationTest.java create mode 100644 src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java diff --git a/build.gradle b/build.gradle index a3efb3f7d..71e44b8b7 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' implementation 'org.assertj:assertj-core:3.24.2' diff --git a/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java b/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java deleted file mode 100644 index 6950c81a4..000000000 --- a/src/test/java/calculator/infixtopostfix/InfixToArrayListTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package calculator.infixtopostfix; - -import calculator.util.converter.InfixToPostfixConverter; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - -public class InfixToArrayListTest { - @Test - @DisplayName("처음 입력 받은 중위 표현 수식을 배열로 변환 하기") - void getInfixList() { - Assertions.assertThat(new InfixToPostfixConverter().convert("1 + 5 * 2")) - .isEqualTo(Arrays.asList("1", "5", "2", "*", "+")); - } - - @Test - @DisplayName("복잡한 중위 표현 수식을 후위 표현 수식으로 변환") - void getPrefix(){ - Assertions.assertThat(new InfixToPostfixConverter().convert("3 + 6 * 2 / 3")) - .isEqualTo(Arrays.asList("3", "6", "2", "*", "3", "/", "+")); - } -} diff --git a/src/test/java/calculator/model/CalculationResultTest.java b/src/test/java/calculator/model/CalculationResultTest.java new file mode 100644 index 000000000..d27fec251 --- /dev/null +++ b/src/test/java/calculator/model/CalculationResultTest.java @@ -0,0 +1,28 @@ +package calculator.model; + +import org.junit.jupiter.api.Assertions; +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 java.util.stream.Stream; + +class CalculationResultTest { + @ParameterizedTest + @DisplayName("CalculationResult의 객체 생성 후 expression과 result를 올바르게 출력하는지 검증하는 테스트") + @MethodSource("testData") + void expression_and_answer(String expression, Integer answer, String result){ + CalculationResult calculationResult = new CalculationResult(expression, answer); + Assertions.assertEquals(calculationResult.toString(), result); + } + + private static Stream testData(){ + return Stream.of( + Arguments.of("3 + 5", 8, "3 + 5 = 8"), + Arguments.of("3 + 8 * -1", -5, "3 + 8 * -1 = -5"), + Arguments.of("3 + 1 - 1", 3, "3 + 1 - 1 = 3") + ); + } + +} \ No newline at end of file diff --git a/src/test/java/calculator/model/OperationTest.java b/src/test/java/calculator/model/OperationTest.java new file mode 100644 index 000000000..f6ab2d18d --- /dev/null +++ b/src/test/java/calculator/model/OperationTest.java @@ -0,0 +1,51 @@ +package calculator.model; + +import org.junit.jupiter.api.Assertions; +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.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + + +class OperationTest { + + private static final Operation operation = Operation.getInstance(); + + + @ParameterizedTest + @DisplayName("Operator 사칙 연산을 검증 하는 테스트") + @CsvSource({"1,+,5,6", "2,-,1,1", "3,*,-5,-15", "12,/,4,3"}) + void calculate(Integer a, String operator, Integer b, Integer result) { + Integer calculated = operation.calculate(a, operator, b); + Assertions.assertEquals(result, calculated); + } + + @ParameterizedTest + @DisplayName("Operator 사칙 연산을 검증 하는 테스트 - 잘못된 결과 값에 대한 테스트") + @CsvSource({"1,+,5,5", "2,-,1,4", "3,*,-5,15", "12,/,4,1"}) + void validateWrongCalculation(Integer a, String operator, Integer b, Integer result) { + Integer calculated = operation.calculate(a, operator, b); + Assertions.assertNotEquals(result, calculated); + + } + + @ParameterizedTest + @DisplayName("String 형태의 연산자를 Operator 객체로 가져오는 것을 검증하는 테스트") + @MethodSource("testData") + void getOperator(Operation.Operator operator, String value) { + Assertions.assertEquals(operator, Operation.getOperator(value)); + + } + + private static Stream testData(){ + return Stream.of( + Arguments.of(Operation.Operator.PLUS, "+"), + Arguments.of(Operation.Operator.MINUS, "-"), + Arguments.of(Operation.Operator.DIVIDE, "/"), + Arguments.of(Operation.Operator.MULTIPLY, "*") + ); + } +} \ No newline at end of file diff --git a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java b/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java new file mode 100644 index 000000000..58bfcd0ec --- /dev/null +++ b/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java @@ -0,0 +1,38 @@ +package calculator.util.converter; + +import calculator.model.Operation; +import calculator.util.ExpressionConverter; +import org.junit.jupiter.api.Assertions; +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.Arrays; +import java.util.List; +import java.util.stream.Stream; + + +class InfixToPostfixConverterTest { + + private static final ExpressionConverter converter = new InfixToPostfixConverter(); + private static final Operation operation = Operation.getInstance(); + + + @ParameterizedTest + @MethodSource("testExpressionData") + void convert(String infix, List postfix) { + List convertedToPostfix = converter.convert(infix); + Assertions.assertEquals(convertedToPostfix, postfix); + } + + private static Stream testExpressionData(){ + return Stream.of( + Arguments.of("3 + 5 ", Arrays.asList("3", "5", "+")), + Arguments.of("3 - 5 * -2", Arrays.asList("3", "5", "-2", "*", "-")), + Arguments.of("3 + 5 - 2 * 1", Arrays.asList("3", "5", "2", "1", "*", "-", "5")), + Arguments.of("1 + 5 * 2", Arrays.asList("1", "5", "2", "*", "+")), + Arguments.of("3 + 6 * 2 / 3", Arrays.asList("3", "+", "6", "*", "2", "/", "3")) + ); + } +} \ No newline at end of file From cd2152ac8cb3bc9615ae5855486d0c8a34e999ff Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 01:39:16 +0900 Subject: [PATCH 22/30] =?UTF-8?q?[test]:=20=EC=98=AC=EB=B0=94=EB=A5=B8=20?= =?UTF-8?q?=EC=88=98=EC=8B=9D=20=ED=91=9C=ED=98=84=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8,=20=EC=88=98=EC=8B=9D=20=EC=97=B0?= =?UTF-8?q?=EC=82=B0=20=EC=97=90=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8,?= =?UTF-8?q?=20=EB=A9=94=EB=89=B4=20=EC=9E=85=EB=A0=A5=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 2 +- src/main/java/calculator/model/Menu.java | 2 +- .../converter/InfixToPostfixConverter.java | 3 +- .../util/validator/CalculatorValidator.java | 2 +- src/test/java/calculator/model/MenuTest.java | 25 ++++++++++ .../java/calculator/model/OperationTest.java | 12 +++++ .../validator/CalculatorValidatorTest.java | 46 +++++++++++++++++++ 7 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 src/test/java/calculator/model/MenuTest.java create mode 100644 src/test/java/calculator/util/validator/CalculatorValidatorTest.java diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 4b7b43a43..d37503b8b 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -42,7 +42,7 @@ public void run() { while (isProgramRunnable) { output.putMenu(); String inputMenu = input.getChoice(CHOICE_PROMPT); - if (!Menu.isValidMenu(inputMenu)) { + if (!Menu.isValidMenuInput(inputMenu)) { output.inputError(MENU_INPUT_ERROR); continue; } diff --git a/src/main/java/calculator/model/Menu.java b/src/main/java/calculator/model/Menu.java index ef346cee2..2fe0b1bcd 100644 --- a/src/main/java/calculator/model/Menu.java +++ b/src/main/java/calculator/model/Menu.java @@ -22,7 +22,7 @@ public Character getCommand(){ return command; } - public static boolean isValidMenu(String input){ + public static boolean isValidMenuInput(String input){ if (input.length() != MENU_INPUT_LENGTH) return false; Character firstChar = input.charAt(FIRST_INDEX); return Arrays.stream(Menu.values()).filter(m -> m.getCommand() diff --git a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java index 33d1f922e..85be128aa 100644 --- a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java +++ b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java @@ -7,10 +7,10 @@ import java.util.ArrayList; import java.util.Stack; -@ToString public class InfixToPostfixConverter implements ExpressionConverter { private final ArrayList postfix; private final Stack opStack; + private final Operation operation; private static final String EXPRESSION_DELIMITER = " "; public static final String OPERATOR_REGEX = "[-*/+]"; @@ -20,6 +20,7 @@ public class InfixToPostfixConverter implements ExpressionConverter { public InfixToPostfixConverter(){ postfix = new ArrayList<>(); opStack = new Stack<>(); + operation = Operation.getInstance(); } @Override diff --git a/src/main/java/calculator/util/validator/CalculatorValidator.java b/src/main/java/calculator/util/validator/CalculatorValidator.java index 9d58e2b7a..97bd1d997 100644 --- a/src/main/java/calculator/util/validator/CalculatorValidator.java +++ b/src/main/java/calculator/util/validator/CalculatorValidator.java @@ -6,7 +6,7 @@ public class CalculatorValidator { public static final String OPERATOR_REGEX = "[-*/+]"; - public static final String OPERAND_REGEX = "^\\d+$"; + public static final String OPERAND_REGEX = "^(0|[-]?[1-9]\\d*)$"; public static final int MINIMUM_OPS = 3; diff --git a/src/test/java/calculator/model/MenuTest.java b/src/test/java/calculator/model/MenuTest.java new file mode 100644 index 000000000..c60163832 --- /dev/null +++ b/src/test/java/calculator/model/MenuTest.java @@ -0,0 +1,25 @@ +package calculator.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; + +class MenuTest { + + @ParameterizedTest + @ValueSource(strings = {"0", "1", "2"}) + @DisplayName("올바른 메뉴 선택을 하였을 때 성공하는 테스트") + void isValidMenu(String menuInput) { + assertTrue(Menu.isValidMenuInput(menuInput)); + } + + @ParameterizedTest + @ValueSource(strings = {"3", "calculate", "no", " ", "\n"}) + @DisplayName("올바르지 메뉴 선택을 하였을 때 ") + void isNotValidMenu(String wrongMenuInput) { + assertFalse(Menu.isValidMenuInput(wrongMenuInput)); + } +} \ No newline at end of file diff --git a/src/test/java/calculator/model/OperationTest.java b/src/test/java/calculator/model/OperationTest.java index f6ab2d18d..d898c4339 100644 --- a/src/test/java/calculator/model/OperationTest.java +++ b/src/test/java/calculator/model/OperationTest.java @@ -23,6 +23,18 @@ void calculate(Integer a, String operator, Integer b, Integer result) { Assertions.assertEquals(result, calculated); } + @ParameterizedTest + @DisplayName("잘못된 표현 수식에 대한 연산을 검증 하는 테스트") + @CsvSource({"2,/,0", "3,/,0"}) + void calculateInvalidExpression(Integer a, String operator, Integer b) { + try { + Integer calculated = operation.calculate(a, operator, b); + } catch (RuntimeException e){ + Assertions.assertEquals("올바른 피연산자가 아닙니다.", e.getMessage()); + } + } + + @ParameterizedTest @DisplayName("Operator 사칙 연산을 검증 하는 테스트 - 잘못된 결과 값에 대한 테스트") @CsvSource({"1,+,5,5", "2,-,1,4", "3,*,-5,15", "12,/,4,1"}) diff --git a/src/test/java/calculator/util/validator/CalculatorValidatorTest.java b/src/test/java/calculator/util/validator/CalculatorValidatorTest.java new file mode 100644 index 000000000..7ed607504 --- /dev/null +++ b/src/test/java/calculator/util/validator/CalculatorValidatorTest.java @@ -0,0 +1,46 @@ +package calculator.util.validator; + +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 java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class CalculatorValidatorTest { + + @ParameterizedTest + @MethodSource("testValidExpression") + @DisplayName("올바른 수식 표현을 검증하는 테스트") + void validateExpression(String expression, boolean isValid) { + boolean isValidResult = CalculatorValidator.isValidExpression(expression); + assertEquals(isValid, isValidResult); + } + + private static Stream testValidExpression(){ + return Stream.of( + Arguments.of("3 + 6 / -2", true), + Arguments.of("3 * 1 - 2", true), + Arguments.of("-2 * -1 + 3", true) + ); + } + + @ParameterizedTest + @MethodSource("testInvalidExpression") + @DisplayName("올바르지 않은 수식 표현을 검증하는 테스트") + void validateInvalidExpression(String expression, boolean isValid) { + boolean isValidResult = CalculatorValidator.isValidExpression(expression); + assertEquals(isValid, isValidResult); + } + + private static Stream testInvalidExpression() { + return Stream.of( + Arguments.of("3 + ", false), + Arguments.of("3", false), + Arguments.of("+", false), + Arguments.of("-3", false) + ); + } +} \ No newline at end of file From 2e6da4bcbb03d0ea4027970778a80979eb94f640 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 02:02:03 +0900 Subject: [PATCH 23/30] =?UTF-8?q?[test]:=20Console=20=EB=A9=94=EB=89=B4=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B3=84=EC=82=B0=20=EA=B2=B0=EA=B3=BC=20=EB=B0=8F?= =?UTF-8?q?=20=EA=B3=84=EC=82=B0=20=EC=9D=B4=EB=A0=A5=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculator/CalculatorConsoleTest.java | 74 +++++++++++++++++++ .../InfixToPostfixConverterTest.java | 1 - 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/test/java/calculator/CalculatorConsoleTest.java diff --git a/src/test/java/calculator/CalculatorConsoleTest.java b/src/test/java/calculator/CalculatorConsoleTest.java new file mode 100644 index 000000000..9af0b6778 --- /dev/null +++ b/src/test/java/calculator/CalculatorConsoleTest.java @@ -0,0 +1,74 @@ +package calculator; + +import calculator.model.CalculationResult; +import calculator.repository.CalculationRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.*; + +class CalculatorConsoleTest { + + private static ByteArrayOutputStream outputMessage; + private static final CalculatorConsole console = new CalculatorConsole(); + private static CalculationRepository repository; + + private static final String NEW_LINE = "\r\n"; + + + @BeforeEach + void setUpOutputStreams(){ + repository = new CalculationRepository(); + outputMessage = new ByteArrayOutputStream(); // OutputStream 생성 + System.setOut(new PrintStream(outputMessage)); // 생성한 OutputStream 으로 설정 + } + + @AfterEach + void restoresStreams(){ + System.setOut(System.out); + } + + @Test + @DisplayName("콘솔에 메뉴 출력하기") + void putMenu() { + console.putMenu(); + assertEquals("0. 종료" + NEW_LINE + "1. 조회" + NEW_LINE + "2. 계산", outputMessage.toString().strip()); + + } + + @Test + @DisplayName("계산 이력 콘솔에 출력하기") + void showCalculationResult() { + String expression1 = "3 + 5"; + Integer answer1 = 8; + + String expression2 = "5 * 2"; + Integer answer2 = 10; + + repository.save(new CalculationResult(expression1, answer1)); + repository.save(new CalculationResult(expression2, answer2)); + + + console.showCalculationResult(repository.findAll()); + assertEquals("3 + 5 = 8" + NEW_LINE + "5 * 2 = 10", outputMessage.toString().strip()); + } + + @Test + @DisplayName("계산의 수식과 답 콘솔에 출력하기") + void showResult() { + String expression = "3 + 5"; + Integer answer = 8; + + CalculationResult result = new CalculationResult(expression, answer); + repository.save(result); + + + console.showResult(result.toString()); + assertEquals("3 + 5 = 8", outputMessage.toString().strip()); + } +} \ No newline at end of file diff --git a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java b/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java index 58bfcd0ec..814902626 100644 --- a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java +++ b/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java @@ -3,7 +3,6 @@ import calculator.model.Operation; import calculator.util.ExpressionConverter; import org.junit.jupiter.api.Assertions; -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; From a6c79e2a33bfdbad8e63b1a519e00b67e8611deb Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 11:43:11 +0900 Subject: [PATCH 24/30] =?UTF-8?q?[refactor]:=20=EC=83=81=EC=88=98=20consta?= =?UTF-8?q?nts=20=EB=AA=A8=EB=91=90=20private=20=EC=A7=80=EC=A0=95?= =?UTF-8?q?=EC=9E=90=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/calculator/Calculator.java | 14 +++++++------- src/main/java/calculator/model/Operation.java | 8 ++++---- .../java/calculator/model/PostfixCalculator.java | 2 +- .../util/validator/CalculatorValidator.java | 10 ++++++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index d37503b8b..d6d9c6ab1 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -19,13 +19,13 @@ public class Calculator implements Runnable { private final Output output; private final CalculationRepository calculationRepository; - public static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; - public static final String INVALID_INPUT_EXPRESSION = "올바른 수식 표현이 아닙니다."; - public static final char REQUEST_EXIT = '0'; - public static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; - public static final char REQUEST_CALCULATION = '2'; - public static final String CHOICE_PROMPT = "\n선택 : "; - public static final int FIRST_INDEX = 0; + private static final String MENU_INPUT_ERROR = "메뉴가 올바르게 입력되지 않았습니다."; + private static final String INVALID_INPUT_EXPRESSION = "올바른 수식 표현이 아닙니다."; + private static final char REQUEST_EXIT = '0'; + private static final char REQUEST_VIEW_CALCULATION_RESULT = '1'; + private static final char REQUEST_CALCULATION = '2'; + private static final String CHOICE_PROMPT = "\n선택 : "; + private static final int FIRST_INDEX = 0; public Calculator(BasicCalculator calculator, ExpressionConverter expressionConverter, Input input, Output output, CalculationRepository calculationRepository) { this.calculator = calculator; diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/Operation.java index 7a0af5570..f9745fd34 100644 --- a/src/main/java/calculator/model/Operation.java +++ b/src/main/java/calculator/model/Operation.java @@ -10,11 +10,11 @@ public class Operation { private static final Map operatorMap = new HashMap<>(); - public static final Integer LOW = 0; - public static final Integer HIGH = 1; + private static final Integer LOW = 0; + private static final Integer HIGH = 1; - public static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; - public static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; + private static final String OPERATOR_INPUT_ERROR = "연산자 입력에 오류가 발생했습니다."; + private static final String INVALID_OPERAND = "올바른 피연산자가 아닙니다."; private Operation(){ Arrays.stream(Operator.values()) diff --git a/src/main/java/calculator/model/PostfixCalculator.java b/src/main/java/calculator/model/PostfixCalculator.java index 427ecc6c2..0ea8d7ae5 100644 --- a/src/main/java/calculator/model/PostfixCalculator.java +++ b/src/main/java/calculator/model/PostfixCalculator.java @@ -5,7 +5,7 @@ public class PostfixCalculator implements BasicCalculator{ - public static final String OPERATOR_REGEX = "[-*/+]"; + private static final String OPERATOR_REGEX = "[-*/+]"; @Override public Integer calculate(List expression) { diff --git a/src/main/java/calculator/util/validator/CalculatorValidator.java b/src/main/java/calculator/util/validator/CalculatorValidator.java index 97bd1d997..929585751 100644 --- a/src/main/java/calculator/util/validator/CalculatorValidator.java +++ b/src/main/java/calculator/util/validator/CalculatorValidator.java @@ -5,13 +5,15 @@ public class CalculatorValidator { - public static final String OPERATOR_REGEX = "[-*/+]"; - public static final String OPERAND_REGEX = "^(0|[-]?[1-9]\\d*)$"; + private static final String OPERATOR_REGEX = "[-*/+]"; + private static final String OPERAND_REGEX = "^(0|[-]?[1-9]\\d*)$"; + + private static final int MINIMUM_OPS = 3; + private static final int FIRST_INDEX = 0; - public static final int MINIMUM_OPS = 3; public static boolean isValidExpression(String expression){ - AtomicInteger index = new AtomicInteger(0); + AtomicInteger index = new AtomicInteger(FIRST_INDEX); long countOfValidOps = Arrays.stream(expression.split(" ")) .filter(e -> isEvenNumber(index) ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) .count(); From 45eeb5056cdb7842ff95dbd9c900cffc79013159 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 12:17:31 +0900 Subject: [PATCH 25/30] =?UTF-8?q?[test]:=20Menu,=20CalculationResult=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=EB=9D=BC=EC=9D=B4=EB=94=A9=20toString=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B2=80=EC=A6=9D=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/CalculationResultTest.java | 12 +++++++++ src/test/java/calculator/model/MenuTest.java | 26 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/test/java/calculator/model/CalculationResultTest.java b/src/test/java/calculator/model/CalculationResultTest.java index d27fec251..5d31939ba 100644 --- a/src/test/java/calculator/model/CalculationResultTest.java +++ b/src/test/java/calculator/model/CalculationResultTest.java @@ -4,6 +4,7 @@ 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.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; @@ -25,4 +26,15 @@ private static Stream testData(){ ); } + @ParameterizedTest + @DisplayName("오버라이딩한 Calculation Result의 ToString 테스트") + @CsvSource({ + "3 + 5, 8, 3 + 5 = 8", + "-2 * 1, -2, -2 * 1 = -2" + }) + void testToString(String expression, Integer answer, String expectedResult) { + CalculationResult result = new CalculationResult(expression, answer); + + Assertions.assertEquals(expectedResult, result.toString()); + } } \ No newline at end of file diff --git a/src/test/java/calculator/model/MenuTest.java b/src/test/java/calculator/model/MenuTest.java index c60163832..cb3fe801b 100644 --- a/src/test/java/calculator/model/MenuTest.java +++ b/src/test/java/calculator/model/MenuTest.java @@ -1,10 +1,14 @@ package calculator.model; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import java.util.Arrays; +import java.util.List; + import static org.junit.jupiter.api.Assertions.*; class MenuTest { @@ -19,7 +23,27 @@ void isValidMenu(String menuInput) { @ParameterizedTest @ValueSource(strings = {"3", "calculate", "no", " ", "\n"}) @DisplayName("올바르지 메뉴 선택을 하였을 때 ") - void isNotValidMenu(String wrongMenuInput) { + void isNotValidMenuInput(String wrongMenuInput) { assertFalse(Menu.isValidMenuInput(wrongMenuInput)); } + + @Test + @DisplayName("Menu의 오버라이딩한 toString이 잘 작동하는지 테스트") + void testToString() { + List menuStringExpected = List.of( + "0. 종료", + "1. 조회", + "2. 계산" + ); + + + List menuList = Arrays.stream(Menu.values()) + .map(Menu::toString).toList(); + Assertions.assertThat(menuList) + .hasSize(3) + .contains("0. 종료") + .contains("1. 조회") + .contains("2. 계산") + .isEqualTo(menuStringExpected); + } } \ No newline at end of file From 35d54d48e98804741dbcc4ae43a9bb1cfd2a1145 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 12:47:28 +0900 Subject: [PATCH 26/30] =?UTF-8?q?[test]:=20Calculator=20Console,=20Postfix?= =?UTF-8?q?=20Calculator=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/InfixToPostfixConverter.java | 5 ++- .../java/calculator/model/OperationTest.java | 30 ++++++++++++++-- .../model/PostfixCalculatorTest.java | 34 +++++++++++++++++++ .../repository/CalculationRepositoryTest.java | 2 +- .../InfixToPostfixConverterTest.java | 19 +++++++---- .../validator/CalculatorValidatorTest.java | 25 ++++++++------ 6 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 src/test/java/calculator/model/PostfixCalculatorTest.java diff --git a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java index 85be128aa..33cbb7ff5 100644 --- a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java +++ b/src/main/java/calculator/util/converter/InfixToPostfixConverter.java @@ -2,7 +2,6 @@ import calculator.model.Operation; import calculator.util.ExpressionConverter; -import lombok.ToString; import java.util.ArrayList; import java.util.Stack; @@ -13,8 +12,8 @@ public class InfixToPostfixConverter implements ExpressionConverter { private final Operation operation; private static final String EXPRESSION_DELIMITER = " "; - public static final String OPERATOR_REGEX = "[-*/+]"; - public static final String OPERAND_REGEX = "^\\d+$"; + private static final String OPERATOR_REGEX = "[-*/+]"; + private static final String OPERAND_REGEX = "^\\d+$"; public InfixToPostfixConverter(){ diff --git a/src/test/java/calculator/model/OperationTest.java b/src/test/java/calculator/model/OperationTest.java index d898c4339..6520f1ad8 100644 --- a/src/test/java/calculator/model/OperationTest.java +++ b/src/test/java/calculator/model/OperationTest.java @@ -1,7 +1,9 @@ package calculator.model; +import org.assertj.core.api.Assert; import org.junit.jupiter.api.Assertions; 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.CsvSource; @@ -17,7 +19,12 @@ class OperationTest { @ParameterizedTest @DisplayName("Operator 사칙 연산을 검증 하는 테스트") - @CsvSource({"1,+,5,6", "2,-,1,1", "3,*,-5,-15", "12,/,4,3"}) + @CsvSource({ + "1, +, 5, 6", + "2, -,1, 1", + "3, *, -5, -15", + "12, /, 4, 3" + }) void calculate(Integer a, String operator, Integer b, Integer result) { Integer calculated = operation.calculate(a, operator, b); Assertions.assertEquals(result, calculated); @@ -25,7 +32,10 @@ void calculate(Integer a, String operator, Integer b, Integer result) { @ParameterizedTest @DisplayName("잘못된 표현 수식에 대한 연산을 검증 하는 테스트") - @CsvSource({"2,/,0", "3,/,0"}) + @CsvSource({ + "2, /, 0", + "3, /, 0" + }) void calculateInvalidExpression(Integer a, String operator, Integer b) { try { Integer calculated = operation.calculate(a, operator, b); @@ -37,7 +47,12 @@ void calculateInvalidExpression(Integer a, String operator, Integer b) { @ParameterizedTest @DisplayName("Operator 사칙 연산을 검증 하는 테스트 - 잘못된 결과 값에 대한 테스트") - @CsvSource({"1,+,5,5", "2,-,1,4", "3,*,-5,15", "12,/,4,1"}) + @CsvSource({ + "1, +, 5, 5", + "2, -, 1, 4", + "3, *, -5, 15", + "12, /, 4, 1" + }) void validateWrongCalculation(Integer a, String operator, Integer b, Integer result) { Integer calculated = operation.calculate(a, operator, b); Assertions.assertNotEquals(result, calculated); @@ -60,4 +75,13 @@ private static Stream testData(){ Arguments.of(Operation.Operator.MULTIPLY, "*") ); } + + @Test + @DisplayName("Operation이 싱글톤으로 객체가 생성되는지 확인하는 테스트") + void getInstance() { + Operation operation = Operation.getInstance(); + Operation operation2 = Operation.getInstance(); + + Assertions.assertEquals(operation, operation2); + } } \ No newline at end of file diff --git a/src/test/java/calculator/model/PostfixCalculatorTest.java b/src/test/java/calculator/model/PostfixCalculatorTest.java new file mode 100644 index 000000000..38aec4294 --- /dev/null +++ b/src/test/java/calculator/model/PostfixCalculatorTest.java @@ -0,0 +1,34 @@ +package calculator.model; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class PostfixCalculatorTest { + + @ParameterizedTest + @MethodSource("testData") + void calculate(List postfix, Integer expectedResult) { + PostfixCalculator postfixCalculator = new PostfixCalculator(); + + Integer result = postfixCalculator.calculate(postfix); + + assertEquals(expectedResult, result); + } + + private static Stream testData() { + return Stream.of( + Arguments.of(Arrays.asList("3", "5", "+"), 8), + Arguments.of(Arrays.asList("3", "5", "*", "-2", "-"), 13), // failed + Arguments.of(Arrays.asList("3", "5", "+", "2", "1", "*", "-"), 6), + Arguments.of(Arrays.asList("1", "5", "2", "*", "+"), 11), + Arguments.of(Arrays.asList("3", "6", "2", "*", "3", "/", "+"), 7) + ); + } +} \ No newline at end of file diff --git a/src/test/java/calculator/repository/CalculationRepositoryTest.java b/src/test/java/calculator/repository/CalculationRepositoryTest.java index 0859b3e5e..4385cff4a 100644 --- a/src/test/java/calculator/repository/CalculationRepositoryTest.java +++ b/src/test/java/calculator/repository/CalculationRepositoryTest.java @@ -41,7 +41,7 @@ void save() { } @Test - @DisplayName("CalculationRepository에서 CalculationResult 값들을 잘 가져오는지 확인하는 테스트") + @DisplayName("Calculation Repository에서 CalculationResult 값들을 잘 가져오는지 확인하는 테스트") void findAll() { String expression1 = "3 + 5 * 2"; Integer answer1 = 13; diff --git a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java b/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java index 814902626..4234b87f0 100644 --- a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java +++ b/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java @@ -3,6 +3,7 @@ import calculator.model.Operation; import calculator.util.ExpressionConverter; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -14,24 +15,28 @@ class InfixToPostfixConverterTest { - private static final ExpressionConverter converter = new InfixToPostfixConverter(); + private static ExpressionConverter converter; private static final Operation operation = Operation.getInstance(); + @BeforeEach + void beforeEach(){ + converter = new InfixToPostfixConverter(); + } @ParameterizedTest @MethodSource("testExpressionData") - void convert(String infix, List postfix) { + void convert(String infix, List expectedPostfix) { List convertedToPostfix = converter.convert(infix); - Assertions.assertEquals(convertedToPostfix, postfix); + Assertions.assertEquals(expectedPostfix, convertedToPostfix); } private static Stream testExpressionData(){ return Stream.of( - Arguments.of("3 + 5 ", Arrays.asList("3", "5", "+")), - Arguments.of("3 - 5 * -2", Arrays.asList("3", "5", "-2", "*", "-")), - Arguments.of("3 + 5 - 2 * 1", Arrays.asList("3", "5", "2", "1", "*", "-", "5")), + Arguments.of("3 + 5", Arrays.asList("3", "5", "+")), + Arguments.of("3 - 5 * -2", Arrays.asList("3", "5", "*", "-2", "-")), // Failed + Arguments.of("3 + 5 - 2 * 1", Arrays.asList("3", "5", "+", "2", "1", "*", "-")), Arguments.of("1 + 5 * 2", Arrays.asList("1", "5", "2", "*", "+")), - Arguments.of("3 + 6 * 2 / 3", Arrays.asList("3", "+", "6", "*", "2", "/", "3")) + Arguments.of("3 + 6 * 2 / 3", Arrays.asList("3", "6", "2", "*", "3", "/", "+")) ); } } \ No newline at end of file diff --git a/src/test/java/calculator/util/validator/CalculatorValidatorTest.java b/src/test/java/calculator/util/validator/CalculatorValidatorTest.java index 7ed607504..aa2ec6dd5 100644 --- a/src/test/java/calculator/util/validator/CalculatorValidatorTest.java +++ b/src/test/java/calculator/util/validator/CalculatorValidatorTest.java @@ -14,33 +14,36 @@ class CalculatorValidatorTest { @ParameterizedTest @MethodSource("testValidExpression") @DisplayName("올바른 수식 표현을 검증하는 테스트") - void validateExpression(String expression, boolean isValid) { + void validateExpression(String expression) { boolean isValidResult = CalculatorValidator.isValidExpression(expression); - assertEquals(isValid, isValidResult); + assertTrue(isValidResult); } private static Stream testValidExpression(){ return Stream.of( - Arguments.of("3 + 6 / -2", true), - Arguments.of("3 * 1 - 2", true), - Arguments.of("-2 * -1 + 3", true) + Arguments.of("3 + 6 / -2"), + Arguments.of("3 * 1 - 2"), + Arguments.of("-2 * -1 + 3") ); } @ParameterizedTest @MethodSource("testInvalidExpression") @DisplayName("올바르지 않은 수식 표현을 검증하는 테스트") - void validateInvalidExpression(String expression, boolean isValid) { + void validateInvalidExpression(String expression) { boolean isValidResult = CalculatorValidator.isValidExpression(expression); - assertEquals(isValid, isValidResult); + assertFalse(isValidResult); } private static Stream testInvalidExpression() { return Stream.of( - Arguments.of("3 + ", false), - Arguments.of("3", false), - Arguments.of("+", false), - Arguments.of("-3", false) + Arguments.of("3 + "), + Arguments.of("3"), + Arguments.of("+"), + Arguments.of("-3"), + Arguments.of("-3 + + "), + Arguments.of("-3 + 5 5 6"), + Arguments.of("+ 1 - 5") ); } } \ No newline at end of file From c459ac6d63c7cded85d40ea1378f75100cdaff8c Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 16:37:08 +0900 Subject: [PATCH 27/30] =?UTF-8?q?[refactor]:=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/CalculatorApp.java | 4 ++-- src/main/java/calculator/Calculator.java | 8 ++++---- src/main/java/calculator/CalculatorConsole.java | 4 ++-- src/main/java/calculator/io/Output.java | 2 +- .../calculator/{util => model}/ExpressionConverter.java | 2 +- .../model/{ => calculator}/CalculationResult.java | 2 +- .../java/calculator/model/{ => calculator}/Operation.java | 2 +- .../model/{ => calculator}/PostfixCalculator.java | 6 ++++-- .../converter/InfixToPostfixConverter.java | 6 +++--- src/main/java/calculator/model/{ => menu}/Menu.java | 2 +- .../{util => model}/validator/CalculatorValidator.java | 2 +- .../java/calculator/repository/CalculationRepository.java | 2 +- src/test/java/calculator/CalculatorConsoleTest.java | 2 +- .../model/{ => calculator}/CalculationResultTest.java | 3 ++- .../calculator}/InfixToPostfixConverterTest.java | 7 ++++--- .../calculator/model/{ => calculator}/OperationTest.java | 4 ++-- .../model/{ => calculator}/PostfixCalculatorTest.java | 3 ++- src/test/java/calculator/model/{ => menu}/MenuTest.java | 3 ++- .../validator/CalculatorValidatorTest.java | 3 ++- .../validator}/ExpressionValidationTest.java | 5 ++--- .../calculator/repository/CalculationRepositoryTest.java | 2 +- 21 files changed, 40 insertions(+), 34 deletions(-) rename src/main/java/calculator/{util => model}/ExpressionConverter.java (81%) rename src/main/java/calculator/model/{ => calculator}/CalculationResult.java (92%) rename src/main/java/calculator/model/{ => calculator}/Operation.java (98%) rename src/main/java/calculator/model/{ => calculator}/PostfixCalculator.java (84%) rename src/main/java/calculator/{util => model}/converter/InfixToPostfixConverter.java (93%) rename src/main/java/calculator/model/{ => menu}/Menu.java (96%) rename src/main/java/calculator/{util => model}/validator/CalculatorValidator.java (96%) rename src/test/java/calculator/model/{ => calculator}/CalculationResultTest.java (94%) rename src/test/java/calculator/{util/converter => model/calculator}/InfixToPostfixConverterTest.java (88%) rename src/test/java/calculator/model/{ => calculator}/OperationTest.java (97%) rename src/test/java/calculator/model/{ => calculator}/PostfixCalculatorTest.java (92%) rename src/test/java/calculator/model/{ => menu}/MenuTest.java (95%) rename src/test/java/calculator/{util => model}/validator/CalculatorValidatorTest.java (94%) rename src/test/java/calculator/{calculator => model/validator}/ExpressionValidationTest.java (90%) diff --git a/src/main/java/CalculatorApp.java b/src/main/java/CalculatorApp.java index bdac95824..7b20dbe6e 100644 --- a/src/main/java/CalculatorApp.java +++ b/src/main/java/CalculatorApp.java @@ -1,7 +1,7 @@ import calculator.Calculator; import calculator.CalculatorConsole; -import calculator.model.PostfixCalculator; -import calculator.util.converter.InfixToPostfixConverter; +import calculator.model.calculator.PostfixCalculator; +import calculator.model.converter.InfixToPostfixConverter; import calculator.repository.CalculationRepository; public class CalculatorApp { diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index d6d9c6ab1..b9dd25a42 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,13 +1,13 @@ package calculator; -import calculator.model.Menu; -import calculator.model.CalculationResult; -import calculator.util.ExpressionConverter; +import calculator.model.menu.Menu; +import calculator.model.calculator.CalculationResult; +import calculator.model.ExpressionConverter; import calculator.io.Input; import calculator.io.Output; import calculator.model.BasicCalculator; import calculator.repository.CalculationRepository; -import calculator.util.validator.CalculatorValidator; +import calculator.model.validator.CalculatorValidator; import java.util.List; diff --git a/src/main/java/calculator/CalculatorConsole.java b/src/main/java/calculator/CalculatorConsole.java index db7bc30d6..3e3f28009 100644 --- a/src/main/java/calculator/CalculatorConsole.java +++ b/src/main/java/calculator/CalculatorConsole.java @@ -1,9 +1,9 @@ package calculator; -import calculator.model.Menu; +import calculator.model.menu.Menu; import calculator.io.Input; import calculator.io.Output; -import calculator.model.CalculationResult; +import calculator.model.calculator.CalculationResult; import java.util.Arrays; import java.util.Map; diff --git a/src/main/java/calculator/io/Output.java b/src/main/java/calculator/io/Output.java index be66b4339..223ac8a70 100644 --- a/src/main/java/calculator/io/Output.java +++ b/src/main/java/calculator/io/Output.java @@ -1,6 +1,6 @@ package calculator.io; -import calculator.model.CalculationResult; +import calculator.model.calculator.CalculationResult; import java.util.Map; diff --git a/src/main/java/calculator/util/ExpressionConverter.java b/src/main/java/calculator/model/ExpressionConverter.java similarity index 81% rename from src/main/java/calculator/util/ExpressionConverter.java rename to src/main/java/calculator/model/ExpressionConverter.java index fc031c8a0..1cf24d96d 100644 --- a/src/main/java/calculator/util/ExpressionConverter.java +++ b/src/main/java/calculator/model/ExpressionConverter.java @@ -1,4 +1,4 @@ -package calculator.util; +package calculator.model; import java.util.List; diff --git a/src/main/java/calculator/model/CalculationResult.java b/src/main/java/calculator/model/calculator/CalculationResult.java similarity index 92% rename from src/main/java/calculator/model/CalculationResult.java rename to src/main/java/calculator/model/calculator/CalculationResult.java index 0a8bf7464..3611db1a2 100644 --- a/src/main/java/calculator/model/CalculationResult.java +++ b/src/main/java/calculator/model/calculator/CalculationResult.java @@ -1,4 +1,4 @@ -package calculator.model; +package calculator.model.calculator; public class CalculationResult { private final String expression; diff --git a/src/main/java/calculator/model/Operation.java b/src/main/java/calculator/model/calculator/Operation.java similarity index 98% rename from src/main/java/calculator/model/Operation.java rename to src/main/java/calculator/model/calculator/Operation.java index f9745fd34..154c54715 100644 --- a/src/main/java/calculator/model/Operation.java +++ b/src/main/java/calculator/model/calculator/Operation.java @@ -1,4 +1,4 @@ -package calculator.model; +package calculator.model.calculator; import java.util.Arrays; import java.util.HashMap; diff --git a/src/main/java/calculator/model/PostfixCalculator.java b/src/main/java/calculator/model/calculator/PostfixCalculator.java similarity index 84% rename from src/main/java/calculator/model/PostfixCalculator.java rename to src/main/java/calculator/model/calculator/PostfixCalculator.java index 0ea8d7ae5..50b75b2af 100644 --- a/src/main/java/calculator/model/PostfixCalculator.java +++ b/src/main/java/calculator/model/calculator/PostfixCalculator.java @@ -1,9 +1,11 @@ -package calculator.model; +package calculator.model.calculator; + +import calculator.model.BasicCalculator; import java.util.List; import java.util.Stack; -public class PostfixCalculator implements BasicCalculator{ +public class PostfixCalculator implements BasicCalculator { private static final String OPERATOR_REGEX = "[-*/+]"; diff --git a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java b/src/main/java/calculator/model/converter/InfixToPostfixConverter.java similarity index 93% rename from src/main/java/calculator/util/converter/InfixToPostfixConverter.java rename to src/main/java/calculator/model/converter/InfixToPostfixConverter.java index 33cbb7ff5..506eb99ae 100644 --- a/src/main/java/calculator/util/converter/InfixToPostfixConverter.java +++ b/src/main/java/calculator/model/converter/InfixToPostfixConverter.java @@ -1,7 +1,7 @@ -package calculator.util.converter; +package calculator.model.converter; -import calculator.model.Operation; -import calculator.util.ExpressionConverter; +import calculator.model.ExpressionConverter; +import calculator.model.calculator.Operation; import java.util.ArrayList; import java.util.Stack; diff --git a/src/main/java/calculator/model/Menu.java b/src/main/java/calculator/model/menu/Menu.java similarity index 96% rename from src/main/java/calculator/model/Menu.java rename to src/main/java/calculator/model/menu/Menu.java index 2fe0b1bcd..8ab2bb05c 100644 --- a/src/main/java/calculator/model/Menu.java +++ b/src/main/java/calculator/model/menu/Menu.java @@ -1,4 +1,4 @@ -package calculator.model; +package calculator.model.menu; import java.util.Arrays; diff --git a/src/main/java/calculator/util/validator/CalculatorValidator.java b/src/main/java/calculator/model/validator/CalculatorValidator.java similarity index 96% rename from src/main/java/calculator/util/validator/CalculatorValidator.java rename to src/main/java/calculator/model/validator/CalculatorValidator.java index 929585751..3920ddc94 100644 --- a/src/main/java/calculator/util/validator/CalculatorValidator.java +++ b/src/main/java/calculator/model/validator/CalculatorValidator.java @@ -1,4 +1,4 @@ -package calculator.util.validator; +package calculator.model.validator; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; diff --git a/src/main/java/calculator/repository/CalculationRepository.java b/src/main/java/calculator/repository/CalculationRepository.java index 240bb644c..8fc77a669 100644 --- a/src/main/java/calculator/repository/CalculationRepository.java +++ b/src/main/java/calculator/repository/CalculationRepository.java @@ -1,6 +1,6 @@ package calculator.repository; -import calculator.model.CalculationResult; +import calculator.model.calculator.CalculationResult; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/test/java/calculator/CalculatorConsoleTest.java b/src/test/java/calculator/CalculatorConsoleTest.java index 9af0b6778..be6c01920 100644 --- a/src/test/java/calculator/CalculatorConsoleTest.java +++ b/src/test/java/calculator/CalculatorConsoleTest.java @@ -1,6 +1,6 @@ package calculator; -import calculator.model.CalculationResult; +import calculator.model.calculator.CalculationResult; import calculator.repository.CalculationRepository; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/calculator/model/CalculationResultTest.java b/src/test/java/calculator/model/calculator/CalculationResultTest.java similarity index 94% rename from src/test/java/calculator/model/CalculationResultTest.java rename to src/test/java/calculator/model/calculator/CalculationResultTest.java index 5d31939ba..f3ae5b0f3 100644 --- a/src/test/java/calculator/model/CalculationResultTest.java +++ b/src/test/java/calculator/model/calculator/CalculationResultTest.java @@ -1,5 +1,6 @@ -package calculator.model; +package calculator.model.calculator; +import calculator.model.calculator.CalculationResult; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java b/src/test/java/calculator/model/calculator/InfixToPostfixConverterTest.java similarity index 88% rename from src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java rename to src/test/java/calculator/model/calculator/InfixToPostfixConverterTest.java index 4234b87f0..c702a4e46 100644 --- a/src/test/java/calculator/util/converter/InfixToPostfixConverterTest.java +++ b/src/test/java/calculator/model/calculator/InfixToPostfixConverterTest.java @@ -1,7 +1,8 @@ -package calculator.util.converter; +package calculator.model.calculator; -import calculator.model.Operation; -import calculator.util.ExpressionConverter; +import calculator.model.calculator.Operation; +import calculator.model.converter.InfixToPostfixConverter; +import calculator.model.ExpressionConverter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/calculator/model/OperationTest.java b/src/test/java/calculator/model/calculator/OperationTest.java similarity index 97% rename from src/test/java/calculator/model/OperationTest.java rename to src/test/java/calculator/model/calculator/OperationTest.java index 6520f1ad8..91bac110c 100644 --- a/src/test/java/calculator/model/OperationTest.java +++ b/src/test/java/calculator/model/calculator/OperationTest.java @@ -1,6 +1,6 @@ -package calculator.model; +package calculator.model.calculator; -import org.assertj.core.api.Assert; +import calculator.model.calculator.Operation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/calculator/model/PostfixCalculatorTest.java b/src/test/java/calculator/model/calculator/PostfixCalculatorTest.java similarity index 92% rename from src/test/java/calculator/model/PostfixCalculatorTest.java rename to src/test/java/calculator/model/calculator/PostfixCalculatorTest.java index 38aec4294..a609da585 100644 --- a/src/test/java/calculator/model/PostfixCalculatorTest.java +++ b/src/test/java/calculator/model/calculator/PostfixCalculatorTest.java @@ -1,5 +1,6 @@ -package calculator.model; +package calculator.model.calculator; +import calculator.model.calculator.PostfixCalculator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; diff --git a/src/test/java/calculator/model/MenuTest.java b/src/test/java/calculator/model/menu/MenuTest.java similarity index 95% rename from src/test/java/calculator/model/MenuTest.java rename to src/test/java/calculator/model/menu/MenuTest.java index cb3fe801b..8bbb8d9d7 100644 --- a/src/test/java/calculator/model/MenuTest.java +++ b/src/test/java/calculator/model/menu/MenuTest.java @@ -1,5 +1,6 @@ -package calculator.model; +package calculator.model.menu; +import calculator.model.menu.Menu; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/calculator/util/validator/CalculatorValidatorTest.java b/src/test/java/calculator/model/validator/CalculatorValidatorTest.java similarity index 94% rename from src/test/java/calculator/util/validator/CalculatorValidatorTest.java rename to src/test/java/calculator/model/validator/CalculatorValidatorTest.java index aa2ec6dd5..39e9154fa 100644 --- a/src/test/java/calculator/util/validator/CalculatorValidatorTest.java +++ b/src/test/java/calculator/model/validator/CalculatorValidatorTest.java @@ -1,5 +1,6 @@ -package calculator.util.validator; +package calculator.model.validator; +import calculator.model.validator.CalculatorValidator; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/src/test/java/calculator/calculator/ExpressionValidationTest.java b/src/test/java/calculator/model/validator/ExpressionValidationTest.java similarity index 90% rename from src/test/java/calculator/calculator/ExpressionValidationTest.java rename to src/test/java/calculator/model/validator/ExpressionValidationTest.java index 5a7c4a6fb..139c7abe9 100644 --- a/src/test/java/calculator/calculator/ExpressionValidationTest.java +++ b/src/test/java/calculator/model/validator/ExpressionValidationTest.java @@ -1,7 +1,6 @@ -package calculator.calculator; +package calculator.model.validator; -import calculator.Calculator; -import calculator.util.validator.CalculatorValidator; +import calculator.model.validator.CalculatorValidator; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/calculator/repository/CalculationRepositoryTest.java b/src/test/java/calculator/repository/CalculationRepositoryTest.java index 4385cff4a..92dbbc47b 100644 --- a/src/test/java/calculator/repository/CalculationRepositoryTest.java +++ b/src/test/java/calculator/repository/CalculationRepositoryTest.java @@ -1,6 +1,6 @@ package calculator.repository; -import calculator.model.CalculationResult; +import calculator.model.calculator.CalculationResult; import org.assertj.core.api.Assertions; import org.assertj.core.data.Index; import org.junit.jupiter.api.AfterEach; From b706154782feaf5dbb79da9e3e9f0cb021ad9de5 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 17:20:42 +0900 Subject: [PATCH 28/30] =?UTF-8?q?[add]:=20=EB=B0=98=ED=99=98=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20List=EB=A1=9C=20=EC=88=98=EC=A0=95,=20delimiter=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EA=B3=84=EC=82=B0=EA=B8=B0=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 56 +++++++++++++++++++ .../converter/InfixToPostfixConverter.java | 3 +- .../model/validator/CalculatorValidator.java | 4 +- src/test/java/calculator/CalculatorTest.java | 53 ++++++++++++++++++ 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 src/test/java/calculator/CalculatorTest.java diff --git a/README.md b/README.md index c1c982e55..de661649c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,62 @@ # java_calculator 자바 계산기 구현 미션 Repository입니다. +
+ PR 반영한 점 + +1. CalculatorApp +- 전략 패턴을 사용하여 CalculatorApp에서 구체적인 객체를 생성하여 `Calculator`에 주입합니다. +```java + CalculatorConsole console = new CalculatorConsole(); + + new Calculator( + new PostfixCalculator(), + new InfixToPostfixConverter(), + console, + console, + new CalculationRepository() + ).run(); +``` + +2. 다양한 형태의 Converter 구현 +- 중위 표현식에서 후위 표현식을 바꿔주는 컨버터 뿐 아니라, 후위 표현식을 중위 표현식으로 변환하는 등의 다양한 수식 간의 변환을 가능하게 하는 `ExpressionConverter` 인터페이스로 추상화를 하고, 구상체인 `InfixToPosfixConverter` 클래스로 구현하여 동적으로 적절한 컨버터가 선택되도록 하였습니다. +- `convert()` 메서드에서 String 형태의 expression을 변환하여 피연산자와 연산자의 리스트 형태의 ArrayList 타입으로 수식을 변환합니다. + +3. Calculator 추상화 +- 컨버터도 추상화할 수 있으면, 구체적인 표현식을 적절하게 계산하는 계산기도 추상화할 수 있다고 생각하였습니다. +- 따라서 실제 표현식을 계산하는 `calculate()` 메서드를 담는 `BasicCalculator` 인터페이스로 추상화하고, 구체적으로 후위 표현 수식을 계산하는 `PostfixCalculator` 클래스로 구체화하였습니다. +- 마찬가지로 runnable한 Application에서 동작할 때, 계산 대상인 표현 수식에 따라 동적으로 적절한 Calculator가 선택됩니다. + +4. Validation +- 기존에 Calculator 내에서 메뉴 선택, 표현 수식을 비롯한 입력에 대한 값 검증을 하던 `validateChoiceInput()`, `validateExpression()`의 메서드는 각각 Menu 클래스 내부, `CalculatorValidator` 클래스 내부로 이동하였습니다. +- 메서드 명 또한 valid 여부에 따라 boolean 타입을 반환한다는 점에서, 이를 잘 드러낼 수 있느 `isValidInputMenu()`, `isValidExpression()` 으로 변경하였습니다. + +5. CalculationRepository - ConcurrentHashMap과 Atomic Variable을 통한 ID값 관리로 멀티 쓰레드 환경에서의 동시성 문제 고려 +- 계산 이력을 저장하기 위해서 Map을 사용한 이유는, 추후 데이터베이스로의 확장 가능성을 생각했을 때, 각 레코드 별로 고유 PK ID값을 통해 CRUD를 편리하게 하는 것을 고려하여 각 계산 결과 객체 값에 대하여 Key 값을 Integer타입으로, 계산 결과 (CalculationResult)를 Value로 저장하도록 구현하였습니다. +- 기존에는 맵의 Key 값으로, 맵의 크기를 기반으로 id 값을 결정하였고 이는 데이터 삭제 로직으로 확장되는 경우를 고려하지 못했습니다. +- 쓰기 작업(put)에서 Lock을 통해 멀티 쓰레드 환경의 동시성 문제를 해결할 수 있는 ConccurrentHashMap을 이용하였습니다. +- 마찬가지로 유니크 아이디의 경우, 맵의 Key의 타입으로 AtomicInteger을 사용하였습니다. + +6. ParameterizedTest 기반 유닛 테스트 +- 단위 테스트 코드들을 작성하였고, ParameterizedTest를 통해 다양한 입력에 대한 테스트를 수행하였습니다. +- [ ] InfixToPostfixConverter 테스트에서 Failed 1개 발생 +- [ ] 계산기 통합 테스트 Failed + +7. 상수 관리 +- 여러 클래스에서 사용되는 상수를 따로 하나의 클래스에서 관리하는 것은 객체 지향적이지 않다는 피드백을 바탕으로 각 클래스에서 사용하는 상수들은 클래스 내부로 옮겼습니다. + +8. toString의 쓰임 +- 디버그나 로깅 목적으로 toString이 사용된 다는 것을 새로 알게 되었고, +- `CalculationRepository`에 객체 저장 시에 `CalculationResult` 자체를 매개 변수로 넣어주도록 변경하였습니다. + +9. Operation +- 사칙 연산을 수행하는 `Operation` 객체를 싱글톤으로 생성하도록 LazyHolder의 방식으로 구현하였습니다. +- [ ] Operation의 Converter, Calculator에서 모두 Operation을 필요로 한다는 점 -> static하게 사용할 수 있는 방법이 없을까? 고민하고 있습니다.. ! +- [ ] 사칙 연산을 수행해내기 위해서는, String(+,-,*,/)과 Operator Enum 객체들을 Map으로 저장하는 `OperatorMap` 을 초기화하는 `Operation` 클래스의 객체가 필수적으로 생성되어야 하는데, Converter과 Calculator 모두 Operation에 의존성을 띄고 있어 설계 리팩토링을 진행해야 할 것 같습니다.. + +
+ + ### 이곳은 공개 Repo입니다. 1. 여러분의 포트폴리오로 사용하셔도 됩니다. 2. 때문에 이 repo를 fork한 뒤 diff --git a/src/main/java/calculator/model/converter/InfixToPostfixConverter.java b/src/main/java/calculator/model/converter/InfixToPostfixConverter.java index 506eb99ae..ecbb12062 100644 --- a/src/main/java/calculator/model/converter/InfixToPostfixConverter.java +++ b/src/main/java/calculator/model/converter/InfixToPostfixConverter.java @@ -4,6 +4,7 @@ import calculator.model.calculator.Operation; import java.util.ArrayList; +import java.util.List; import java.util.Stack; public class InfixToPostfixConverter implements ExpressionConverter { @@ -23,7 +24,7 @@ public InfixToPostfixConverter(){ } @Override - public ArrayList convert(String infixExpression) { + public List convert(String infixExpression) { StringBuilder num = new StringBuilder(); // 중위 표기식을 리스트로 변환 diff --git a/src/main/java/calculator/model/validator/CalculatorValidator.java b/src/main/java/calculator/model/validator/CalculatorValidator.java index 3920ddc94..5fbfe3d6f 100644 --- a/src/main/java/calculator/model/validator/CalculatorValidator.java +++ b/src/main/java/calculator/model/validator/CalculatorValidator.java @@ -11,10 +11,12 @@ public class CalculatorValidator { private static final int MINIMUM_OPS = 3; private static final int FIRST_INDEX = 0; + private static final String EXPRESSION_DELIMITER = " "; + public static boolean isValidExpression(String expression){ AtomicInteger index = new AtomicInteger(FIRST_INDEX); - long countOfValidOps = Arrays.stream(expression.split(" ")) + long countOfValidOps = Arrays.stream(expression.split(EXPRESSION_DELIMITER)) .filter(e -> isEvenNumber(index) ? e.matches(OPERAND_REGEX) : e.matches(OPERATOR_REGEX)) .count(); return countOfValidOps >= MINIMUM_OPS && Arrays.stream(expression.split(" ")).count() == countOfValidOps; diff --git a/src/test/java/calculator/CalculatorTest.java b/src/test/java/calculator/CalculatorTest.java new file mode 100644 index 000000000..9312d78a7 --- /dev/null +++ b/src/test/java/calculator/CalculatorTest.java @@ -0,0 +1,53 @@ +package calculator; + +import calculator.model.calculator.PostfixCalculator; +import calculator.repository.CalculationRepository; +import calculator.model.converter.InfixToPostfixConverter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +class CalculatorTest { + + private final static ByteArrayOutputStream outputMessage = new ByteArrayOutputStream(); + private final PrintStream systemOut = System.out; + private final InputStream systemIn = System.in; + + + @BeforeEach + public void setUpStreams(){ + System.setOut(new PrintStream(outputMessage)); + } + + @AfterEach + void restoresStreams(){ + System.setOut(System.out); + System.setIn(systemIn); + } + + @Test + @DisplayName("계산기 통합 테스트") + void 계산기통합테스트_계산하기() { // fail + String[] inputs = { "2", "3 + 5 * -1", "2", "-1 * 2 + 5 - 1", "1", "0"}; + InputStream in = new ByteArrayInputStream(String.join("\r\n", inputs).getBytes()); + System.setIn(in); + + CalculatorConsole console = new CalculatorConsole(); + new Calculator( + new PostfixCalculator(), + new InfixToPostfixConverter(), + console, + console, + new CalculationRepository() + ).run(); + + String output = outputMessage.toString(); + System.out.println("output = " + output); + } +} \ No newline at end of file From fe6e3b57cee81aa9fcfc24f761ed8a74eb26cfda Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Wed, 14 Jun 2023 18:15:17 +0900 Subject: [PATCH 29/30] =?UTF-8?q?[fix]:=20readme=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=9C=20=EB=82=B4=EC=9A=A9=EB=93=A4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 63 ++++--------------------------------------------------- 1 file changed, 4 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index de661649c..9940f790c 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,18 @@ # java_calculator 자바 계산기 구현 미션 Repository입니다. -
- PR 반영한 점 - -1. CalculatorApp -- 전략 패턴을 사용하여 CalculatorApp에서 구체적인 객체를 생성하여 `Calculator`에 주입합니다. -```java - CalculatorConsole console = new CalculatorConsole(); - - new Calculator( - new PostfixCalculator(), - new InfixToPostfixConverter(), - console, - console, - new CalculationRepository() - ).run(); -``` - -2. 다양한 형태의 Converter 구현 -- 중위 표현식에서 후위 표현식을 바꿔주는 컨버터 뿐 아니라, 후위 표현식을 중위 표현식으로 변환하는 등의 다양한 수식 간의 변환을 가능하게 하는 `ExpressionConverter` 인터페이스로 추상화를 하고, 구상체인 `InfixToPosfixConverter` 클래스로 구현하여 동적으로 적절한 컨버터가 선택되도록 하였습니다. -- `convert()` 메서드에서 String 형태의 expression을 변환하여 피연산자와 연산자의 리스트 형태의 ArrayList 타입으로 수식을 변환합니다. - -3. Calculator 추상화 -- 컨버터도 추상화할 수 있으면, 구체적인 표현식을 적절하게 계산하는 계산기도 추상화할 수 있다고 생각하였습니다. -- 따라서 실제 표현식을 계산하는 `calculate()` 메서드를 담는 `BasicCalculator` 인터페이스로 추상화하고, 구체적으로 후위 표현 수식을 계산하는 `PostfixCalculator` 클래스로 구체화하였습니다. -- 마찬가지로 runnable한 Application에서 동작할 때, 계산 대상인 표현 수식에 따라 동적으로 적절한 Calculator가 선택됩니다. - -4. Validation -- 기존에 Calculator 내에서 메뉴 선택, 표현 수식을 비롯한 입력에 대한 값 검증을 하던 `validateChoiceInput()`, `validateExpression()`의 메서드는 각각 Menu 클래스 내부, `CalculatorValidator` 클래스 내부로 이동하였습니다. -- 메서드 명 또한 valid 여부에 따라 boolean 타입을 반환한다는 점에서, 이를 잘 드러낼 수 있느 `isValidInputMenu()`, `isValidExpression()` 으로 변경하였습니다. - -5. CalculationRepository - ConcurrentHashMap과 Atomic Variable을 통한 ID값 관리로 멀티 쓰레드 환경에서의 동시성 문제 고려 -- 계산 이력을 저장하기 위해서 Map을 사용한 이유는, 추후 데이터베이스로의 확장 가능성을 생각했을 때, 각 레코드 별로 고유 PK ID값을 통해 CRUD를 편리하게 하는 것을 고려하여 각 계산 결과 객체 값에 대하여 Key 값을 Integer타입으로, 계산 결과 (CalculationResult)를 Value로 저장하도록 구현하였습니다. -- 기존에는 맵의 Key 값으로, 맵의 크기를 기반으로 id 값을 결정하였고 이는 데이터 삭제 로직으로 확장되는 경우를 고려하지 못했습니다. -- 쓰기 작업(put)에서 Lock을 통해 멀티 쓰레드 환경의 동시성 문제를 해결할 수 있는 ConccurrentHashMap을 이용하였습니다. -- 마찬가지로 유니크 아이디의 경우, 맵의 Key의 타입으로 AtomicInteger을 사용하였습니다. - -6. ParameterizedTest 기반 유닛 테스트 -- 단위 테스트 코드들을 작성하였고, ParameterizedTest를 통해 다양한 입력에 대한 테스트를 수행하였습니다. -- [ ] InfixToPostfixConverter 테스트에서 Failed 1개 발생 -- [ ] 계산기 통합 테스트 Failed - -7. 상수 관리 -- 여러 클래스에서 사용되는 상수를 따로 하나의 클래스에서 관리하는 것은 객체 지향적이지 않다는 피드백을 바탕으로 각 클래스에서 사용하는 상수들은 클래스 내부로 옮겼습니다. - -8. toString의 쓰임 -- 디버그나 로깅 목적으로 toString이 사용된 다는 것을 새로 알게 되었고, -- `CalculationRepository`에 객체 저장 시에 `CalculationResult` 자체를 매개 변수로 넣어주도록 변경하였습니다. - -9. Operation -- 사칙 연산을 수행하는 `Operation` 객체를 싱글톤으로 생성하도록 LazyHolder의 방식으로 구현하였습니다. -- [ ] Operation의 Converter, Calculator에서 모두 Operation을 필요로 한다는 점 -> static하게 사용할 수 있는 방법이 없을까? 고민하고 있습니다.. ! -- [ ] 사칙 연산을 수행해내기 위해서는, String(+,-,*,/)과 Operator Enum 객체들을 Map으로 저장하는 `OperatorMap` 을 초기화하는 `Operation` 클래스의 객체가 필수적으로 생성되어야 하는데, Converter과 Calculator 모두 Operation에 의존성을 띄고 있어 설계 리팩토링을 진행해야 할 것 같습니다.. - -
- ### 이곳은 공개 Repo입니다. 1. 여러분의 포트폴리오로 사용하셔도 됩니다. 2. 때문에 이 repo를 fork한 뒤 -3. 여러분의 개인 Repo에 작업하며 +3. 여러분의 개인 Repo에 작업하며 4. 이 Repo에 PR을 보내어 멘토의 코드 리뷰와 피드백을 받으세요. ### Branch 명명 규칙 + 팀의 PR규칙 정하기 1. 여러분 repo는 알아서 해주시고 😀(본인 레포니 main으로 하셔두 되져) -2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 gituser_id을 적어주세요 :) +2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 gituser_id을 적어주세요 :) - base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main`또는 `github_id` -3. 실제 진행할 PR규칙은 멘토+팀원들과 정하여 진행해주세요 :) +3. 실제 진행할 PR규칙은 멘토+팀원들과 정하여 진행해주세요 :) - ← head repo : `여러분repo` compare : `main`로 할지 - 또는 ← head repo : `여러분repo` compare : `github_id`로 할지 - 참고 : [Github 위치 및 피드백 기준 가이드](https://www.notion.so/backend-devcourse/Github-e1a0908a6bbf4aeaa5a62981499bb215) @@ -79,7 +24,7 @@ - 스스로 OOP를 생각하고 코드로 옮길 수 있는 능력해보자 ### 요구사항 -- 콘솔로 구현입니다.(스윙으로 구현하시는 분들 계실까봐) +- 콘솔로 구현입니다.(스윙으로 구현하시는 분들 계실까봐) - 객체지향적인 코드로 계산기 구현하기 - [ ] 더하기 - [ ] 빼기 From 9cfa1d6a09c6f0b2e7cfaa4e63cd2989a9127b86 Mon Sep 17 00:00:00 2001 From: Yebin Lee Date: Fri, 11 Aug 2023 11:35:00 +0900 Subject: [PATCH 30/30] =?UTF-8?q?[docs]:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=8F=20=EA=B5=AC=ED=98=84=20=EB=82=B4=EC=9A=A9?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 111 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 9940f790c..4748c93ae 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,93 @@ -# java_calculator -자바 계산기 구현 미션 Repository입니다. +## 📌 계산기 TDD, OOP로 만들기 +### 다양한 표현 수식에 대한 사칙 연산을 수행하는 콘솔 기반의 계산기를 TDD와 OOP를 기반으로 구현해내는 프로젝트 +- 메뉴 입력(0. 종료, 1. 조회, 2. 계산)을 받는 기능 +- 이전 계산 이력에 대한 결과를 순서대로 조회하는 기능 +- 수식(중위 표현식)을 입력 받아 계산 결과 값을 출력하는 기능 +- 다양한 오류 처리 (메뉴 입력 오류, 표현 수식 오류) -### 이곳은 공개 Repo입니다. -1. 여러분의 포트폴리오로 사용하셔도 됩니다. -2. 때문에 이 repo를 fork한 뒤 -3. 여러분의 개인 Repo에 작업하며 -4. 이 Repo에 PR을 보내어 멘토의 코드 리뷰와 피드백을 받으세요. -### Branch 명명 규칙 + 팀의 PR규칙 정하기 -1. 여러분 repo는 알아서 해주시고 😀(본인 레포니 main으로 하셔두 되져) -2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 gituser_id을 적어주세요 :) - - base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main`또는 `github_id` -3. 실제 진행할 PR규칙은 멘토+팀원들과 정하여 진행해주세요 :) - - ← head repo : `여러분repo` compare : `main`로 할지 - - 또는 ← head repo : `여러분repo` compare : `github_id`로 할지 -- 참고 : [Github 위치 및 피드백 기준 가이드](https://www.notion.so/backend-devcourse/Github-e1a0908a6bbf4aeaa5a62981499bb215) +
+ +## 👩‍💻 요구 사항과 구현 내용 +### 전체적인 클래스 관계 구조도 + -### 과제를 통해 기대하는 역량 -- 깃허브를 통한 코드리뷰를 경험해보자 -- 기본적인 테스트 코드 작성 및 활용하는 능력해보자 -- 스스로 OOP를 생각하고 코드로 옮길 수 있는 능력해보자 -### 요구사항 -- 콘솔로 구현입니다.(스윙으로 구현하시는 분들 계실까봐) +### 기능 구현 + +- 콘솔로 구현입니다.(스윙으로 구현하시는 분들 계실까봐) - 객체지향적인 코드로 계산기 구현하기 - - [ ] 더하기 - - [ ] 빼기 - - [ ] 곱하기 - - [ ] 나누기 - - [ ] 우선순위(사칙연산) -- [ ] 테스트 코드 구현하기 -- [ ] 계산 이력을 맵으로 데이터 저장기능 만들기 + - [x] 더하기 + - [x] 빼기 + - [x] 곱하기 + - [x] 나누기 + - [x] 우선순위(사칙연산) +- [x] 테스트 코드 구현하기 +- [x] 계산 이력을 맵으로 데이터 저장기능 만들기 - 애플리케이션이 동작하는 동안 데이터베이스 외에 데이터를 저장할 수 있는 방법을 고안해보세요. -- (선택) 정규식 사용 +- [x] 정규식 사용 + + + + +## ✅ 피드백 반영사항 + +
+ 첫번째 PR 반영사항 + +### 1. CalculatorApp +- 전략 패턴을 사용하여 CalculatorApp에서 구체적인 객체를 생성하여 `Calculator`에 주입합니다. +```java + CalculatorConsole console = new CalculatorConsole(); + + new Calculator( + new PostfixCalculator(), + new InfixToPostfixConverter(), + console, + console, + new CalculationRepository() + ).run(); +``` + +### 2. 다양한 형태의 Converter 구현 +- 다양한 수식 간의 변환을 가능하게 하는 `ExpressionConverter` 인터페이스로 추상화를 하고, 현재 계산기 프로그램에서는 중위 표현식을 후위 표현식으로 변환하는 `InfixToPosfixConverter` 클래스로 구현하여 동적으로 적절한 컨버터가 선택되도록 하였습니다. +- `convert()` 메서드에서 String 형태의 expression을 변환하여 피연산자와 연산자의 리스트 형태의 ArrayList 타입으로 수식을 변환합니다. + +### 3. Calculator 추상화 +- 컨버터도 추상화할 수 있으면, 구체적인 표현식을 적절히 계산하여 계산 결과 값을 도출하는 계산기도 추상화할 수 있다고 생각하였습니다. +- 따라서 `calculate()` 추상 메소드를 포함하는 `BasicCalculator` 인터페이스로 추상화하고, 구체적으로 후위 표현 수식을 계산하는 `PostfixCalculator` 클래스로 구체화하였습니다. +- 마찬가지로 runnable한 Application에서 동작할 때, 계산 대상인 표현 수식에 따라 동적으로 적절한 Calculator가 선택됩니다. + +### 4. Validation +- 기존에 Calculator 내에서 메뉴 선택, 표현 수식을 비롯한 입력에 대한 값 검증을 하던 `validateChoiceInput()`, `validateExpression()`의 메서드는 각각 Menu 클래스 내부, `CalculatorValidator` 클래스 내부로 이동하였습니다. +- 메서드 명 또한 valid 여부에 따라 boolean 타입을 반환한다는 점에서, 이를 잘 드러낼 수 있느 `isValidInputMenu()`, `isValidExpression()` 으로 변경하였습니다. + +### 5. CalculationRepository - ConcurrentHashMap과 Atomic Variable을 통한 ID값 관리로 멀티 쓰레드 환경에서의 동시성 문제 고려 +- 계산 이력을 저장하기 위해서 Map을 사용한 이유는, 추후 데이터베이스로의 확장 가능성을 생각했을 때, 각 레코드 별로 고유 PK ID값을 통해 CRUD를 편리하게 하는 것을 고려하여 각 계산 결과 객체 값에 대하여 Key 값을 Integer타입으로, 계산 결과 (CalculationResult)를 Value로 저장하도록 구현하였습니다. +- 기존에는 맵의 Key 값으로, 맵의 크기를 기반으로 id 값을 결정하였고 이는 데이터 삭제 로직으로 확장되는 경우를 고려하지 못했습니다. +- 쓰기 작업(put)에서 Lock을 통해 멀티 쓰레드 환경의 동시성 문제를 해결할 수 있는 ConccurrentHashMap을 이용하였습니다. +- 마찬가지로 유니크 아이디의 경우, 맵의 Key의 타입으로 AtomicInteger을 사용하였습니다. + +### 6. ParameterizedTest 기반 유닛 테스트 +- 단위 테스트 코드들을 작성하였고, ParameterizedTest를 통해 다양한 입력에 대한 테스트를 수행하였습니다. +- [ ] InfixToPostfixConverter 테스트에서 Failed 1개 발생 +- [ ] 계산기 통합 테스트 Failed + +### 7. 상수 관리 +- 여러 클래스에서 사용되는 상수를 따로 하나의 클래스에서 관리하는 것은 객체 지향적이지 않다는 피드백을 바탕으로 각 클래스에서 사용하는 상수들은 클래스 내부로 옮겼습니다. + +### 8. toString의 쓰임 +- 디버그나 로깅 목적으로 toString이 사용된 다는 것을 새로 알게 되었고, +- `CalculationRepository`에 객체 저장 시에 `CalculationResult` 자체를 매개 변수로 넣어주도록 변경하였습니다. + +### 9. Operation +- 사칙 연산을 수행하는 `Operation` 객체를 싱글톤으로 생성하도록 LazyHolder의 방식으로 구현하였습니다. +- [ ] Operation의 Converter, Calculator에서 모두 Operation을 필요로 한다는 점 -> static하게 사용할 수 있는 방법이 없을까? 고민하고 있습니다.. ! +- [ ] 사칙 연산을 수행해내기 위해서는, String(+,-,*,/)과 Operator Enum 객체들을 Map으로 저장하는 `OperatorMap` 을 초기화하는 `Operation` 클래스의 객체가 필수적으로 생성되어야 하는데, Converter과 Calculator 모두 Operation에 의존성을 띄고 있어 설계 리팩토링을 진행해야 할 것 같습니다.. + +
### 실행결과(콘솔) ```