본문 바로가기
Java/기초문법

[Java] Java를 통해 계산기 만들기(2)

by worldcenter 2024. 9. 9.

 

 

이번에는 기존에 클래스 없이 만든 계산기 소스코드를 클래스를 적용하는 방식으로 변경해 보도록 하겠습니다.

 

우선 사칙 연산을 수행 할 Calculator 클래스를 생성하고, main 메서드에서 해당 Calculator 내부에 있는 메서드를 호출하는 방식으로 변경하였습니다.

컬렉션 중 Queue를 선택한 이유는 Set, Map의 경우 중복 저장이 불가하고, Stack의 경우 LIFO(Last In First Out)이라 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현해야 하는 요건에 맞지 않기 때문 입니다.

# Calculator 클래스
package Lv2;

import java.util.LinkedList;
import java.util.Queue;

public class Calculator {

    // private 필드 선언
    private Queue<Double> resultQ = new LinkedList<>();

    // 덧셈 메서드 선언
    public double add(double firstNum, double secondNum) {
        resultQ.add(firstNum + secondNum);
        return resultQ.peek();
    }

    // 뺄셈 메서드 선언
    public double subtract(double firstNum, double secondNum) {
        resultQ.add(firstNum - secondNum);
        return resultQ.peek();
    }

    // 곱셈 메서드 선언
    public double multiply(double firstNum, double secondNum) {
        resultQ.add(firstNum * secondNum);
        return resultQ.peek();
    }

    // 나눗셈 메서드 선언
    public double divide(double firstNum, double secondNum) {
        resultQ.add(firstNum / secondNum);
        return resultQ.peek();
    }

    // 먼저 저장된 데이터 삭제 메서드 선언
    public void removeResult() {
        System.out.println(resultQ.poll() + "값의 삭제가 완료되었습니다.");
    }

    // 모든 데이터 삭제
    public void allClear() {
        resultQ.clear();
        System.out.println("모든 결과 데이터가 삭제 되었습니다.");
    }

    // 객체 수 파악
    public void size() {
        System.out.println("결과 값의 수: " + resultQ.size());
    }

    // 큐 객체 확인
    public Queue<Double> getResultQ() {
        return resultQ;
    }

}

 

 

Calculator 클래스 생성이 완료되고 나면 이를 호출할 main 메서드가 필요합니다. App 클래스에 메인 메서드를 생성하고 다음과 같이 코드를 작성하였습니다.

하지만 이 경우 몇 가지 문제가 발생했습니다.

package Lv2;

import java.util.*;

public class App {
    public static void main(String[] args) {

        while(true) {

            try {
                // Calculator 객체 생성
                Calculator cal = new Calculator();

                // 계산기 기능 선택
                System.out.println("======== 계산기 ========");
                System.out.println("1.계산 2.Clear 3.All_Clear");
                System.out.println("=======================");
                System.out.print("기능 선택: ");
                Scanner sc = new Scanner(System.in);
                int func = sc.nextInt();

                // 계산기 기능별 동작
                switch (func) {
                    case 1:
                        // 첫 번째 피연산자 입력(0을 포함한 양의 정수)
                        System.out.println();
                        System.out.println("======== 계산 =========");
                        System.out.print("첫 번째 숫자를 입력하세요: ");
                        double firstNum = sc.nextLong();

                        // 연산자 입력
                        System.out.print("사칙연산 기호를 입력하세요(+, -, *, /): ");
                        char operator = sc.next().charAt(0);

                        // 두 번째 피연산자 입력(0을 포함한 양의 정수)
                        System.out.print("두 번째 숫자를 입력하세요: ");
                        double secondNum = sc.nextLong();

                        // 데이터 정합성 검사
                        if (firstNum < 0 || secondNum < 0) {
                            System.out.println("======== 결과값 ========");
                            System.out.println("음수는 입력이 불가합니다!");
                            continue;
                        }

                        System.out.println("======== 결과값 ========");

                        // 연산자에 따른 계산
                        switch (operator) {
                            case '+':
                                System.out.println((int) cal.add(firstNum, secondNum));
                                break;

                            case '-':
                                System.out.println((int) cal.subtract(firstNum, secondNum));
                                break;

                            case '*':
                                System.out.println((int) cal.multiply(firstNum, secondNum));
                                break;

                            case '/':
                                // 나눠 떨어지는 수는 정수로 표기되고, 소수점이 있는 경우 실수로 표기
                                if (firstNum % secondNum == 0.0) {
                                    System.out.println((int) cal.divide(firstNum, secondNum));
                                } else {
                                    System.out.println(cal.divide(firstNum, secondNum));
                                }
                                break;

                            default:
                                System.out.println("사칙연산을 잘못 입력하였습니다!");
                                break;

                        }
                        break;

                    case 2:
                        System.out.println();
                        System.out.println("======== Clear =========");
                        System.out.print("가장 먼저 저장된 데이터를 삭제하시겠습니까? (y or n): ");
                        String clear = sc.next();
                        if (clear.equals("y")) {
                            cal.removeResult();
                            continue;
                        }
                        break;

                    case 3:
                        System.out.println();
                        System.out.println("======== All_Clear =========");
                        System.out.print("저장된 모든 데이터를 삭제하시겠습니까? (y or n): ");
                        String allClear = sc.next();
                        if (allClear.equals("y")) {
                            cal.allClear();
                            continue;
                        }
                        break;
                }

            // 예외 처리
            } catch (InputMismatchException e) { // 데이터 타입 허용 범위 외 값이 입력된 경우
                System.out.println("======== 결과값 ========");
                System.out.println("잘못된 값을 입력하였습니다!");
            } catch (ArithmeticException e) { // 산술적인 에러가 발생한 경우
                System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다!");

            } finally {
                // 연산 재개 또는 종료
                System.out.println("======================");
                System.out.print("더 계산하시겠습니까? (y or exit): ");
                Scanner sc = new Scanner(System.in);
                String sys = sc.next();
                if (sys.equals("exit")) {
                    System.out.println("계산기를 종료합니다.");
                    break;
                } else {
                    continue;
                }
            }
        }
    }
}

 

 

코드 개발 시 이슈

1. 객체 초기화 문제

while 반복문안에 Calculator 객체 생성 구문이 있어 매번 객체가 초기화 되는 문제가 발생하였습니다. 그에 따라 기존에 계산한 결과 값들을 저장하지 않고 있어 다음과 같이 Queue 객체 삭제 시 null 값이 나오는 것을 확인했습니다.

이를 해결하고자 객체 생성 구문을 while 문 밖으로 빼 보았습니다.

 

 

while 문 밖에 객체를 생성하게 되면 프로그램이 시작될 때만 객체를 생성하고 이후에는 해당 객체를 활용하여 Calculator 메서드를 실행하기 때문에 초기화 될 일이 없습니다.

Calculator cal = new Calculator();

 

다만 이 방식도 문제가 있습니다. Calculator 클래스에 존재하는 사칙 연산 메서드를 실행하면 return 값으로 Queue에 있는 값을 가져오도록 했는데 peek() 메서드의 경우 첫 번째로 저장된 데이터만 가져오기 때문에 연산을 여러 번 수행하더라도 처음 수행한 연산의 결과 값만 가져오기 때문 입니다.

public double multiply(double firstNum, double secondNum) {
        resultQ.add(firstNum * secondNum);
        return resultQ.peek();
    }

 

 

2. 컬렉션 변경

그에 따라 기존에 Queue 컬렉션이 아닌 List 컬렉션으로 필드 타입을 변경해 보았습니다. List 컬렉션의 특징은 순서를 유지한 채로 저장이 가능하며, index를 통해 순서 관리가 가능하기 때문에 중복 저장도 가능합니다.

# Calculator 클래스
package Lv2;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class Calculator {

    private List<Double> resultList = new LinkedList<Double>();

    // 덧셈 메서드 선언
    public double add(double firstNum, double secondNum) {
        resultList.add(firstNum + secondNum);
        return resultList.get(resultList.size()-1);
    }

    // 뺄셈 메서드 선언
    public double subtract(double firstNum, double secondNum) {
        resultList.add(firstNum - secondNum);
        return resultList.get(resultList.size()-1);
    }

    // 곱셈 메서드 선언
    public double multiply(double firstNum, double secondNum) {
        resultList.add(firstNum * secondNum);
        return resultList.get(resultList.size()-1);
    }

    // 나눗셈 메서드 선언
    public double divide(double firstNum, double secondNum) {
        resultList.add(firstNum / secondNum);
        return resultList.get(resultList.size()-1);
    }

    // 먼저 저장된 데이터 삭제 메서드 선언
    public void removeResult() {
        System.out.println(resultList.remove(0) + "값의 삭제가 완료되었습니다.");
    }

    // 모든 데이터 삭제
    public void allClear() {
        resultList.clear();
        System.out.println("모든 결과 데이터가 삭제 되었습니다.");
    }

    // 객체 수 파악
    public void size() {
        System.out.println("결과 값의 수: " + resultList.size());
    }

    // 큐 객체 확인
    public List<Double> getResultList() {
        return resultList;
    }
}
package Lv2;

import java.util.*;

public class App {
    public static void main(String[] args) {

        // Calculator 객체 생성
        Calculator cal = new Calculator();

        while(true) {

            try {

                // 계산기 기능 선택
                System.out.println("======== 계산기 ========");
                System.out.println("1.계산 2.Clear 3.All_Clear");
                System.out.println("=======================");
                System.out.print("기능 선택: ");
                Scanner sc = new Scanner(System.in);
                int func = sc.nextInt();

                // 계산기 기능별 동작
                switch (func) {
                    case 1:
                        // 첫 번째 피연산자 입력(0을 포함한 양의 정수)
                        System.out.println();
                        System.out.println("======== 계산 =========");
                        System.out.print("첫 번째 숫자를 입력하세요: ");
                        double firstNum = sc.nextLong();

                        // 연산자 입력
                        System.out.print("사칙연산 기호를 입력하세요(+, -, *, /): ");
                        char operator = sc.next().charAt(0);

                        // 두 번째 피연산자 입력(0을 포함한 양의 정수)
                        System.out.print("두 번째 숫자를 입력하세요: ");
                        double secondNum = sc.nextLong();

                        // 데이터 정합성 검사
                        if (firstNum < 0 || secondNum < 0) {
                            System.out.println("======== 결과값 ========");
                            System.out.println("음수는 입력이 불가합니다!");
                            continue;
                        }

                        System.out.println("======== 결과값 ========");

                        // 연산자에 따른 계산
                        switch (operator) {
                            case '+':
                                System.out.println((int) cal.add(firstNum, secondNum));
                                break;

                            case '-':
                                System.out.println((int) cal.subtract(firstNum, secondNum));
                                break;

                            case '*':
                                System.out.println((int) cal.multiply(firstNum, secondNum));
                                break;

                            case '/':
                                // 나눠 떨어지는 수는 정수로 표기되고, 소수점이 있는 경우 실수로 표기
                                if (firstNum % secondNum == 0.0) {
                                    System.out.println((int) cal.divide(firstNum, secondNum));
                                } else {
                                    System.out.println(cal.divide(firstNum, secondNum));
                                }
                                break;

                            default:
                                System.out.println("사칙연산을 잘못 입력하였습니다!");
                                break;

                        }
                        break;

                    case 2:
                        System.out.println();
                        System.out.println("======== Clear =========");
                        System.out.print("가장 먼저 저장된 데이터를 삭제하시겠습니까? (y or n): ");
                        String clear = sc.next();
                        if (clear.equals("y")) {
                            cal.removeResult();
                            continue;
                        }
                        break;

                    case 3:
                        System.out.println();
                        System.out.println("======== All_Clear =========");
                        System.out.print("저장된 모든 데이터를 삭제하시겠습니까? (y or n): ");
                        String allClear = sc.next();
                        if (allClear.equals("y")) {
                            cal.allClear();
                            continue;
                        }
                        break;
                }

            // 예외 처리
            } catch (InputMismatchException e) { // 데이터 타입 허용 범위 외 값이 입력된 경우
                System.out.println("======== 결과값 ========");
                System.out.println("잘못된 값을 입력하였습니다!");
            } catch (ArithmeticException e) { // 산술적인 에러가 발생한 경우
                System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다!");

            } finally {
                // 연산 재개 또는 종료
                System.out.println("======================");
                System.out.print("더 계산하시겠습니까? (y or exit): ");
                Scanner sc = new Scanner(System.in);
                String sys = sc.next();
                if (sys.equals("exit")) {
                    System.out.println("계산기를 종료합니다.");
                    break;
                } else {
                    continue;
                }
            }
        }
    }
}

 

컬렉션 타입을 List로 변경하니 모든 기능이 정상적으로 동작함을 확인했습니다.

처음에는 사칙 연산 결과 값을 집어 넣고 먼저 들어간 값부터 삭제한다는 기능 구현에 초점이 맞춰지다보니 Queue 컬렉션을 사용하였습니다.

하지만, 계산기 프로그램의 경우 연산의 마지막 값을 반환해야 하기에 Queue가 적정치 않다는 것을 깨닫게 되었고 순서를 보장해주는 List로 변경하였으며, 빈번한 객체 삭제와 삽입이 일어나는 계산기의 특성 상 LinkedList 클래스를 객체로 사용하였습니다.

'Java > 기초문법' 카테고리의 다른 글

[Java] 컬렉션 자료구조 - List 컬렉션  (0) 2024.09.16
[Java] 익명 클래스  (0) 2024.09.11
[Java] Scanner 클래스와 메서드  (0) 2024.09.05
[Java] Java 통해 계산기 만들기(1)  (0) 2024.09.04
[Java] 타입 변환  (0) 2024.08.21