김코딩

계산기 Lv.2 본문

TIL

계산기 Lv.2

김코딩딩 2025. 4. 17. 11:16

오늘은 Lv. 2 계산기를 구현해보았습니다.

오늘의 요구사항은 이렇습니다.


Lv 2. 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기 만들기

  • 사칙연산을 수행 후, 결과값 반환 메서드 구현 & 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
    • 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
    • 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
    • 1) 양의 정수 2개(0 포함)와 연산 기호를 매개변수로 받아 사칙연산(➕,➖,✖️,➗) 기능을 수행한 후 2) 결과 값을 반환하는 메서드와 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성합니다.
  • Lv 1에서 구현한 App 클래스의 main 메서드에 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 (제거되지 않음)

 

'TIL' 카테고리의 다른 글

계산기 Lv.3  (0) 2025.04.22
이놈의 Enum, 그거 어떻게 쓰는건데?  (0) 2025.04.22
계산기 Lv.1  (0) 2025.04.16
객체지향  (1) 2025.04.15
KPT 회고  (0) 2025.04.11