| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- 스프링 배치
- 템플릿 메서드 패턴
- spring boot
- 스케줄러
- Spring Batch
- 코드카타
- java
- GoF 23
- 트러블슈팅
- Effective Java
- 프로그래머스
- 빌더 패턴
- Til
- Spring
- 백엔드
- redis
- 프록시 패턴
- 토스
- 디자인 패턴
- 스프링
- lv1
- 계산기
- 성능 개선
- 추상클래스
- DB
- 로드밸런서
- 김영한
- 이펙티브 자바
- 자바
- 배치
Archives
- Today
- Total
김코딩
계산기 Lv.2 본문
오늘은 Lv. 2 계산기를 구현해보았습니다.
오늘의 요구사항은 이렇습니다.
Lv 2. 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기 만들기
- 사칙연산을 수행 후, 결과값 반환 메서드 구현 & 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
- 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
- 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
- 1) 양의 정수 2개(0 포함)와 연산 기호를 매개변수로 받아 사칙연산(➕,➖,✖️,➗) 기능을 수행한 후 2) 결과 값을 반환하는 메서드와 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성합니다.
- Lv 1에서 구현한 App 클래스의 main 메서드에 Calculator 클래스가 활용될 수 있도록 수정
- 연산 수행 역할은 Calculator 클래스가 담당
- 연산 결과는 Calculator 클래스의 연산 결과를 저장하는 필드에 저장
- 소스 코드 수정 후에도 수정 전의 기능들이 반드시 똑같이 동작해야합니다.
- 연산 수행 역할은 Calculator 클래스가 담당
- App 클래스의 main 메서드에서 Calculator 클래스의 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)
- 간접 접근을 통해 필드에 접근하여 가져올 수 있도록 구현합니다. (Getter 메서드)
- 간접 접근을 통해 필드에 접근하여 수정할 수 있도록 구현합니다. (Setter 메서드)
- 위 요구사항을 모두 구현 했다면 App 클래스의 main 메서드에서 위에서 구현한 메서드를 활용 해봅니다.
- Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후 App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정
- 키워드 : 컬렉션
- 컬렉션에서 ‘값을 넣고 제거하는 방법을 이해한다.’가 중요합니다!
- 키워드 : 컬렉션
요구사항 세부 정리
| 클래스 설계 | Calculator 클래스를 생성하고, 내부에 연산 메서드와 결과 저장 필드를 구성 |
| 연산 기능 구현 | calculate(int num1, int num2, char operator) 메서드에서 +, -, *, / 연산 처리 |
| 결과 저장 | 연산이 수행될 때마다 결과를 컬렉션(예: Queue, List, Map, Set)에 저장 |
| 캡슐화 적용 | 결과 저장 필드는 private으로 선언하고, getResults()로만 간접 접근 |
| App 클래스 수정 | Lv1의 App 클래스에서 연산 처리를 Calculator 클래스로 위임 |
| 기존 기능 유지 | 기능이 클래스로 분리되더라도 Lv1과 동일한 동작(계산 반복, exit로 종료)은 유지해야 함 |
| 가장 오래된 결과 삭제 | 저장된 결과들 중 가장 먼저 저장된 값을 제거하는 removeResult() 메서드를 구현하고 App에서 호출할 수 있도록 구성 |
Calculator.java 클래스
import java.util.LinkedList;
import java.util.Queue;
public class Calculator {
// 가장 오래된 저장 값을 지운다 : 큐 (FIFO -> 먼저 들어온 사람이 먼저 나간다.)
/**
* Queue<Double> results = new LinkedList<>();
* results.add(); -> 큐에 값을 삽입한다.
* results.poll()(); -> 큐에 값을 제거한다. 큐가 공백이면 null 반환
* results.size(); -> 큐의 크기를 반환한다.
* results.isEmpty(); -> 큐가 비어있는지 확인한다.
*/
private Queue<Double> results = new LinkedList<>();
public double calculate(int num1, int num2, char operator) {
double result;
if (operator == '+') {
result = add(num1, num2);
} else if (operator == '-') {
result = subtract(num1, num2);
} else if (operator == '*') {
result = multiply(num1, num2);
} else if (operator == '/') {
result = divide(num1, num2);
} else {
throw new IllegalArgumentException("올바른 연산자를 입력해주세요.");
}
results.add(result);
return result;
}
private double divide(double num1, int num2) {
double result;
if (num2 == 0) {
throw new ArithmeticException("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
} else {
result = num1 / num2;
}
return result;
}
public double add(int num1, int num2) {
double result;
result = num1 + num2;
return result;
}
public double subtract(int num1, int num2) {
double result;
result = num1 - num2;
return result;
}
public double multiply(int num1, int num2) {
double result;
result = num1 * num2;
return result;
}
public Queue<Double> getResults() {
return results;
}
public void removeResult() {
if (!results.isEmpty()) {
results.poll();
}
}
}
App.java 클래스
import java.util.InputMismatchException;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Calculator calc = new Calculator();
while (true) {
// 첫 번째 숫자 입력 : 정수를 입력 받기 위한 스캐너 변수와 메시지를 매개변수로 받습니다.
int num1 = inputNumber(sc, "첫 번째 숫자를 입력하세요: ");
// 두 번째 숫자 입력
int num2 = inputNumber(sc, "두 번째 숫자를 입d력하세요: ");
// 연산자 입력
char operator = getOperator(sc);
// 연산자에 맞는 연산 수행
double result;
try {
result = calc.calculate(num1, num2, operator);
} catch (ArithmeticException | IllegalArgumentException e) {
System.out.println(e.getMessage());
continue;
}
// 연산 결과 출력
System.out.println("결과: " + result);
System.out.print("전체 결과를 확인하시겠습니까?(궁금하다면 yes를 입력해주세요): ");
checkingResult(sc, calc);
System.out.println("가장 오래된 결과를 삭제하시겠습니까?(삭제하고싶다면 del을 입력해주세요.): ");
removeOldestResult(sc, calc);
// if 문을 도입하여 exit 입력 시 프로그램 종료
System.out.print("더 계산하시겠습니까? (exit 입력 시 종료): ");
String order = sc.nextLine();
if (order.equals("exit")) {
System.out.println("계산기를 종료합니다. 안녕히 가세요");
break;
}
}
}
private static void checkingResult(Scanner sc, Calculator calc) {
String checkResult = sc.nextLine();
if (checkResult.equals("yes")) {
System.out.println("지금까지 계산한 결과: " + calc.getResults());
}
}
private static void removeOldestResult(Scanner sc, Calculator calc) {
String checkDel = sc.nextLine();
if (checkDel.equals("del")) {
calc.removeResult();
System.out.print("현재 남아있는 결과값: " + calc.getResults());
sc.nextLine();
}
}
private static char getOperator(Scanner sc) {
System.out.print("사칙연산 기호를 입력하세요(예: +,-,*,/) : ");
return sc.nextLine().charAt(0);
}
private static int inputNumber(Scanner sc, String message) {
while (true) {
System.out.print(message);
try {
int num = sc.nextInt();
if (num < 0) {
System.out.println("0 이상의 정수만 입력 가능합니다.");
continue;
}
sc.nextLine();
return num;
} catch (InputMismatchException e) {
System.out.println("잘못된 입력입니다. 숫자를 입력해주세요.");
sc.nextLine();
}
}
}
}
왜 Queue를 선택하였을까?
이번 요구사항에서는 다음과 같은 기능이 필요하였습니다
Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현
이 요구사항은 FIFO(First-In-First-Out) 구조를 필요로 하고, 그에 딱 들어맞는 자료구조가 바로 Queue입니다.
그렇다면 다른 컬렉션 프레임워크는 사용하면 안될까?
자바에는 List, Set, Map, Queue 등 다양한 컬렉션 프레임워크가 있습니다. 그중 어떤 컬렉션이 적합한지는 요구사항에 따라 결정됩니다.
| Queue | FIFO | 가장 먼저 들어온 값을 먼저 제거 | 가장 적합 |
| List | 인덱스 기반 순서 유지 | 인덱스로 접근 가능하지만, 맨 앞 제거 시 비효율 | 가능은 하나 덜 적합 |
| Set | 중복 불가, 순서 없음 | 순서와 중복 제어 목적일 때 사용 | 부적합 |
| Map | 키-값 쌍 | 연산 결과처럼 단순한 값 저장에는 적합하지 않음 | 부적합 |
정리하자면, Queue는 이번 요구사항에서 요구한 데이터 처리 방식(FIFO)에 가장 적합한 자료구조였습니다.
Queue 란?
Queue는 먼저 들어온 데이터가 먼저 나가는 선입선출(FIFO: First-In-First-Out) 구조를 가진 자료구조입니다.
줄을 서는 구조와 비슷하게, 먼저 들어온 요소가 가장 먼저 처리됩니다.
마치 은행 창구에서 먼저 줄 선 사람이 먼저 처리되는 것과 같은 구조라고 보면 이해하기 쉽습니다.
자바에서 Queue는 어떻게 사용될까?
자바에서는 Queue 인터페이스를 구현한 대표적인 클래스인 LinkedList나 ArrayDeque 등을 통해 사용할 수 있습니다.
자주 사용하는 Queue 메서드
| add(E e) | 큐에 요소 추가 (꽉 차면 예외 발생) | queue.add(1.0); |
| offer(E e) | 큐에 요소 추가 (꽉 차면 false 반환) | queue.offer(1.0); |
| poll() | 큐의 맨 앞 요소 꺼내고 제거 (비어있으면 null) | queue.poll(); |
| remove() | 큐의 맨 앞 요소 꺼내고 제거 (비어있으면 예외) | queue.remove(); |
| peek() | 맨 앞 요소를 꺼내지 않고 조회 | queue.peek(); |
| element() | 맨 앞 요소 조회 (비어있으면 예외) | queue.element(); |
| isEmpty() | 큐가 비어있는지 확인 | queue.isEmpty(); |
| size() | 큐에 저장된 요소 개수 반환 | queue.size(); |
Queue<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
queue.add("C");
System.out.println(queue.poll()); // 출력: A (제거됨)
System.out.println(queue.peek()); // 출력: B (제거되지 않음)