일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- OOP
- BFS
- 누적합
- CSS
- Spring
- 이분 탐색
- Elasticsearch
- parametric search
- 내일배움캠프
- Algorithm
- SpringBoot
- 백준
- 브루트포스
- ES
- 완전탐색
- 구현
- 이분탐색
- 계산기 만들기
- 객체지향
- Java
- binary search
- 프로젝트
- Generics
- web
- programmers
- 코딩테스트
- til
- Baekjoon
- 알고리즘
- 프로그래머스
- Today
- Total
개발하는 햄팡이
[JAVA][계산기 만들기-3] Lv2. 클래스를 사용하여 객체 지향 개념을 적용한 계산기 만들기 본문
https://bitj-bitbox.tistory.com/40
[JAVA][계산기 만들기-1] github 라벨 편집, Issue Template 생성, PR Template 생성
저번 주에 HTML, CSS, JavaScript를 사용하여 간단한 팀소개 페이지 만들기가 끝나고이번 주 부터는 Java 기본 문법에 대해서 공부하면서 콘솔에 입출력을 받는 계산기 프로그램을 만드는 과제를 진행
bitj-bitbox.tistory.com
https://bitj-bitbox.tistory.com/41
[JAVA][계산기 만들기-2] Lv1. 클래스 사용하지 않고 기본 연산 계산기 만들기
오늘은 Lv.1 요구사항을 처리할 것이다.그런데 Lv1요구사항이 클래스를 사용하지 않고 기본 연산 계산기 만들기라는 것.클래스 사용하지 않고....라는게 무슨 말일까자바는 다 클래스인데....뭐 어
bitj-bitbox.tistory.com
이제 Lv2 요구사항인 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기 만들기를 할 차례이다.
사실 LV2 요구사항을 지금 봤는데 메소드도 사용하지 않았어야 했나? 싶긴하다..
근데 뭐... Java 활용 능력을 기르기 위해 다음 과제를 하는 것이기 때문에 문제는 없을 것 같다.
그리고 메소드 사용하지 말라는 말도 없었으니...
일단 시작
1. 사칙 연산을 수행하는 Calculator 클래스 만들기
Lv2 요구사항은 Calculator 클래스를 만들어서 결과를 저장하는 컬렉션 타입을 사용해야 한다.
Lv1 요구사항의 예시 코드를 보면 App class에 main 메소드를 호출하고 거기에 코드를 작성하는 방법으로 진행하도록 되어있는데 (class명을 App으로 하라는 얘기는 없긴하지만)
그냥 계산기라서 클래스이름을 Calculator로 했는데 Lv2 요구사항에서 Calculator라는 이름으로 클래스를 만들어서 분리하라고 쓰여있다...
코드를 일단 옮겨야겠다..
App 클래스를 만들어 원래 있던 코드를 옮기로 새로 작업 시작
그 다음으로 할일
1. 사칙연산을 수행하고 결과값을 반환하는 메서드 필요
2. 연산결과를 컬렉션 타입 필드에 저장해야 함
코드는 이미 이전에 다 구현을 해 놓았으니 조금만 수정하면 된다.
package calculator;
import java.util.ArrayList;
import java.util.List;
public class Calculator {
List<Double> results = new ArrayList<>();
public double calculate(int num1, int num2, char operator) {
double result = 0;
try {
switch (operator) {
case '+':
result = add(num1, num2);
break;
case '-':
result = subtract(num1, num2);
break;
case '*':
result = multiply(num1, num2);
break;
case '/':
result = 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() + ")");
}
results.add(result);
return result;
}
private int add(int a, int b) {
return a + b;
}
private int subtract(int a, int b) {
return a - b;
}
private int multiply(int a, int b) {
return a * b;
}
private double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
}
일단 여기까지 완료.
그 다음엔 main메소드를 수정해야한다.
조건에 따르면 이전에 main메소드에 구현한 모든 기능들이 똑같이 동작해야한다.
그래서 main에 있던 계산 메소드가 어차피 Calculator class에 있으니 지워버렸다.
그런데 나눗셈때문에 리턴값을 double로 맞췄더니 결과가 소수점이 아닐 경우에도 1.0 이런식으로 표현이돼서 매우 불편하다..
이걸 어떻게 해결할까 코드를 이리저리 만져봤는데
내가 생각한 방법은 if문을 이용하여 double일때와 int일 때 값이 일치하는치 확인하고
일치한다면
Integer와 Double의 toString을 이용해서 표현하는 방법인데 코드를 작성하고 나니 뭔가 지저분하고 맘에 안든다...
그래서 다른 사람들은 어떻게 하는지 검색한 결과
DecimalFormat이라는게 있다는 것을 알게되었다...!
import java.text.DecimalFormat;
DecimalFormat df = new DecimalFormat("0.##"); // 소수점 이하 최대 두 자리
while (true) {
double result = CALCULATOR.calculate(num1, num2, operator);
System.out.println("[결과] " + df.format(result));
}
이런식으로 하면 소수점 이하가 없으면 정수,
한두자리 소수면 4.5, 4.56이렇게 나오고,
세자리부터는 반올림된다.
반올림은 굳이 신경쓰고 싶지 않아서 소수점 자리 포맷을 지정하지는 않았다.
결과는 아주 만족!
기존의 결과와 똑같이 만들기 위해서 양식을 맞춰 주었다
그 다음 컬렉션에 저장이 잘 되는지 테스트
근데 모든 연산자가 /로 표시되고 있다는것을 발견. 후다닥 수정해준다.
현재까지의 코드
App.java
package calculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class App {
private static final Scanner SCANNER = new Scanner(System.in);
private static final Calculator CALCULATOR = new Calculator();
private static final DecimalFormat df = new DecimalFormat();
public static void main(String[] args) {
System.out.println("Hello, App!");
while (true) {
System.out.println("계산을 중단하려면 exit 를 입력해주세요.");
int num1 = getPositiveInteger("첫 번째 숫자를 입력하세요. (exit입력 시 종료): ");
int num2 = getPositiveInteger("두 번째 숫자를 입력하세요. (exit입력 시 종료): ");
char operator = getOperator("사칙연산 기호를 입력하세요. (exit입력 시 종료): ");
double result = CALCULATOR.calculate(num1, num2, operator);
System.out.printf("[결과] %d %s %d = %s \n", num1, operator, num2, df.format(result));
}
}
private static int getPositiveInteger(String prompt) {
while (true) {
System.out.print(prompt);
String input = SCANNER.nextLine().trim();
handleExit(input);
try {
int num = Integer.parseInt(input);
if (num < 0) {
throw new IllegalArgumentException("[Error] : 양의 정수(0 포함)만 입력 가능합니다.");
}
return num;
} catch (NumberFormatException e) {
System.out.println("[Error] : 정수를 입력해주세요.");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println("Unexpected Exception: " + e.getMessage());
}
}
}
private static char getOperator(String prompt) {
while (true) {
System.out.print(prompt);
String line = SCANNER.nextLine().trim();
handleExit(line);
if (line.length() == 1) {
char op = line.charAt(0);
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
}
System.out.println("[Error] : 유효한 사칙연산 기호를 입력해주세요. (+, -, *, /)");
}
}
private static void handleExit(String input) {
if (input.equalsIgnoreCase("exit")) {
System.out.println("프로그램을 종료합니다.");
SCANNER.close();
System.exit(0);
}
}
}
Caculator.java
package calculator;
import java.util.ArrayList;
import java.util.List;
public class Calculator {
List<Double> results = new ArrayList<>();
public double calculate(int num1, int num2, char operator) {
double result = 0;
try {
switch (operator) {
case '+':
result = add(num1, num2);
break;
case '-':
result = subtract(num1, num2);
break;
case '*':
result = multiply(num1, num2);
break;
case '/':
result = 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() + ")");
}
results.add(result);
return result;
}
private int add(int a, int b) {
return a + b;
}
private int subtract(int a, int b) {
return a - b;
}
private int multiply(int a, int b) {
return a * b;
}
private double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
}
2. Calculator 클래스 캡슐화하기
이제 그냥 선언해 두었던 결과 컬렉션을 private으로 설정하고 getter와 setter를 설정하여 직접적으로 접근할 수 없도록 제한을 걸어야할 차례다.
캡슐화를 해야하는 이유는 아래 포스팅 참조..
https://bitj-bitbox.tistory.com/39
[JAVA] OOP란? - 객체 지향 프로그래밍의 개념과 특징
1. 객체 지향 프로그래밍(OOP)란?객체 지향 프로그래밍(Object-Oriented Programming, OOP)은 데이터와 그 데이터를 처리하는 코드를 하나의 객체(Object)로 묶어 설계하는 방법이다.전통적인 프로그래밍 방
bitj-bitbox.tistory.com
그래서 이제 setter을 어떻게 설정해야할까...?
생각해봤는데 setter를 만들 필요가 있나? 생각이 드는데...
일단 새로운 결과 리스트를 만들수도 있으니 새로운 결과 리스트를 매개변수로 하는 setter를 만들고
원해 컬렉션에 해당 값들을 집어넣는 setter를 만들었다.
- 요구사항에서 setter를 만들라고 해서 만들긴했는데 뭔가 무분별한 setter를 만든 것 같은 느낌이 찝찝하다.
Lv2 마지막 요구사항에 Calculator 클래스에저장된 연산 결과등 중 가장 먼저 저장된 데이터를 삭제하는 시능을 가진 메서드를 만들으라고 했으니 삭제 메소드도 만든다..!
삭제를 하려고 생각을 해봤는데 구현체를 ArrayList로 선언을 했었다.
근데 삭제를 앞에서 한다면 ArrayList보단 LinkedList가 더 효율적일 것 같아서 LinkedList로 수정했다.
LinkedList가 더 효율적인 이유는 ArrayList는 배열 형태로 되어있어 맨 앞을 삭제하면 뒤에있는 값들을 한칸씩 땡겨오는데,
LinkedList는 배열 형태가 아니라 노드로 연결되어있기 때문에 맨 앞 노드와의 연결을 끊으면 되기 때문에 더 효율적이다.
그래서 일단 여기까지 한 최종 코드는 아래와 같다
package calculator;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Calculator {
private final List<Double> results;
public double calculate(int num1, int num2, char operator) {
double result = 0;
try {
switch (operator) {
case '+':
result = add(num1, num2);
break;
case '-':
result = subtract(num1, num2);
break;
case '*':
result = multiply(num1, num2);
break;
case '/':
result = 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() + ")");
}
this.results.add(result);
return result;
}
public Calculator() {
this.results = new LinkedList<>();
}
public List<Double> getResults() {
return this.results;
}
public void setResults(List<Double> newResults) {
this.results.clear();
if (newResults != null) {
this.results.addAll(newResults);
}
}
public void deleteFirstResult() {
this.results.remove(0);
}
private int add(int a, int b) {
return a + b;
}
private int subtract(int a, int b) {
return a - b;
}
private int multiply(int a, int b) {
return a * b;
}
private double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
}
금방 끝날 줄 알았는데 블로그 글을 쓰면서 작업해서 그런가 좀 오래 걸린다...
다음은 Java의 제네릭, 람다식, 스트림, Enum을 사용해서 좀 더 유연성과 가독성을 높인 계산기를 만들어 볼 예정이다.
'Back-End > Java' 카테고리의 다른 글
[JAVA][계산기 만들기-5] Lv3. Generics를 활용한 계산기 만들기 (2) | 2025.04.23 |
---|---|
[JAVA][계산기 만들기-4] Lv3. Enum을 활용한 계산기 만들기 (0) | 2025.04.22 |
[JAVA][계산기 만들기-1] github 라벨 편집, Issue Template 생성, PR Template 생성 (1) | 2025.04.18 |
[JAVA] OOP란? - 객체 지향 프로그래밍의 개념과 특징 (1) | 2025.04.16 |
[Java 기초] Java란? (4) | 2025.04.15 |