끄적끄적/TIL

TIL #6 계산기프로젝트 - 클래스, 객체, 컬렉션, 예외처리

hubaek 2024. 9. 11. 12:22


저번에는 절차지향적으로 단순한 사칙연산 계산이였다면, 이번엔 클래스와 객체를 설계해서 객체지향개념을 활용한 계산기 프로그램을 만들고자 한다.

 

과제 내용보고 적은 요구사항(간단)
간단하게 적은 클래스, 메서드 설계

 

 

Calculator.java

처음엔 연산 결과를 저장하는 컬렉션 타입 필드를 생성하라고 했을땐, 떠오르기 힘들었고 어떻게 시작을 해야하나 막막했었다.

그래서 사칙연산 메서드를 먼저 작성을 해보려고 했다.

처음엔 하나의 메서드가 아닌 add, subtract, multiply, divide 4개의 메서드로 나누려고 했지만, 

App.java의 main에서 Scanner를 통해서 사칙연산을 입력받기에 메서드 호출에서 어떻게 하지? 라는 고민과 함께 메서드를 통합하였다.

// 연산 결과를 저장하는 컬렉션 타입 필드
private List<Double> resultList = new ArrayList<>();

public double arithmeticOperation(int firstInputNum, int lastInputNum, char operator) {
        double result;

        switch (operator) {
            case '+' -> result = firstInputNum + lastInputNum;
            case '-' -> result = firstInputNum - lastInputNum;
            case '*' -> {
                if (lastInputNum == 0) {
                    throw new IllegalArgumentException("0으로 곱할 수 없습니다.");
                } else {
                    result = firstInputNum * lastInputNum;
                }
            }
            case '/' -> {
                if (lastInputNum == 0) {
                    throw new IllegalArgumentException("0으로 나눌 수 없습니다.");
                } else {
                    result = (double) firstInputNum / lastInputNum;
                }
            }

            default -> throw new IllegalArgumentException("잘못된 연산자입니다.");
        }

        // 연산 결과를 컬렉션에 저장
        resultList.add(result);
        return result;

    }

클래스를 활용하지 않았을때, 활용했던 switch문을 메서드에 넣고, 연산결과를 컬렉션에 저장하기 위해서 resultList를 선언하고

arithmeticOperation 메서드에서 연산결과를 List에 추가하고, 연산결과를 return했음.

곱하기와 나누기에서는 0으로 나눌 수 없는 부분과 연산기호에서 Level1에선 if문으로 예외를 방지했다면,

Level2에서는 잘못된 인수가 메서드에 전달될 때 발생하는 IllegalArgumentException 예외를 발생시켰다.

처음에는 어떤 예외가 있는지 잘 몰라서, 이런 상황에 예외를 어떤 것을 써야하는지 구글링을 통해서 예외를 적용 시켰다.. 예외는 아직 어려운,,

 

 


미션에 getter / setter 만들기가 있었는데, getter는 쉽게 생각했지만 setter는 굳이 필요한 부분인가? 라고 생각하며 일단 만들어두기만 했다.

그러면서 동시에 가장 먼저 저장된 데이터를 삭제하는 메서드 까지 추가하라는 과제가 있어서 추가했지만 활용성은 생각하지 못했다.

// 연산 결과를 반환하는 메서드
    public List<Double> getResultList(){
        if (resultList.isEmpty()) {
            throw new IndexOutOfBoundsException("저장된 연산 결과가 없습니다.");
        }
        return resultList;
    }


    public void setResultList() {
        this.resultList = resultList;
    }

    // 저장된 연산 결과 가장 먼저 저장된 데이터를 삭제하는 메서드
    public void removeResultList() {
        // 연산결과가 비어 있을 때 삭제하면 예외
        if (resultList.isEmpty()) {
            throw new IndexOutOfBoundsException("저장된 연산 결과가 없습니다.");
        }
        resultList.remove(0);
    }

removeResultList를 만들면서 App.java에 테스트용도로 연산 1번을 하고, remove하고, get을 했을 때 

IndexOutOfBoundsException 예외 오류가 떠서, getReulstList와 removeResultList에 리스트가 비어 있으면 예외처리 되도록 로직을 추가 하였다. 

위에서 IllegalArgumentException예외 처리하면서 살짝은 익숙해 졌는지 조금 수월하게 예외를 떠오르면서 예외처리를 한 것 같다.

 

App.java

처음에 계산기가 계속 반복되어야하고, 종료 명령어 입력시에 종료를 생각했을때 종료를 시키는건 알겠는데 계산기가 무한 반복되어야하는 게 생각이 안났다.

while(조건문)에서 조건문을 그냥 int num =1 > 0 이런식으로 생각을 했는데 인텔리제이에서 힌트를 주었다.

그냥 while(true)로 적으면 break문을 만날때 까지 무한 반복이라는 것... (아주 쉽지만 하나 배운 포인트,,)

 

// Calculator 클래스에 IllegalArgumentException 예외처리로 대체, 이코드도 쓰는게 맞을까? 고민
            while (true) {
                operator = sc.nextLine().charAt(0);
                if (operator == '+' || operator == '-' || operator == '*' || operator == '/') {
                    break;
                } else {
                    System.out.println("계산 기호를 다시 입력해주세요(+,-,*,/)");
                }
            }

그리고 최초엔 연산기호를 입력 받을 땐, while문과 if문을 통해서 연산기호 검증을 했지만, Calculator에서 예외 처리를 해서 이 코드가 또 필요가 있을까? 라는 고민에 일단 주석처리를 해둔 상황.. 

처음 조건문을 통해 예외 방지를 하고 예외처리 해두는건 맞는 것 같은데 아직 뭐가 더 좋은 코드인지는 모르겠다..

 

// while문 안의 로직
// 입력한 정수 2개와 연산기호를 calculator 클래스의 메서드 호출로 사칙연산 계산
            try {
                double result = calculator.arithmeticOperation(firstInputNum, lastInputNum, operator);
                System.out.println("결과 : " + result);
            }catch (IllegalArgumentException e) {
                // arithmeticOperation에서 예외 발생시, 오류메시지 print
                System.out.println("오류 : " + e.getMessage());
            }


            System.out.println("더 계산하시겠습니까? (yes/no)" );
            String endApp = sc.next();

            // 계산기 종료 로직
            if (endApp.equals("no")) {
                System.out.println("계산기를 종료합니다.");
                break;
            }
            
 // while문 끝나고 과제용 첫번째 값 제거 로직
 // 컬렉션 첫번째 값 제거하기
        try {
            calculator.removeResultList();
            System.out.println(calculator.getResultList().toString());
        } catch (IndexOutOfBoundsException e) {
            System.out.println("예외 : " + e.getMessage());
        }

Calculator에서 throw new 예외를 던지고, App.java에서 try-catch문으로 예외처리하고, e.getMessage()로 예외오류문 출력

아직 예외에 대해서는 익숙하지 않아서 여러 방법이 있던데 공부를 더 해야 할 듯 하다.

 


아직 구현하지 못한 부분

- 다른 분의 코드를 보다가, 정수 입력 받는 부분에서 parser로 정수가 아니거나, 정수로 변환해주는 로직이 있었는데 고려를 못했다.

- setter나 remove등 활용법에서 약간 떠올랐지만, 활용하지 못한 부분.

- getter로 연산 결과 전체 뿌려주기 

- Level3 파트, Enum과 제네릭, 람다 스트림 까지 해보고 싶었지만, 못했다.

 

** 아쉬웠던 점

- 핑계지만 예비군으로 3일을 빠지게 되어서 절대적인 시간이 남들보다 부족해서 튜터님에게 조언을 구해서, 선 과제를 하고 모르는 부분은 강의를 듣는 형식으로 진행을 하였다.

- 그와 동시에 건강도 복합적으로 안좋은 상태가 지속이 되어서, Level2까지는 금방 할 줄 알았는데 집중하기도 힘들었고 생각보다 처음 설계하는 부분이 조금 어렵게 느껴졌다. -> 하고보니 그렇게 어렵진 않았던..

- Level3까지 도전하고 싶었는데 시간과 몸상태로 못한게 너무 아쉽다.