내일배움캠프/TIL

[Spring_4기 본캠프] 3주차 - 계산기 lv2 | Day 23

austindynasty 2024. 11. 19. 10:56

1. 계산기를 lv2로 업그레이드 하자!

계산기 lv1을 구현하고 나서 이어지는 lv2 구현하기. 이전에 구현했던 계산기에 몇 가지 기능을 추가하고 나면 lv2가 뚝딱-! 완성된다.

- 사칙 연산을 수행 후 결과값을 반환하는 메서드를 구현하세요.

- 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성하세요.

- 연산 수행 역할은 Calculator 클래스가 담당, 연산 결과는 Calculator 클래스의 연산 결과를 저장하는 필드에 저장

- App 클래스의 main 메서드에서 Calculator 클래스의 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)

- 간접 접근을 통해 필드에 접근하여 값을 가져오는 Getter메서드, 값을 제거할 Setter메서드 활용

 

<제한 사항>

- 연산 수행 역할을 하는 클래스와 연산 결과를 저장하는 클래스, 적어도 2개 이상의 클래스로 분리

- 사칙 연산 수행과 결과값 반환에 적절한 메서드 활용

- 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근할 수 없도록 캡슐화 해야함

- 컬렉션에서 값을 넣고 제거하는 메서드가 활용되어야 함

 

<구현 계획>

- CalculatorApp 클래스와 Calculator 클래스 2개로 클래스를 분리해 연산 수행과 결과 저장 클래스를 분리하자.

- lv1 구현 시 아쉬웠던 점, 개선점을 lv2에 보완해서 구현하도록 하자. (종료 조건)

- 컬렉션 타입은 LinkedList를 활용해 데이터의 추가/삭제가 용이하도록 하자. 

- 접근제어자를 활용해 캡슐화를 구현하자.

- LinkedList에서 값을 추가할 땐 add(Setter메서드), 조회할 땐 get(Getter메서드) 메서드를 활용하자.

 

↓ 계산기 main ( CalculatorApp ) 코드보기↓

더보기
package lv2;

import java.util.*;

public class CalculatorApp {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Calculator calculator = new Calculator();

        while (true) {
            System.out.println("계산을 시작하시겠습니까? Yes or No : ");
            String input = sc.next();

            if (input.equalsIgnoreCase("No")) {
                break;
            } else if (!input.equalsIgnoreCase("Yes")) {
                System.out.println("잘못된 입력입니다. Yes 혹은 No만 입력할 수 있습니다.");
                continue; 
            }
            System.out.print("숫자를 입력하세요 : ");
            int num1 = sc.nextInt();

            System.out.print("연산자를 입력하세요 : ");
            String operator = sc.next();

            System.out.print("숫자를 입력하세요 : ");
            int num2 = sc.nextInt();

            int result = calculator.calculate(num1, num2, operator); //calculate 함수 호출
            System.out.println(num1 + operator + num2 + " = " + result);
            System.out.println("계산 기록 : " + calculator.getHistory());//getHistory 메서드를 사용해 기록 조회
            sc.close();
        }
    }
}

-> Calculator 클래스를 기반으로 객체를 생성해서 CalculatorApp클래스에서 해당 객체를 참조할 수 있게 했다. 그럼 이제 연산 수행과 결과 저장을 Calculator 클래스로 따로 빼주면 된다. 여기서 클래스를 나누고 객체를 생성하면 좋은 점을 알 수 있다. Calculator클래스에서 객체를 생성하면 해당 클래스가 가진 계산 기능을 다른 클래스에서도 이용할 수 있다. 클래스마다 계산 기능을 만들어서 사용하게되면 기능이 바뀔 때마다 모든 코드를 수정해줘야 하는데, 클래스로서 관리하게되면 유지보수가 쉬워진다. 

 

↓연산 수행, 결과 저장 Calculator 클래스 코드 보기↓

더보기
package lv2;

import java.util.LinkedList;

public class Calculator {
    private final LinkedList<Integer> linkedList = new LinkedList<>();

    public int calculate(int num1, int num2, String operator) {
        int result = 0;

        switch (operator) {
            case "-" -> result = num1 - num2;
            case "+" -> result = num1 + num2;
            case "*" -> result = num1 * num2;
            case "/" -> {
                if (num2 == 0) {
                    System.out.println("잘못된 입력입니다. 다시 입력해주세요.");
                    return result; 
                }
                result = num1 / num2;
            }
            default -> System.out.println("잘못된 연산자 입니다. 다시 입력해주세요.");
        }
        //setter 메서드 추가
            addResult(result);//계산 결과 linkedList에 저장
            return result;
        }
        //getter 메서드 추가
        public LinkedList<Integer> getHistory() { //계산 기록 조회
            return new LinkedList<>(linkedList); //getHistory 메서드에서 new 키워드로 새로운 LinkedList 인스턴스 생성(linkedList를 복사).메인에서 기록 조회만 가능하고 수정은 불가
        }
        private void addResult(int result){ //addResult 메서드에 접근하지 못하도록 private 접근제어자 사용
            if (linkedList.size() >= 5) {
                linkedList.pollFirst(); //결과 5개가 쌓이면 가장 먼저 들어간 데이터 삭제
            }
            linkedList.add(result);
        }
    }

-> Calculator 클래스에서 생성한 객체의 메서드를 calculate라고 하고, 두 숫자 사이의 연산을 진행하도록 했다. 또한 결과를 저장하고 삭제하는 기능은 private 접근제어자를 사용해 메서드에 접근할 수 없게 해 캡슐화를 구현했다. 

이제는 주석이 없으면 코드를 작성하기가 어렵다. 아무래도 주석으로 먼저 내가 구현하고자 하는 내용을 정리해놓고 코드를 작성하면 구상을 하기가 쉬워지고, 흐름을 이어가기가 쉬워서 그런 듯하다. 

 

lv1 계산기를 구현할 때 원래 연산부분 (operator) 코드를 작성할 때 switch가 아닌 if문으로 작성했었다.

그런데 위 그림처럼 인텔리제이가 if문을 switch로 바꿔보라고 제안을 해왔다. switch문으로 바꾸고나니 코드가 더 직관적이고 깔끔해졌다.

조건문은 대체적으로 쉬운 if문으로 작성하고 있었는데 switch문을 자주 써봐야겠다.

 

클래스에서 객체를 생성하고, 그 객체를 다른 클래스에서 사용할 수 있게 한다는 것은 모든 자동차의 규격에 맞는 핸들의 설계도를 만든 후 다른 자동차에서 그 핸들이 필요할 때마다 설계도를 가져와서 만들어 쓰는 것과 동일하다고 생각되었다(클래스는 설계도, 객체는 실제 핸들). 아직 클래스나 객체 생성에 대한 개념이 확실하게 박혀있지 않아서 쓸 때마다 내가 틀리지 않게 잘 쓰고 있는지 계속 확인해야만 했지만 익숙해지면 굉장히 편해질 것 같다.

 

< 개선한 점 >

- 사용자가 계산을 계속하거나 중단하는 것을 입력 초기에 선택권을 주어 진행/종료 명령을 받기 쉬워짐

- 계산 진행/종료 조건으로 입력해야하는 문자 yes/no 를 대소문자 구분없이 단어 자체를 인식할 수 있게 equlasIgnoreCase 메서드를 사용