Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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();
}
}
47 changes: 47 additions & 0 deletions app/src/main/java/example/calculator/CalculatorController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package example.calculator;

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

public class CalculatorController {
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() {
while (true) {
output.printMenu();
int choice = input.getIntInput();

switch (choice) {
case 1:
output.printCalculationHistory(calculator.getCalculationHistory());
break;
case 2:
handleCalculation();
break;

Choose a reason for hiding this comment

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

true, 1, 2는 변수명으로 의미있는 이름을 부여해주세요

default:
System.out.println("잘못된 선택입니다. 다시 선택해주세요.");

Choose a reason for hiding this comment

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

👍

}
}
}

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

System.out.print("수식을 입력하세요: ");

Choose a reason for hiding this comment

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

출력의 책임은 Output이 갖는 것이 좋겠네요

String expression = input.getStringInput();

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

output.printResult(result);
}
}
87 changes: 87 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,87 @@
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;

public class Calculator {
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 (token.matches("[+\\-*/]")) {

Choose a reason for hiding this comment

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

Pattern객체를 상수로 만드는 것도 좋겠네요

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--;
}
}
}
}
}
16 changes: 16 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,16 @@
package example.calculator.model;

public enum Menu {
조회(1),
계산(2);

Choose a reason for hiding this comment

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

일반적으로 이름에 한글을 작성하지 않아요. Enum의 인스턴스의 네이밍 컨벤션에 맞게 수정해주세요
https://stackoverflow.com/questions/3069743/coding-conventions-naming-enums


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는 너무 일반적인 이름인 것 같아요! 조금 더 구체적인 이름을 쓰시면 이해에 더 도움이 될 것 같아요~


Menu(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}
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();
}
}
21 changes: 21 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,21 @@
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.조회.getValue() + ". " + Menu.조회);
System.out.println(Menu.계산.getValue() + ". " + Menu.계산);
Copy link
Member

Choose a reason for hiding this comment

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

출력되는 정보들이 Menu라는 클래스가 대부분 알고 있는 것 같아요! 여기서 개별적으로 가져와서 조합하는 것보다는 정보를 많이 알고 있는 클래스에게 맡기는 건 어떤가요?

}

public void printResult(double result) {
System.out.println("결과: " + result);
}
}
28 changes: 28 additions & 0 deletions app/src/test/java/example/calculator/model/CalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package example.calculator.model;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class CalculatorTest {

@ParameterizedTest
@MethodSource("testData")
public void testCalculate(double operand1, double operand2, String operator, double expected) {
Calculator calculator = new Calculator();
double result = calculator.calculate(operand1, operand2, operator);
assertThat(result).isEqualTo(expected);
}

private static Stream<Arguments> testData() {
return Stream.of(
Arguments.of(1, 2, "+", 3),
Arguments.of(1, 2, "-", -1),
Arguments.of(2, 3, "*", 6),
Arguments.of(4, 2, "/", 2)
);
Comment on lines +20 to +26
Copy link
Member

Choose a reason for hiding this comment

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

👍

}
}
Loading