Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf

# These are Windows script files and should use crlf
*.bat text eol=crlf

25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Compiled class files
*.class

# Log files
*.log

# IntelliJ IDEA files
.idea/

# Eclipse files
.classpath
.project
.settings/

# Build output
build/
out/

# Dependency directories
.lib/
libs/

# Gradle specific
.gradle/
gradle/
46 changes: 46 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java application project to get you started.
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
* User Manual available at https://docs.gradle.org/8.1.1/userguide/building_java_projects.html
*/

plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

dependencies {
// Use JUnit Jupiter for testing.
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'

// This dependency is used by the application.
implementation 'com.google.guava:guava:31.1-jre'
testImplementation 'org.assertj:assertj-core:3.21.0'
// JUnit 5
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
}

// Apply a specific Java toolchain to ease working on different environments.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}

application {
// Define the main class for the application.
mainClass = 'java.calculator.App'
}

tasks.named('test') {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
14 changes: 14 additions & 0 deletions app/src/main/java/example/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package example;

import example.calculator.CalculatorController;
import example.calculator.model.Calculator;
import example.calculator.view.Input;
import example.calculator.view.Output;

public class Main {
public static void main(String[] args) {
CalculatorController calculatorController = new CalculatorController(new Input(), new Output(),
new Calculator());
calculatorController.start();
}
}
52 changes: 52 additions & 0 deletions app/src/main/java/example/calculator/CalculatorController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package example.calculator;

import example.calculator.model.Calculator;
import example.calculator.view.Output;
import example.calculator.view.Input;

public class CalculatorController {
private static final int MENU_VIEW_HISTORY = 1;
private static final int MENU_HANDLE_CALCULATION = 2;
private Input input;
private Output output;
private Calculator calculator;

public CalculatorController(Input input, Output output, Calculator calculator) {
this.input = input;
this.output = output;
this.calculator = calculator;
}

public void start() {
boolean isRunning = true;

while (isRunning) {
output.printMenu();
int userChoice = input.getIntInput();

switch (userChoice) {
case MENU_VIEW_HISTORY:
output.printCalculationHistory(calculator.getCalculationHistory());
break;
case MENU_HANDLE_CALCULATION:
handleCalculation();
break;
default:
isRunning = false; // 루프 종료 조건
break;
}
}
}

private void handleCalculation() {
input.getStringInput();

output.printMessage();
String expression = input.getStringInput();

String[] tokens = expression.split("\\s+");
double result = calculator.handleCalculation(tokens, expression);

output.printResult(result);
}
}
89 changes: 89 additions & 0 deletions app/src/main/java/example/calculator/model/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package example.calculator.model;

import example.calculator.model.operation.Addition;
import example.calculator.model.operation.CalculationOperation;
import example.calculator.model.operation.Division;
import example.calculator.model.operation.Multiplication;
import example.calculator.model.operation.Subtraction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class Calculator {
private static final Pattern OPERATOR_PATTERN = Pattern.compile("[+\\-*/]");
private Map<String, CalculationOperation> calculationOperations;
private List<String> calculationHistory;

Choose a reason for hiding this comment

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

Calculator가 히스토리까지 관리하는 책임을 가지고 있는 것 같네요. 분리 해주는 것이 좋아보여요

Copy link
Author

Choose a reason for hiding this comment

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

네. 단일 책임 원칙(Single Responsibility Principle)을 따르는 것이 좋을 거 같은데 생각을 못했네요 🥲


public Calculator() {
calculationOperations = new HashMap<>();
calculationOperations.put("*", new Multiplication());
calculationOperations.put("/", new Division());
calculationOperations.put("+", new Addition());
calculationOperations.put("-", new Subtraction());
Comment on lines +21 to +24
Copy link
Member

Choose a reason for hiding this comment

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

key값으로 들어가는 StringCalculationOperation들의 getOperator에 정보가 있는 거 같은데 중복되는 것 같아요!
정보를 아는 클래스에게 가져오는 건 어떨까요?


calculationHistory = new ArrayList<>();
}

public List<String> getCalculationHistory() {
return calculationHistory;
}

public double calculate(double a, double b, String operator) {
CalculationOperation operation = calculationOperations.get(operator);
if (operation == null) {
throw new IllegalArgumentException("Invalid operator: " + operator);
}

return operation.calculate(a, b);
}

private void addToCalculationHistory(String expression) {
calculationHistory.add(expression);
}

public double handleCalculation(String[] tokens, String expression) {

Choose a reason for hiding this comment

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

tokensexpression을 분리하여 매개변수로 사용하신 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

expression은 문자열 그대로 히스토리에 저장하기 위해 받고, tokens는 계산에 필요한 값들을 명확하게 표현하기 위해 따로 분리하였습니다! 따로 분리하는 것이 가독성이 좋아 보여 이렇게 사용했습니다

Choose a reason for hiding this comment

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

expression이 history를 저장하는 데만 사용하고 있어서, 가독성 측면에서 어떤게 좋은지 공감이 되질 않네요.
오히려 연산은 토큰으로하고 history 저장은 expression으로 하니 헷갈리는 것 같아요.

token은 내부에서 split 하여 사용하는 것이 좋아보입니다

List<String> operators = new ArrayList<>();
List<Double> operands = new ArrayList<>();

for (String token : tokens) {
if (OPERATOR_PATTERN.matcher(token).matches()) {
operators.add(token);
} else {
operands.add(Double.parseDouble(token));
}
}

double result = getResult(operators, operands);

String calculationExpression = expression.replaceAll("\\s+", "");
addToCalculationHistory(calculationExpression + " = " + result);
return result;
}

private double getResult(List<String> operators, List<Double> operands) {
calculateOperations(operands, operators, "*", "/");
calculateOperations(operands, operators, "+", "-");

return operands.get(0);
}

private void calculateOperations(List<Double> operands, List<String> operators, String... targetOperators) {
for (String targetOperator : targetOperators) {
for (int i = 0; i < operators.size(); i++) {
String operator = operators.get(i);
if (operator.equals(targetOperator)) {
Comment on lines +73 to +76
Copy link
Member

Choose a reason for hiding this comment

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

다른 분들의 객체지향 생활 체조 관련 리뷰 참고 해주세요!

double operand1 = operands.get(i);
double operand2 = operands.get(i + 1);
double result = calculate(operand1, operand2, operator);

operands.set(i, result);
operands.remove(i + 1);
operators.remove(i);
i--;
}
}
}
}
}
26 changes: 26 additions & 0 deletions app/src/main/java/example/calculator/model/Menu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package example.calculator.model;
public enum Menu {
조회(1, "조회"),
계산(2, "계산");

private final int value;
Copy link
Member

Choose a reason for hiding this comment

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

value는 너무 일반적인 이름인 것 같아요! 조금 더 구체적인 이름을 쓰시면 이해에 더 도움이 될 것 같아요~

private final String name;

Menu(int value, String name) {
this.value = value;
this.name = name;
}

public int getValue() {
return value;
}

public String getName() {
return name;
}

public String getFormattedInfo() {
return value + ". " + name;
}
}

13 changes: 13 additions & 0 deletions app/src/main/java/example/calculator/model/operation/Addition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package example.calculator.model.operation;

public class Addition implements CalculationOperation {
@Override
public double calculate(double a, double b) {
return a + b;
}

@Override
public String getOperator() {
return "+";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package example.calculator.model.operation;

public interface CalculationOperation {
double calculate(double a, double b);

Choose a reason for hiding this comment

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

double 타입은 연산이 부정확 할 수 있어요

https://jerry92k.tistory.com/6


String getOperator();
}
17 changes: 17 additions & 0 deletions app/src/main/java/example/calculator/model/operation/Division.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package example.calculator.model.operation;

public class Division implements CalculationOperation {
@Override
public double calculate(double a, double b) {
if (b != 0) {
return a / b;
} else {
throw new IllegalArgumentException("Cannot divide by zero.");
}
}

@Override
public String getOperator() {
return "/";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package example.calculator.model.operation;

public class Multiplication implements CalculationOperation {
@Override
public double calculate(double a, double b) {
return a * b;
}

@Override
public String getOperator() {
return "*";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package example.calculator.model.operation;

public class Subtraction implements CalculationOperation {
@Override
public double calculate(double a, double b) {
return a - b;
}

@Override
public String getOperator() {
return "-";
}
}
19 changes: 19 additions & 0 deletions app/src/main/java/example/calculator/view/Input.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package example.calculator.view;

import java.util.Scanner;

public class Input {
private Scanner scanner;

public Input() {
scanner = new Scanner(System.in);
}

public int getIntInput() {
return scanner.nextInt();
}

public String getStringInput() {
return scanner.nextLine();
}
}
25 changes: 25 additions & 0 deletions app/src/main/java/example/calculator/view/Output.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package example.calculator.view;

import example.calculator.model.Menu;
import java.util.List;

public class Output {
public void printCalculationHistory(List<String> calculationHistory) {
for (String history : calculationHistory) {
System.out.println(history);
}
}

public void printMenu() {
System.out.println(Menu.조회.getFormattedInfo());
System.out.println(Menu.계산.getFormattedInfo());
}

public void printResult(double result) {
System.out.println("결과: " + result);
}

public void printMessage() {
System.out.print("수식을 입력하세요: ");
}
}
Loading