Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ repositories {

dependencies {
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.assertj:assertj-core:3.22.0'
testImplementation 'org.junit.jupiter:junit-jupiter:1.9.1'
testImplementation 'org.assertj:assertj-core:3.24.1'
}

test {
Expand Down
28 changes: 12 additions & 16 deletions src/main/java/calcproject/Main.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
package calcproject;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import calcproject.di.container.CalcManagerDependencyInjectionContainer;
import calcproject.engine.CalcExpressionTokenizer;
import calcproject.engine.Calculator;
import calcproject.models.CalcResultRecordModel;
import calcproject.repository.CalcResultRecordRepository;
import calcproject.repository.MemoryCalcResultRecordRepository;
import calcproject.factory.CalcManagerViewFactory;
import calcproject.factory.CalcResultRecordRepositoryFactory;
import calcproject.factory.ConsoleViewCalcManagerViewFactory;
import calcproject.factory.InMemoryCalcResultRecordRepositoryFacotry;
import calcproject.service.CalcManager;
import calcproject.view.console.CalcConsoleView;

public class Main {
public static void main(String[] args) {
Map<Integer, CalcResultRecordModel> calcMap = new HashMap<>();
int startIdx = 0;
CalcResultRecordRepository calcResultRecordRepository =
new MemoryCalcResultRecordRepository(calcMap, startIdx);
Scanner scanner = new Scanner(System.in);
CalcConsoleView calcConsoleView = new CalcConsoleView(scanner);
CalcExpressionTokenizer calcExpressionTokenizer = new CalcExpressionTokenizer();
Calculator calculator = new Calculator(calcExpressionTokenizer);

CalcManager calcManager = new CalcManager(calcResultRecordRepository, calcConsoleView, calcConsoleView,
calculator);
CalcManagerViewFactory calcManagerViewFactory = new ConsoleViewCalcManagerViewFactory();
CalcResultRecordRepositoryFactory calcResultRecordRepositoryFactory = new InMemoryCalcResultRecordRepositoryFacotry();

CalcManagerDependencyInjectionContainer calcManagerDependencyInjectionContainer =
new CalcManagerDependencyInjectionContainer(calcResultRecordRepositoryFactory, calcManagerViewFactory,
calculator);
Copy link

Choose a reason for hiding this comment

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

모든 객체가 main을 통해서 생성되고 있어서, main과 강한 의존성을 가지겠네요.
구조를 짜고 계층형으로 분리하면 좀 더 좋을 것 같아요.


CalcManager calcManager = calcManagerDependencyInjectionContainer.createCalcManager();
calcManager.startCalcManager();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package calcproject.di.container;

import calcproject.engine.Calculator;
import calcproject.factory.CalcManagerViewFactory;
import calcproject.factory.CalcResultRecordRepositoryFactory;
import calcproject.repository.CalcResultRecordRepository;
import calcproject.service.CalcManager;
import calcproject.view.CalcInput;
import calcproject.view.CalcOutput;

public class CalcManagerDependencyInjectionContainer {
Copy link

Choose a reason for hiding this comment

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

DependencyInjectionContainer라는 네이밍은 너무 스프링 기술에 치중되어 있는 느낌이 드는데요,
실제로 스프링의 기술을 구현해서 사용하는 것이 아니라면, 다른 이름이 좋아보여요


private CalcResultRecordRepositoryFactory calcResultRecordRepositoryFactory;

private CalcManagerViewFactory calcManagerViewFactory;

private Calculator calculator;

public CalcManagerDependencyInjectionContainer(
CalcResultRecordRepositoryFactory calcResultRecordRepositoryFactory,
CalcManagerViewFactory calcManagerViewFactory, Calculator calculator) {
this.calcResultRecordRepositoryFactory = calcResultRecordRepositoryFactory;
this.calcManagerViewFactory = calcManagerViewFactory;
this.calculator = calculator;
}

public CalcManager createCalcManager() {
CalcResultRecordRepository calcResultRecordRepository =
calcResultRecordRepositoryFactory.createCalcResultRecordRepository();

CalcInput calcInput =
calcManagerViewFactory.createCalcInput();

CalcOutput calcOutput =
calcManagerViewFactory.createCalcOutput();

CalcManager calcManager = new CalcManager(
calcResultRecordRepository,
calcInput,
calcOutput,
this.calculator
);

return calcManager;
}
}
29 changes: 28 additions & 1 deletion src/main/java/calcproject/engine/CalcExpressionTokenizer.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
package calcproject.engine;

import java.util.ArrayList;
import java.util.List;

public class CalcExpressionTokenizer {
public List<String> tokenizeExpression(String expression) {
return null;
List<String> tokens = new ArrayList<>();
StringBuilder sb = new StringBuilder();

for (int i = 0; i < expression.length(); i++) {
char ch = expression.charAt(i);

if (ch == ' ') {
continue;
}

if (Character.isDigit(ch)) {
sb.append(ch);
} else {
String numberString = sb.toString();
tokens.add(numberString);
sb.setLength(0);

tokens.add(Character.toString(ch));
}
Copy link

Choose a reason for hiding this comment

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

if (isOperator) 를 만들어서 else를 사용하지 않는 건 어떠신가요?

}

if (sb.length() > 0) {
String numberString = sb.toString();
tokens.add(numberString);
Comment on lines +24 to +25
Copy link

Choose a reason for hiding this comment

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

반복되는 코드가 나오는 것 같은데 공통 메서드로 추출하는 건 어떠신가요?

}

return tokens;
}
}
5 changes: 5 additions & 0 deletions src/main/java/calcproject/engine/Calculable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package calcproject.engine;

public interface Calculable {
public double calculate(double num1, double num2);
}
71 changes: 70 additions & 1 deletion src/main/java/calcproject/engine/Calculator.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package calcproject.engine;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class Calculator {
private CalcExpressionTokenizer calcExpressionTokenizer;
Expand All @@ -9,8 +11,75 @@ public Calculator(CalcExpressionTokenizer calcExpressionTokenizer) {
this.calcExpressionTokenizer = calcExpressionTokenizer;
}

private boolean isNumber(String token) {
for (int i = 0; i < token.length(); i++) {
if (!Character.isDigit(token.charAt(i)))
return false;
}
return true;
}

public List<String> tokensToPostfixNotation(List<String> tokens) {
Stack<String> stack = new Stack<>();
List<String> postFixNotationTokens = new ArrayList<>();
for (String currentToken : tokens) {
if (isNumber(currentToken)) {
postFixNotationTokens.add(currentToken);
continue;
}

while (!stack.empty()) {
Operator currentOperator = Operator.opValueOf(currentToken);
Operator stackPeekOPerator = Operator.opValueOf(stack.peek());

if (stackPeekOPerator == Operator.UnSupportedOp) {
return null;
}
Copy link

Choose a reason for hiding this comment

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

List을 반환값으로 가지고 있는데 null을 반환하는 이유가 있을까요?


if (currentOperator.getPriority() > stackPeekOPerator.getPriority()) {
break;
}

postFixNotationTokens.add(stack.pop());
}

stack.add(currentToken);
}

while (!stack.empty()) {
postFixNotationTokens.add(stack.pop());
}

return postFixNotationTokens;
}

public double calculatePostfixNotation(List<String> postFixNotationTokens) {
Stack<Double> stack = new Stack<>();

for (int i = 0; i < postFixNotationTokens.size(); i++) {
String token = postFixNotationTokens.get(i);
Copy link

Choose a reason for hiding this comment

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

for each를 사용해도 되겠네요


if (isNumber(token)) {
double num = Double.valueOf(token);
stack.push(num);
} else {
Operator operator = Operator.opValueOf(token);

double operand2 = stack.pop();
double operand1 = stack.pop();
Comment on lines +67 to +68
Copy link

Choose a reason for hiding this comment

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

stack이 비어있다면 어떻게 되나요?

double result = operator.calculate(operand1, operand2);
stack.push(result);
}
}

return stack.pop();
}

public double calculateExpression(String expression) {
List<String> tokens = calcExpressionTokenizer.tokenizeExpression(expression);
return Double.NaN;
List<String> postfixTokens = tokensToPostfixNotation(tokens);
double calcResult = calculatePostfixNotation(tokens);

return calcResult;
}
}
44 changes: 44 additions & 0 deletions src/main/java/calcproject/engine/Operator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package calcproject.engine;

import java.util.Arrays;

public enum Operator {
Plus("+", 0, (a, b) -> a + b),
Minus("-", 0, (a, b) -> a - b),
Multiply("*", 1, (a, b) -> a * b),
Divide("/", 1, (a, b) -> a / b),
UnSupportedOp("", -1, (a, b) -> Double.NaN);
Copy link

Choose a reason for hiding this comment

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

반환 값으로 UnSupportedOp를 사용하게 되면 어떻게 되나요?


private String operator;
private int priority;
private Calculable calculable;

Operator(String operator, int priority, Calculable calculable) {
this.operator = operator;
this.priority = priority;
this.calculable = calculable;
}

public static Operator opValueOf(String op) {
return Arrays.stream(values())
.filter(
value -> value
.getOperator()
.equals(op)
)
.findAny()
.orElse(Operator.UnSupportedOp);
}

public String getOperator() {
return this.operator;
}

public int getPriority() {
return this.priority;
}

public double calculate(double num1, double num2) {
return this.calculable.calculate(num1, num2);
}
}
5 changes: 5 additions & 0 deletions src/main/java/calcproject/engine/tokenaction/TokenAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package calcproject.engine.tokenaction;

public interface TokenAction {
void perform();
}
Copy link

Choose a reason for hiding this comment

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

안 쓰는 인터페이스는 지워주세요

10 changes: 10 additions & 0 deletions src/main/java/calcproject/factory/CalcManagerViewFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package calcproject.factory;

import calcproject.view.CalcInput;
import calcproject.view.CalcOutput;

public interface CalcManagerViewFactory {
public CalcInput createCalcInput();

public CalcOutput createCalcOutput();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package calcproject.factory;

import calcproject.repository.CalcResultRecordRepository;

public interface CalcResultRecordRepositoryFactory {
public CalcResultRecordRepository createCalcResultRecordRepository();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package calcproject.factory;

import calcproject.view.CalcInput;
import calcproject.view.CalcOutput;
import calcproject.view.console.CalcConsoleView;

public class ConsoleViewCalcManagerViewFactory implements CalcManagerViewFactory {

private CalcConsoleView calcConsoleView;

public ConsoleViewCalcManagerViewFactory() {
this.calcConsoleView = createCalcConsoleView();
}

public CalcConsoleView createCalcConsoleView() {
CalcConsoleView calcConsoleView = new CalcConsoleView();
return calcConsoleView;
}

@Override
public CalcInput createCalcInput() {
return this.calcConsoleView;
}

@Override
public CalcOutput createCalcOutput() {
return this.calcConsoleView;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package calcproject.factory;

import java.util.HashMap;
import java.util.Map;

import calcproject.models.CalcResultRecordModel;
import calcproject.repository.CalcResultRecordRepository;
import calcproject.repository.MemoryCalcResultRecordRepository;

public class InMemoryCalcResultRecordRepositoryFacotry implements CalcResultRecordRepositoryFactory {
@Override
public CalcResultRecordRepository createCalcResultRecordRepository() {
Map<Integer, CalcResultRecordModel> calcMap = new HashMap<>();
int startIdx = 0;
CalcResultRecordRepository calcResultRecordRepository =
new MemoryCalcResultRecordRepository(calcMap);

return calcResultRecordRepository;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public class MemoryCalcResultRecordRepository implements CalcResultRecordReposit
private Map<Integer, CalcResultRecordModel> calcMap;
Copy link

Choose a reason for hiding this comment

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

입력과 조회(모든 레코드) 두 가지 기능만 사용하고 있는데 굳이 Map을 쓰신 이유가 있나요?

private int lastIdx;

public MemoryCalcResultRecordRepository(Map<Integer, CalcResultRecordModel> calcMap, int startIdx) {
public MemoryCalcResultRecordRepository(Map<Integer, CalcResultRecordModel> calcMap) {
this.calcMap = calcMap;
this.lastIdx = startIdx;
this.lastIdx = 0;
}

@Override
Expand All @@ -27,7 +27,7 @@ public void saveCalcResultRecord(CalcResultRecordModel calcResultRecord) {
public List<CalcResultRecordModel> loadCalcResultRecords() {
return this.calcMap.values()
.stream()
.sorted(Comparator.comparing(calcModel -> calcModel.getId()))
.sorted(Comparator.comparing(CalcResultRecordModel::getId))
.collect(Collectors.toList());
}
}
2 changes: 1 addition & 1 deletion src/main/java/calcproject/service/CalcManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ private void executeCmd(Command cmd) {
break;
}
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/calcproject/view/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public enum Command {

public static Command valueOf(int choiceNum) {
return Arrays.stream(values())
.filter(value -> value.equals(choiceNum))
.filter(value -> value.getCmdIdx() == choiceNum)
Copy link

Choose a reason for hiding this comment

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

비교하는 메서드를 따로 만드는건 어떨까요?

.findAny()
.orElse(CALCULATE.EXIT);
Copy link

Choose a reason for hiding this comment

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

CALCULATE.EXIT는 사용자 의도로 인해 종료한다는 느낌인데요
강제종료를 의도하신거라면 TERMINATE는 어떠신가요?

}
Expand Down
Loading