일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Elasticsearch
- 브루트포스
- 이분 탐색
- Spring
- SpringBoot
- OOP
- 완전탐색
- web
- 이분탐색
- til
- 알고리즘
- CSS
- binary search
- 내일배움캠프
- ES
- Baekjoon
- 객체지향
- 백준
- 구현
- 계산기 만들기
- 코딩테스트
- 프로그래머스
- Algorithm
- parametric search
- programmers
- 누적합
- BFS
- Java
- 프로젝트
- Generics
- Today
- Total
개발하는 햄팡이
[JAVA][계산기 만들기-2] Lv1. 클래스 사용하지 않고 기본 연산 계산기 만들기 본문
오늘은 Lv.1 요구사항을 처리할 것이다.
그런데 Lv1요구사항이 클래스를 사용하지 않고 기본 연산 계산기 만들기라는 것.
클래스 사용하지 않고....라는게 무슨 말일까
자바는 다 클래스인데....
뭐 어쨌든 다른 클래스 만들지 말고 main에서만 하라고 하는 것 같은 느낌인데
일단 Calculator라는 패키지와 클래스를 만들고 해당 위치에 main함수를 입력하여 진행하기로 한다!
1. 숫자 입력받기
첫번째로 처리해야 할 부분은 입출력.
딱 봤을때부터 양의 정수를 받아야하니깐 양의 정수 범위에 있지 않은 애들은 어떻게 예외처리를 해야하지...
라는 생각이 들었다.
처음엔 그냥 단순하게 Scanner를 사용해서 int형을 받았다.
Scanner의 nextInt()메소드로 정수를 쉽게 받을 수 있는데 음수도 받을 수 있다.
그런데 요구사항은 양의 정수만 받아야 하기 때문에 양의 정수가 아닐경우에 다시 받아야할 것 같다.
그래서 예외처리도 하고 반복문도 돌려주고 하면 한 곳에 다 넣기엔 좀 지저분할 것 같아서
메소드를 따로 분리했다.
클래스를 사용하지 말고가 메소드는 분리하지 말란 말은 없었으니 일단 분리했다.
나름 잘 짰다고 생각했는데
돌리고 예외처리를 확인하는 중에 만난 첫번째 문제.
문자를 입력했더니 엄청난 루프에 걸린다.
내가 Scanner를 싫어하는 이유....
int를 받을려고 nextInt()를 호출했는데 int가 아닌 다른 값이 들어왔을 때 값이 그대로 Scanner에 남아있게 된다.
그래서 계속 잘못 됐다고 반복을 하는 것.
그래서 next()를 호출해서 Scanner 버퍼에 담겨있는 토큰을 없애줘야한다.
next()라는 모든 타입을 입력받는 메소드를 호출해서
정상적으로 토큰을 받은 다음 어디에도 저장하지 않고 갖다 버린다고 생각하면 된다.
음의 정수를 받았을땐 어쨌든 nextInt()가 정상적으로 값을 받긴하니깐 그냥 그대로 반복문을 돌아도 된다.
위처럼 수정하고 나서 다시 테스트를 해보면
의도한대로 잘 나오는 것 같다.
일단 입출력 받기 성공
2. 사칙연산 입력받기
그 다음으로 할 일은 사칙연산 기호를 입력받기.
계산기를 만들어야하기 때문에 당연히 사칙연산 기호를 받아야한다.
사칙연산 기호 받는 메소드 만들기 시작!
숫자를 입력받는 메소드와 비슷한 구조로 만들어줬다.
다른 점은 String을 입력받아야하기때문에 nextLine()으로 받은 다음 공백은 trim으로 없애주고,
길이가 0이 아니면 연산자말고 다른 값이 들어와있다는 것이기 때문에 다시 입력하라고 한다.
참고로 trim()을 하고 length체크도 해서 어차피 길이가 1인데 왜 chatAt(0)으로 첫번째를 가져왔는가?
라고 하면 String의 비교는 .equals() 메소드를 사용하는데
.equals()메소드는 내부 로직을 살펴보면
일단 원시 타입 비교인 char비교와 다르게 메소드를 호출한다. 벌써 연산이 하나 더 있고,,
String은 안에 값이 없을 경우 NullPointException이 발생할 수 있다.
그러나 char는 원시타입이라서 무조건 기본값이 있어 Exception이 발생하지 않는다.
이건 모든 ==연산은 다 해당된다.
그리고 뭐 나중에 숫자를 입력받는데 324532854 처럼 길 경우 String으로 비교하는 것보다 int로 바꾸어 비교하는게 더 빠르다.
이건 String은 그 값 한칸한칸 잘라서 char 배열로 담고있는 것인데 int는 숫자 한개를 담고 있기때문에
String 비교시 배열을 순회하면서 일치여부를 하나하나 확인한다.
java에 익숙한 사람들에게는 당연한 말이지만
처음 java를 공부하는 사람들이라면 이런 디테일한 내용을 알고 싶어할 것 같아서 주절주절 써봤다.
그리고 연산을 수행하는 기능을 만들기 전에 내가 브랜치 이름은 io-handler로 만들어서 일단 머지를 한 다음 진행해야겠다...
나 혼자 PR 올리고 나혼자 승인하고 ㅋㅋㅎㅋㅎㅋㅎㅋ
뭐 근데 연습하기니깐....
코드합치기는 머지 커밋을 생성하여 합치는 방법 말고 리베이스로 합쳤다.
머지와 리베이스의 차이점은 어디에서 어디로 합쳐졌다는 머지 커밋이 생기는거란 나중에 남는 깃 로그의 형태 정도?
여기선 간단하게 하고 나중에 내용을 다뤄봐야겠다.
3. 연산 수행
입력은 받았으니 이제 연산을 해야한다.
이제 입력받은 값들로 연산을 해보자!
나는 switch가 좀 더 가독성이 높다고 생각해서 switch구문을 이용할 것이다.
그리고 모든 연산을 메소드로 분리해서 작성할 것이다.
나는 한 곳에 모든 코드가 주루룩 있는 것을 별로 좋아하지 않는다.
물론 코테 볼땐 바빠서 그렇게 하긴하는데 수정에 수정을 거쳐서 최대한 읽기 쉽도록 바꾸는 편.
이렇게 안하면 나중에 다시 봤을때 이해를 못한다...
private static int add(int a, int b) {
return a + b;
}
private static int subtract(int a, int b) {
return a - b;
}
private static int multiply(int a, int b) {
return a * b;
}
private static double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
이렇게 사칙연산 메소드로 만들어주고..
그나저나 클래스를 사용하지 말라고 해서 한 곳에 주루룩 작성중인데 매우 불편하다...
당장 다른곳으로 옮기고 싶다.
그 다음은 연산자에 따라서 다른 메소드를 호출해주고, 각종 에러를 처리해주고,
결과를 main에서 출력한다.
테스트 결과
위에서 입력받는거 테스트를 안하고 왔더니 바로 생긴 문제
또 Scanner문제..또 개행문자 문제
위에서도 잘못된 값이 남아있어서 털어줘야된다고 했는데
0을 입력한 후, 다음으로 넘어가기위해 enter를 치면 nextInt는 정수만 쏙 빼가고 enter라는 개항문자는 남아있어 생기는 문제이다.
그 다음으로 사칙연산이 와야하는데 개행문자만 오니깐 Scanner입장에선 오류를 낼수밖에 없다.
원래 숫자 입력을 받으면서 생기는 곳에서만 Scanner의 버퍼를 털어줬는데..
모든 숫자 입력 후에 nextLine()으로 털어줬다.
그런데 커밋 올릴려고 봤더니 .gitignore파일이 없어서 바이트 코드 파링과 인텔리제이 관련 각종 파일이 올라가고 있어서 충돌이 났다..
그래서 다 삭제하고 다시 .gitignore 파일을 추가한 다음 다시 커밋..
깔끔해졌다...
다음부턴 레포 만들자마자 .gitignore설정 필수...
현재 코드
package calculator;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Calculator {
private static final Scanner SCANNER = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("Hello, Calculator!");
int num1 = getPositiveInteger("첫 번째 숫자를 입력하세요: ");
int num2 = getPositiveInteger("두 번째 숫자를 입력하세요: ");
char operator = getOperator("사칙연산 기호를 입력하세요: ");
try {
switch (operator) {
case '+':
System.out.printf("[결과] %d + %d = %d%n", num1, num2, add(num1, num2));
break;
case '-':
System.out.printf("[결과] %d - %d = %d%n", num1, num2, subtract(num1, num2));
break;
case '*':
System.out.printf("[결과] %d * %d = %d%n", num1, num2, multiply(num1, num2));
break;
case '/':
System.out.printf("[결과] %d / %d = %f%n", num1, num2, divide(num1, num2));
break;
default:
System.out.println("[Error] : 알 수 없는 연산자입니다.");
}
} catch (ArithmeticException e) {
System.out.println("[Error] : " + e.getMessage());
} catch (Exception e) {
System.out.println("[Error] : 연산 중 오류가 발생했습니다. (" + e.getMessage() + ")");
}
SCANNER.close();
}
private static int add(int a, int b) {
return a + b;
}
private static int subtract(int a, int b) {
return a - b;
}
private static int multiply(int a, int b) {
return a * b;
}
private static double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
private static int getPositiveInteger(String prompt) {
while (true) {
System.out.print(prompt);
try {
int input = SCANNER.nextInt();
SCANNER.nextLine();
if (input < 0) {
throw new IllegalArgumentException("[Error] : 양의 정수(0 포함)만 입력 가능합니다.");
}
return input;
} catch (InputMismatchException e) {
System.out.println("[Error] : 정수를 입력해주세요.");
SCANNER.nextLine();
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static char getOperator(String prompt) {
while (true) {
System.out.print(prompt);
String line = SCANNER.nextLine().trim();
if (line.length() == 1) {
char op = line.charAt(0);
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
}
System.out.println("[Error] : 유효한 사칙연산 기호를 입력해주세요. (+, -, *, /)");
}
}
}
4. 계산 무한 반복 & exit입력 시 프로그램 종료
그 다음으로 할일은
반복문을 사용하되, 반복의 종료를 알려주는 “exit” 문자열을 입력하기 전까지 무한으로 계산을 진행할 수 있도록 소스 코드를 수정하기
이건 그냥 단순하게 이제까지 작성한 코드를 while문에 넣고 계속 연산할 수 있게 하고,
입력받는 중간중간 exit가 입력될 경우 시스템을 종료시키는 코드를 넣으면 된다.
나는 정상적으로 프로그램을 종료했다(코드 0)는 의미를 넣고 싶었고, 메소드 분리를 한 상태라서 메소드 사이사이 중단을 해줘야하는 상황이었기 때문에 반복문을 break하지 않고 exit입력 시
System.exit(0)코드를 실행하도록 했다.
.
.
.
그렇게 하려고 했더니 정수 값을 받는 부분에서 String을 받아야하는 상황이 발생했다.
그래서 nextInt()로 받지 않고 nextLine()으로 받아 int형으로 파싱해주는 방법으로 구현하였다.
Integer.parseInt()를 사용하였는데 이 때문에 Exception처리도 다 바꿔줘야했다.
연산자 받는 부분은 어차피 String을 받고 있었어서 그냥 handleExit()메소드로 검사 한번 해주고 끝
일단 내가 생각할 수 있는 부분은 모두 테스트 통과..
이렇게 Lv1은 마무리 되었다.
이제 자바의 꽃 클래스로 객체 만들어서 코드를 짜는 Lv2를 할 차례이다.
이건 다음 시간에 계속..
여러가지 상황을 생각하고 겪어보고 처리하는 과정이 너무 재미있는 것 같다...
아직 쉬워서 재미있는 건진 모르겠지만..
사람들이 이상하다고 생각할 수 있지만 사실이다.
원래부터 인형 집을 만든다던가..레고를 만든다던가 뭔가 만들고 구상하고 그런걸 좋아하는 편인데
손에 힘이 없어서 힘을 써야하는 만들기에서 벽에 부딪혀 점점 흥미를 잃었었다.
그런데 웬걸 코딩은 그냥 키보드 뚝딱뚝딱으로 가만히 앉아서 만들 수 있다니 ^.^...너무 좋다