2단계 실습 시작
- Slack을 통해 merge가 되는지 확인한 후에 코드 리뷰 3단계 과정으로 다음 단계 미션을 진행한다.
문자열 사칙 연산 계산기 구현
| 이번 미션의 핵심은 내가 구현하는 코드에 단위 테스트를 추가하는 경험을 하는 것이다.
| 모든 예외 상황을 처리하기 위해 너무 복잡하게 접근하지 않아도 된다.
기능 요구사항
- 사용자가 입력한 문자열 값에 따라 사칙연산을 수행할 수 있는 계산기를 구현해야 한다.
- 입력 문자열의 숫자와 사칙 연산 사이에는 반드시 빈 공백 문자열이 있다고 가정한다.
- 나눗셈의 경우 결과 값을 정수로 떨어지는 값으로 한정한다.
- 문자열 계산기는 사칙연산의 계산 우선순위가 아닌 입력 값에 따라 계산 순서가 결정된다. 즉, 수학에서는 곱셈, 나눗셈이 덧셈, 뺄셈 보다 먼저 계산해야 하지만 이를 무시한다.
- 예를 들어 2 + 3 * 4 / 2와 같은 문자열을 입력할 경우 2 + 3 * 4 / 2 실행 결과인 10을 출력해야 한다.
프로그래밍 요구사항
- 메소드가 너무 많은 일을 하지 않도록 분리하기 위해 노력해 본다.
기능 분리 힌트
- 테스트할 수 있는 단위로 나누어 구현 목록을 만든다.
- 덧셈
- 뺄셈
- 곱셈
- 나눗셈
- 입력 값이 null이거나 빈 공백 문자일 경우 IllegalArgumentException throw
- 사칙연산 기호가 아닌 경우 IllegalArgumentException throw
- 사칙 연산을 모두 포함하는 기능 구현
- 공백 문자열을 빈 공백 문자로 분리하려면 String 클래스의 split(" ") 메소드를 활용한다.
- 반복적인 패턴을 찾아 반복문으로 구현한다.
요구사항 정리 및 기능 분리
1. 문자열을 공백 단위로 분리한다.
2. 입력값이 존재하지 않는 경우 확인, 공백단위로 분리한 어레이의 사이즈가 홀수로 나타나는지 않으면 에러 메세지 출력.
3. 공백단위로 분리한 어레이에서 홀수열에 존재하는 문자열이 숫자가 아니면 에러 메세지 출력.
4. 공백단위로 분리한 어레이에서 짝수열에 존재하는 문자열이 연산자가 아니면 에러 메세지 출력
5. 연산을 종료할 때는 q를 입력할 수 있도록 수정.
6. 덧셈.
7. 뺄셈.
8. 곱셈.
9. 나눗셈.
9-1. 분모가 0일 경우에 에러 메세지 출력.
기능은 README.md 에 정리하였다.
해야할 것 (ToDO), 완료한 것 (Done)으로 구분하였으며, 처음부터 모든걸 다 완벽히 적겠어 라기 보다는 큰 틀을 잡는다고 생각하고 진행하였다. README.md는 진행하면서 계속해서 추가해 나가면 된다고 한다.
import calculator.Calculator;
import java.util.Arrays;
import java.util.Scanner;
public class Solution {
public static void main(String[] args) {
Solution solution = new Solution();
solution.run();
}
private void run(){
/*
계산을 진행
오류가 발생했을 때 에러 메세지를 돌려준다.
*/
while (true){
String cmd = requestInput();
if (cmd.equals("q")){
break;
}
try{
validInput(cmd);
Calculator cal = new Calculator(cmd);
cal.calc();
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
private String requestInput(){
/*
값을 입력 받고, 전달 받은 값을 돌려준다.
*/
System.out.println("===============================");
System.out.println("계산할 값을 입력하세요");
System.out.println("계산을 종료하고 싶다면 q를 입력해주세요");
System.out.println("===============================");
Scanner sc = new Scanner(System.in);
return sc.nextLine();
}
private void validInput(String cmd){
String[] operatorAry = {"+","-","*","/"};
String[] cmdAry = cmd.split(" ");
if (cmdAry.length <= 1 || cmdAry.length % 2 == 0) {
throw new IllegalArgumentException("값을 제대로 입력 하셨나요?");
}
for (int i = 0; i < cmdAry.length; i+=2) {
if (!cmdAry[i].matches("^[0-9]*$")) {
// 입력 값이 숫자가 아닐 경우
throw new IllegalArgumentException("입력 값이 숫자가 아닙니다.");
}
}
for (int i = 1; i < cmdAry.length; i+=2) {
if (!Arrays.asList(operatorAry).contains(cmdAry[i])) {
// 사칙연산 기호가 아닌 경우
throw new IllegalArgumentException("사칙 연산 기호가 존재하지 않습니다.");
}
}
}
}
Solution class이다.
Solution class 에서는 프로그램을 실행하는 역할과, 입력값을 받고 입력 받은 값이 알맞은 포맷의 형태를 지니고 있는지 확인하는 작업을 진행하고있다.
Pull Request를 요청하고 보니 개인적으로 아쉬운 점이 보인다.
1. class 명
- Solution이라는 클래스 보다 Main 이라는 클래스로 이름을 짓고 실행기능만 가지고 있는 것은 어떨까?
2. 기능 분리
- 입력값을 받고, 검증하는 부분을 새로운 클래스로 나누는 것은 어떨까?
- 예를 들어 Input이라는 클래스를 생성하고, 그 클래스 안에는 printInfo, getInput, checkValidInput 이라는 메소드 명을 만들고
- printInfo : 사용자에게 알림 메시지를 보냄
- getInput : 사용자로부터 값을 입력 받음
- checkValidInput : 입력 받은 값이 알맞은 형태를 지니고 있는지 검증하는 부분을 갖는다.
위와 같이 변경을 하는 것도 좋아 보인다.
package calculator;
import java.util.Arrays;
public class Calculator {
String cmd;
public Calculator(String cmd) {
this.cmd = cmd;
}
public void calc(){
String[] cmdAry = cmd.split(" ");
int res = Integer.parseInt(cmdAry[0]);
for (int i = 1; i < cmdAry.length; i+=2) {
if (cmdAry[i].equals("+")) {
res = plus(res, Integer.parseInt(cmdAry[i+1]));
} else if (cmdAry[i].equals("-")) {
res = minus(res, Integer.parseInt(cmdAry[i+1]));
} else if (cmdAry[i].equals("*")) {
res = multi(res, Integer.parseInt(cmdAry[i+1]));
} else if (cmdAry[i].equals("/")) {
res = divide(res, Integer.parseInt(cmdAry[i+1]));
}
}
System.out.println(res);
}
public Integer plus(int a, int b){
return a+b;
}
public Integer minus(int a, int b){
return a-b;
}
public Integer multi(int a, int b){
return a*b;
}
public Integer divide(int a, int b){
if (b == 0){
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return a/b;
}
}
Calculation class이다.
계산을 담당하는 부분이다.
여기서도 의문이 드는 부분이 두가지있다.
1. depth를 줄이기 위해 메소드를 분리시키는 것
- for문 -> (if문) 연산자 확인 -> 계산
- 위와 같은 형태로 계산이 진행되고 있는데, 나눗셈을 하는 부분에서 분모가 0인 경우를 예외 처리하려고 보니 depth가 3이 된다.
- 이를 피하기 위해 계산 부분을 따로 빼내었는데, 이렇게 나누고 보니 개인적으로는 가독성이 더 떨어지는 것 같다. depth를 줄이기 위해 이렇게 메소드를 나누는 것이 옳은 것인가 의문이 든다.
계산기와 같은 기능을 하는 프로그램을 만드는 것이 처음이라 그런지, 생각보다 시간도 오래 걸렸고 내가 잘 하고 있는 것인지 의문이 많이 들었다. 너무 조급해 하지 않고 멘토님의 피드백을 바로 받아들여 한 걸음 앞으로 나아가 보도록 하겠다.
계산기의 기능은 README.md 에 작성을 하였습니다.
계산기를 구현 하다보니 몇가지 의문이 발생하였습니다.
클래스, 메소드를 적절하게 나누고 있지 못하는 것 같습니다.
-- Solution class를 살펴보면 input을 받고, input이 적절한 포맷으로 들어오는 것 까지 확인하고 있습니다.
-- input을 받는 것과 포맷을 확인하는 부분을 따로 떼어내는 것이 좀 더 좋았을까요??
지금 구조에서는 Solution class가 너무 많은 역할을 하고 있는건 맞습니다
입력 담당, 출력 담당, 연산 담당, 입력한 문자에 대한 검증 담당
이런식으로 나눠져야 테스트를 작성하시거나 객체의 재 활용 측면에서도 더 장점이 많겠지요
이런 부분에 대한 연습은 앞으로 자동차 미션 하시면서 점점 연습해나가시면 됩니다
너무 고민 많이 안 하셨으면 좋겠습니다
요구 사항을 보면 "반복적인 패턴을 찾아 반복문으로 구현" 하라고 명시되어 있습니다.
-- 음.. 추가적으로 딱히 변경할 부분이 보이질 않습니다. 제가 놓치고 있는 부분이 있을까요??
이 부분은 그냥 힌트 정도로 보시면 될 것 같습니다
그리고 반복적으로 보여지는 로직이 있다면 method로 추출해서
재 사용성을 높이는 방향으로 작성하시면 될 것 같습니다
계산 기능을 살펴보면 plus, minus, multi, divide를 각각의 메소드를 구현하였습니다.
-- depth를 줄이기 위해 시도한 방법인데 마음에 들지 않습니다.
-- 사실 제가 원하는 부분은 for 문 -> (if문) 연산자 확인 -> (if 문) 연산자가 나눗셈(/) 일때 에러 메세지 출력
-- 위와 같이 작성을 하려고 했으나 depth가 3이나 되버리는 바람에 다시 메소드로 분리하였습니다.
-- 더 좋은 방법이 있을까요??
Enum을 좀 활용하시면 연산에 대한 역할 분담을 나눠볼 수 있을 것 같은데요
이번 미션 까지는 워밍업이라서 Review Request를 다시 요청드리지는 않겠습니다만
두 가지 의견을 드리고 싶습니다
먼저 Enum에 대해서 한번 정리하고 가시면 좋을 것 같습니다
그리고 제가 리뷰했던 분 중에 Enum을 활용하셔서 이 요구사항을 구현해주신 분이 계신데요
다른 분들은 어떻게 같은 문제에 대해서 해결했는지 한번 참고해보시면
더 도움 되실 것 같아서 공유 드립니다
https://github.com/bingbingpa/java-racingcar/tree/step2/src/main/java/calculator/calculator
참고 부탁 드립니다
commit message를 조금 더 작게 작게 나눠서 올려야 할까요??
지금 정도 수준이면 충분할 것 같습니다
추가적으로 더 만들어야 하는 기능이 있을까요??
자동차 미션에서 본격적으로 연습해보시죠
금년은 유난히 더 더운 것 같습니다.
코로나도 기승을 부리는데 건강에 더욱 신경을 써야 할 것 같습니다!
건강 조심하세요!
많은 질문을 하여 죄송합니다ㅜㅜ
전혀 죄송해 하지 않으셔 됩니다
왜 죄송해 하시나요
언제든 궁금하거나 고민 있으시면 코멘트나 DM 주셔도 됩니다
날씨 더운데 지치지만 마시고 꼭 마지막까지 완주하시기를 응원할께요
️
일단 자동차 미션부터 본격적으로
피드백 정리
Solution class가 너무 많은 역할을 하고있음.
입력 담당, 출력 담당, 연산 담당, 입력한 문자에 대한 검증 담당으로 나눴으면 더 좋았을 것이다.
테스트를 작성하시거나 객체의 재 활용 측면에서도 더 장점이 많아질 것이다.
Enum에 대해서 한번 정리하고 가시면 좋을 것 같다.
https://techblog.woowahan.com/2527/
https://github.com/bingbingpa/java-racingcar/tree/step2/src/main/java/calculator/calculator
'강의 > TDD, Clean Code with Java 12기' 카테고리의 다른 글
4단계 자동차경주 (우승자) (8일차) (0) | 2021.07.27 |
---|---|
3단계 자동차경주 - 피드백반영 (6일차, 7일차) (0) | 2021.07.25 |
3단계 자동차 경주 (5일차) (0) | 2021.07.24 |
2단계 문자열 계산기 피드백 반영 (4일차) (0) | 2021.07.23 |
자동차 경주 - 학습 테스트 (1일차) (0) | 2021.07.20 |