Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
cef4f6a
docs: README에 기능 목록 작성
juno-junho Jun 2, 2023
0e5168b
feat: project setup & gitignore setting
juno-junho Jun 2, 2023
8b786c2
docs: gitignore 설정 및 assertj 추가
juno-junho Jun 2, 2023
464ee7d
feat: 덧셈 기능 구현
juno-junho Jun 2, 2023
d681ef6
feat: 뺄셈 기능 구현
juno-junho Jun 2, 2023
100652e
feat: 곱셈 및 나눗셈 기능 구현
juno-junho Jun 2, 2023
a152c5f
test: setup 메서드로 중복 코드 제거 refactor
juno-junho Jun 2, 2023
fcb46be
docs(README) : 기능 목록 작성
juno-junho Jun 9, 2023
cdb0b84
feat(Repository) : List로 repository 생성
juno-junho Jun 9, 2023
747b790
feat(view) : 입력과 출력을 담당하는 클래스들 생성
juno-junho Jun 9, 2023
9c5d63c
docs(README) : 진행한 사항들 체크
juno-junho Jun 9, 2023
11f9b35
feat(view) : 입력과 출력 추상화
juno-junho Jun 9, 2023
daaabb9
feat : controller를 실행하는 application 생성
juno-junho Jun 9, 2023
254d78b
feat(controller) : view와 domain, repository를 잇는 컨트롤러 생성
juno-junho Jun 9, 2023
021662f
feat(controller) : 실행 코드를 담당하는 enum 생성
juno-junho Jun 9, 2023
07b941e
docs(README) : 기능목록 체크
juno-junho Jun 9, 2023
f78d702
feat: 계산 데이터 조회 기능 구현
juno-junho Jun 9, 2023
04ecda0
feat: controller에 view 주입하도록 구조 변경
juno-junho Jun 9, 2023
a01be32
docs: 조회기능 구현 후 기능목록 체크
juno-junho Jun 9, 2023
91358fc
feat(view): 식 입력받을때 메세지 추가
juno-junho Jun 9, 2023
e80fbfc
refactor(controller): 컨트롤러 리팩토링
juno-junho Jun 9, 2023
8b9fef4
test(domain): 식 형식 처리 테스트 작성
juno-junho Jun 9, 2023
faacd78
feat(domain): 식에 대한 원시값 포장 클래스 생성
juno-junho Jun 9, 2023
156e61b
test(domain): 사칙연산에 대한 테스트 작성
juno-junho Jun 9, 2023
5034d14
test(domain): 사칙연산에 대한 테스트 리펙토링
juno-junho Jun 9, 2023
f8720b9
feat(domain): 사칙연산 기능 구현
juno-junho Jun 9, 2023
ea769f0
test(domain): 중위 표현식을 후위표현식으로 바꾸는 테스트 작성
juno-junho Jun 9, 2023
d7201e3
feat(domain): 후위연산자로 변환하는 기능 구현
juno-junho Jun 9, 2023
eb3763d
feat(domain): 중위 연산을 후위 연산으로 바꾸는 기능 구현
juno-junho Jun 9, 2023
8e6ddbe
fix: 결과 값을 int형에서 double형으로 수정
juno-junho Jun 9, 2023
1cb190f
test(domain): 테스트 클래스 이름 수정
juno-junho Jun 9, 2023
b1bc8a0
fix: 결과 값 int로 변환
juno-junho Jun 9, 2023
1882786
test(domain): 테스트 이름 변경
juno-junho Jun 9, 2023
cab396b
test(domain): 식에서 계산 결과 테스트 작성
juno-junho Jun 9, 2023
ae83531
feat(domain): 사칙연산 계산 기능 구현
juno-junho Jun 9, 2023
37fd625
docs(README): 기능목록 점검 및 체크
juno-junho Jun 9, 2023
a610665
refactor(domain): Calculator 클래스 refactor
juno-junho Jun 9, 2023
c577dd7
refactor(domain): ArithmeticOperators 메서드 rename
juno-junho Jun 9, 2023
c3b3acc
refactor(domain): Expression 객체 refactor
juno-junho Jun 9, 2023
2b3fe4f
fix(domain): 두자리 이상 연산 결과 이상문제 해결
juno-junho Jun 9, 2023
2847dc7
refactor(domain): 메서드 명 변경 및 전체적 refactor
juno-junho Jun 9, 2023
e0c42fa
refactor(domain): 결과값 추가
juno-junho Jun 9, 2023
8568748
test(domain): 계산 시 정수 형 결과가 벗어 났을 경우 예외 테스트
juno-junho Jun 9, 2023
bedc166
feat(domain): 정수형 overflow, underflow 예외 처리
juno-junho Jun 9, 2023
1681a69
test(domain): 정수형 overflow, underflow 예외 처리
juno-junho Jun 9, 2023
f30f052
feat(domain): 예외를 전환해 주는 Util 클래스 생성 및 예외 처리 기능 구현
juno-junho Jun 9, 2023
7026a94
test(domain): 테스트 이름 rename
juno-junho Jun 9, 2023
c3469b0
docs(README): readme update
juno-junho Jun 9, 2023
f022143
fix(view): nextLine 입력 받는 문제 해결
juno-junho Jun 9, 2023
bba7d3b
feat(view): BufferedReaderInputView 생성 및 적용
juno-junho Jun 9, 2023
fe6b642
fix(controller): double -> int 형 변경 및 무한 루프 구현
juno-junho Jun 9, 2023
b2b4e92
refactor(repository): repository rename
juno-junho Jun 9, 2023
0a1bcd1
refactor(domain): 오타 수정
juno-junho Jun 9, 2023
49628f5
docs(README): 기능목록 명확하게 작성
juno-junho Jun 10, 2023
c422b59
refactor: interface line break 일관되게 작성
juno-junho Jun 10, 2023
c934956
refactor: 매직 넘버 제거 및 메서드 네이밍 변경
juno-junho Jun 11, 2023
52b4812
test: displayname 명확하게 refactor
juno-junho Jun 11, 2023
31d0c2b
refactor: 연산자 확인 메서드 이름 변경
juno-junho Jun 11, 2023
c2c482a
feat: 값 객체 equals hashcode 재정의
juno-junho Jun 11, 2023
7b07a10
refactor: = 값 상수화
juno-junho Jun 11, 2023
23f5a67
refactor: 상수 private로 범위 설정 및 전체적인 refactor
juno-junho Jun 12, 2023
3b4a132
test: displayname 작성 및 테스트 네임 일관성있게 변경
juno-junho Jun 12, 2023
69bf844
refactor: 상수 분리 및 Calculator 클래스 상태 값 제거
juno-junho Jun 12, 2023
39d5517
refactor: calculator 클래스 메서드 분리
juno-junho Jun 12, 2023
64b690d
refactor: Util -> StringUtil 이름 변경
juno-junho Jun 12, 2023
69bca5d
refactor: OutputView 인터페이스 사용하도록 다시 되돌리기
juno-junho Jun 12, 2023
f2064c9
test: calculator 리팩토링에 따른 테스트 리펙토링
juno-junho Jun 12, 2023
d5b9315
refactor: REGEX 오타 수정
juno-junho Jun 12, 2023
552bdd3
refactor: line break 수정
juno-junho Jun 12, 2023
9df6088
test: expression값이 같으면 Expression 클래스가 동일함을 보장한다.
juno-junho Jun 12, 2023
a425b61
refactor: Operator 인터페이스 ~able로 변경
juno-junho Jun 12, 2023
37b9706
chore: 패키지 이름 수정
juno-junho Jun 15, 2023
e1bb1da
feat: regex 생성 클래스 분리 및 상수 분리
juno-junho Jun 15, 2023
235f332
refactor: Expression을 InfixExpression으로 세분화
juno-junho Jun 15, 2023
06518a6
refactor: 도메인 패키지 안 클래스 및 인터페이스 전체적인 refactor
juno-junho Jun 15, 2023
665e715
refactor: 도메인 패키지 안 클래스 및 인터페이스 전체적인 refactor
juno-junho Jun 15, 2023
4e39667
feat: service 레이어 생성
juno-junho Jun 15, 2023
7e20296
feat(view): 콘솔로 input과 output 합치는 기능 구현
juno-junho Jun 15, 2023
500eddf
refactor(domain): REGEX 상수 클래스 인스턴스화 및 상속 막는 기능 구현
juno-junho Jun 15, 2023
95d0f50
refactor(application): application 주입 수정
juno-junho Jun 15, 2023
07d518b
feat(controller): 프로그램 종료 구현
juno-junho Jun 15, 2023
64dae33
chore: 패키지 분리 및 이동
juno-junho Jun 15, 2023
73da710
fix: 정규식 문제 해결 (\\추가)
juno-junho Jun 15, 2023
de9f0c1
fix: = 기호 수정
juno-junho Jun 15, 2023
988d8f8
refactor: 컨트롤러 콘솔 및 서비스로 리펙토링
juno-junho Jun 15, 2023
0c6d27b
docs: 기능목록 체크
juno-junho Jun 15, 2023
c210056
chore: 폴더이동
juno-junho Jun 15, 2023
ff23b5e
feat: 0으로 나눌 경우 예외 발생 기능 구현
juno-junho Jun 15, 2023
d8beb05
test: 0으로 나눌 경우 테스트 및 delimiter 수정
juno-junho Jun 15, 2023
9edbbd7
test: StringUtil 테스트 및 Expression hashCode 테스트
juno-junho Jun 15, 2023
038ad39
chore: line break 변경
juno-junho Jun 15, 2023
3bd4249
docs: 기능목록 체크 완료
juno-junho Jun 15, 2023
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
46 changes: 46 additions & 0 deletions java-calculator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
gradlew.bat
gradlew
gradle

### IntelliJ IDEA ###
.idea
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store
100 changes: 100 additions & 0 deletions java-calculator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

## 자바 계산기 구현 미션 기능목록 작성

동작 예시
```
1. 조회
2. 계산

선택 : 2

1 + 2
3

1. 조회
2. 계산

선택 : 2

1 + 2 * 3
7

1. 조회
2. 계산

선택 : 1

1 + 2 = 3
1 + 2 * 3 = 7

선택 : 2

3 - 2 * 2
-1
```

- [x] 입력
- [x] Scanner를 통한 콘솔 입력
- [x] 선택 값 입력 받기
- [x] 계산 기능 선택 시 -> 식 입력 받기
- [x] BufferedReader로 입력 받는 것 교체 / 확장 가능 하게 추상화 시키기 (OCP)
- [x] 출력
- [x] 조회 기능 선택 시 -> 조회 시 넘겨 받은 결과값 출력하기
- [x] 계산 기능 선택 시 -> 계산 후 계산한 결과 출력하기
- [x] OutputView 추상화 시키기

- [x] 컨트롤러 및 application 생성
- 컨트롤러 역할 : view와 domain을 이어 준다 / repository에서 값을 가져온다.
- 컨트롤러에서 담당 : repository를 결정한다
- view는 주입 받는다(사용자가 결정)
- 게임을 실행하는 `run()`메서드를 가진다.
- Application 클래스 역할 : 사용자
- 컨트롤러를 실행한다.
- view 객체를 생성해 컨트롤러에 주입시킨다.

- [x] 조회 기능 구현
- [x] 조회시 계산한 값이 없으면 `계산 값이 존재하지 않습니다` 메세지 출력 -> OutputView에서 담당
- [x] 계산 후 이력을 List로 저장 -> Repository에서 생성
- [x] repository 패키지 안 List 가진 일급 컬렉션으로 구현
- [x] 인터페이스로 repository 추상화
- [x] 조회 기능 선택 시 계산 이력을 List에서 가져 오기 (repository에서 controller로 직접적으로 가져오기)


- [x] 계산 기능 구현
- [x] 덧셈
- [x] 뺄셈
- [x] 나눗셈
- [x] 곱하기
- [x] 우선순위 (사칙연산)
- [x] 후위 표기식 찾아보기
- [x] 나눗셈과 곱셈이 덧셈과 뺄셈보다 우선순위가 높다
```
중위 표현식 -> 후위 표현식 전환 알고리즘 :
1. 피연산자는 스택에 넣지 않고 그냥 출력한다.
2. 연산자는 스택이 비었으면 스택에 push한다.
3. 연산자는 스택이 비어있지 않으면 스택에 있는 연산자와의 우선순위를 비교해 스택에 있는 연산자의 우선순위가 같거나 크다면 스택에 있는 연산자를 pop을 한 후 출력하고 현재 연산자는 스택에 push한다.
4. 만약 3번에서 우선순위가 현재 연산자가 더 크면 현재 연산자를 push한다.(스택에서 pop하지 않음)
5. 수식이 끝나면 스택이 빌 때 까지 pop을 한 후 출력한다.

후위 표현식 -> 계산 알고리즘
1. 피연산자면 스택에 push한다.
2. 연산자를 만나면 pop을 두번하고 각각 값을 저장한 후, 연산자에 맞는 계산을 한다.
3. 계산을 한 뒤, 결과 값은 다시 스택에 넣는다. 이 과정을 수식이 끝날 때 까지 반복한다.
4. 수식이 끝났다면 스택에 마지막 남은 값이 결과 값이 된다.
```
- [x] 식을 입력 받을 때 계산기에서 수행가능한 연산 범위를 제한 할 수 있다.
- [x] Regex를 통해서 검증하기.
- [x] 중위표현식, 후위 표현식에 따른 계산 확장 가능성 기능 구현

- [x] 정규식 문제 해결 => \\ 추가해서 해설
- [x] 0으로 나눌 경우 문제 해결
- [x] var 사용 x -> 정확히 타입 표기를 통한 명시성 증가ㅈ

- [x] 예외 처리
- [x] 조회(1), 계산(2) 이외의 값을 입력 했을 경우 `IllegalArgumentException` 발생 -> controller에서 처리
- [x] 형식 (숫자 + 공백 + 기호 + 공백 + 숫자 + 공백 + 기호 + ...)이 잘못된 경우 `IllegalArgumentException` 발생
- 공백은 무조건 하나로 처리 -> split시 space 하나로 처리해야 자를 수 있음. split 특성상 delimiter 포함 하지를 못한다.
- 정규식으로 처리
- [x] 식에 int 형 범위 벗어 나 있는 경우 `IllegalArgumentException` 발생
- [x] 계산 과정에 있어 int 자료형 overflow / underflow 발생 시 `IllegalArgumentException` 발생
- Math 클래스의 `~Exact()` 메서드 사용
20 changes: 20 additions & 0 deletions java-calculator/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id 'java'
}

group = 'com.programmers'
version = '1.0-SNAPSHOT'

repositories {
mavenCentral()
}

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

test {
useJUnitPlatform()
}
1 change: 1 addition & 0 deletions java-calculator/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'java-calculator'
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.programmers.blackdog;

import com.programmers.blackdog.controller.CalculatorController;
import com.programmers.blackdog.view.*;

public class Application {
public static void main(String[] args) {
InputView inputView = new ScannerInputView();
OutputView outputView = new PrintStreamOutputView();
Console console = new Console(inputView, outputView);

CalculatorController calculator = new CalculatorController(console);
calculator.run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.programmers.blackdog.controller;

import com.programmers.blackdog.controller.constant.Selection;
import com.programmers.blackdog.service.CalculatorService;
import com.programmers.blackdog.service.Service;
import com.programmers.blackdog.view.Console;

import java.util.List;

import static com.programmers.blackdog.controller.constant.Selection.findByCode;

public class CalculatorController {

private final Service service;
private final Console console;
Copy link

Choose a reason for hiding this comment

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

MVC 패턴과 Layered 아키텍처로 설계하신거 같아요!

Controller는 어떤 역할을 할까요? 준호님의 생각이 궁금합니다.


public CalculatorController(Console console) {
this.console = console;
this.service = new CalculatorService();
Copy link

Choose a reason for hiding this comment

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

이 Service는 왜 주입을 안받고 직접 생성하시는지 이유가 궁금합니다.

}

public void run() {
while (true) {
try {
switch (getCode()) {
case CHECK_DATA:
printAllPreviousData();
break;
case CALCULATE:
printCalculatedResultAndSave();
break;
case EXIT:
exitProgram();
return;
}
} catch (Exception e) {
Copy link

Choose a reason for hiding this comment

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

혹시 왜 매우 넓은 범위의 Exception을 통째로 catch하신지 이유가 궁금합니다.

console.printErrorMessage(e.getMessage());
}
}
}

private Selection getCode() {
Copy link

Choose a reason for hiding this comment

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

getCode() 보다는 조금더 명확한 의미가 좋을거같아요.

일반적으로 저는 get을 프로퍼티나 필드의 값을 가져올때 사용합니다.

사용자로부터 입력을 받아온다면?

int selectionCode = console.getSelectionCode();
return findByCode(selectionCode);
}


private void printAllPreviousData() {
List<String> calculatedData = service.findAll();
console.printExpressions(calculatedData);
}
Comment on lines +48 to +51

Choose a reason for hiding this comment

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

데이터라고 �명명하는 것은 메소드 기준에서는 다소 추상적인것 같아요. 데이터가 어떤 데이턴지 구현명을 붙이는건 어떨까요? 이 코드 내에서 데이터가 아닌 부분이 없듯이 계산된 데이터 라는 부분이 중간에 계산된 부분인지 결과인지 파악하기 어려운 것 같습니다.

Choose a reason for hiding this comment

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

지역변수명도 동일합니당


private void printCalculatedResultAndSave() {
String expression = console.getExpression();
int result = service.calculate(expression);

service.save(expression, result);

console.printCalculatedResult(result);
}

private void exitProgram() {
console.printEndMessage();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.programmers.blackdog.controller.constant;

import java.util.Arrays;

public enum Selection {
CHECK_DATA(1), CALCULATE(2), EXIT(3);

private final int code;

Selection(int code) {
this.code = code;
}

public static Selection findByCode(int code) {
return Arrays.stream(Selection.values())
.filter(selection -> selection.code == code)
.findAny()
.orElseThrow(() -> new IllegalArgumentException("해당 값이 없습니다"));
Copy link

Choose a reason for hiding this comment

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

해당 값이 없다기보다
"메뉴를 잘못 입력하였습니다", "올바른 메뉴 번호를 입력해주세요"
라는 더 친절한 메시지와 함께 사용자에게 보여주면 좋을거같아요.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.programmers.blackdog.domain;

import java.util.Arrays;

public enum ArithmeticOperators implements Operable {
ADDITION("+", 0) {
@Override
public int apply(int a, int b) {
return Math.addExact(a, b);
}
}, SUBTRACTION("-", 0) {
@Override
public int apply(int a, int b) {
return Math.subtractExact(a, b);
}
}, MULTIPLICATION("*", 1) {
@Override
public int apply(int a, int b) {
return Math.multiplyExact(a, b);
}
}, DIVISION("/", 1) {
@Override
public int apply(int a, int b) {
validateDivideNumIsZero(b);
return a / b;
}
};

private static void validateDivideNumIsZero(int num) {
if (num == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
}

private final String operator;
private final int priority;

ArithmeticOperators(String operator, int priority) {
this.operator = operator;
this.priority = priority;

Copy link

Choose a reason for hiding this comment

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

공백 지우면 깔끔할거같아요~

}

public static ArithmeticOperators convertTokenToOperator(String token) {
return Arrays.stream(values())
.filter(operator -> operator.getOperator().equals(token))
.findAny()
.orElseThrow();
Copy link

Choose a reason for hiding this comment

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

메시지를 같이 전달해주면 친절할거 같습니다.

}

public static boolean isNotOperator(String token) {
return Arrays.stream(values())
.noneMatch(operator -> operator.getOperator().equals(token));
}

public String getOperator() {
return operator;
}

public int getPriority() {
return priority;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.programmers.blackdog.domain;

@FunctionalInterface
public interface Operable {
Copy link

Choose a reason for hiding this comment

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

good

int만 지원되는건 아쉽네요. 다른 방법이 있지 않을까요?

Choose a reason for hiding this comment

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

i think so too.

int apply(int a, int b);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.programmers.blackdog.domain;

import java.util.Arrays;
import java.util.stream.Collectors;

import static com.programmers.blackdog.domain.ArithmeticOperators.values;

public class RegexGenerator {
Copy link

Choose a reason for hiding this comment

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

무엇을 하는 클래스인가요?


private static final String REGEX_PREFIX = "^\\d+\\s([";
private static final String REGEX_SUFFIX = "]\\s\\d+\\s)+$";
public static final String PREFIX = "\\";
public static final String SUFFIX = "";

public String generateWithOperator(ArithmeticOperators... arithmeticOperators) {
Copy link

Choose a reason for hiding this comment

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

ArithmeticOperators...

ArithmeticOperators[]

는 동작할 때 어떤 차이가 있을까요?

StringBuilder operators = new StringBuilder();
for (ArithmeticOperators arithmeticOperator : arithmeticOperators) {
operators.append(PREFIX).append(arithmeticOperator.getOperator());
}
return REGEX_PREFIX + operators.append(REGEX_SUFFIX);
}

public String generateWithAllOperator() {
String allOperators = Arrays.stream(values())
.map(ArithmeticOperators::getOperator)
.collect(Collectors.joining(PREFIX, PREFIX, SUFFIX));
return REGEX_PREFIX + allOperators + REGEX_SUFFIX;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.programmers.blackdog.domain.calculator;

import com.programmers.blackdog.domain.expression.Expression;

public interface AbstractCalculator {
int calculate(Expression expression);
Copy link

Choose a reason for hiding this comment

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

계산 결과가 int만 지원되는건 아쉽네요!

Choose a reason for hiding this comment

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

계산 결과가 단순 int value로 리턴되기보다 객체로 바라볼 순 없을까요?

}
Loading