유미의 기록들

[개인 과제 -Java] 계산기 프로그램 (Level 2) 본문

대외활동 기록/내일배움캠프

[개인 과제 -Java] 계산기 프로그램 (Level 2)

지유미 2024. 8. 1. 01:08
728x90
반응형

📌 요구사항

1. Calculator클래스 생성 

    - 양의 정수 2개와 연산 기호를 매개변수로 받아 사칙연산 기능을 수행한 후 결과 값을 반환하는 메서드

    - 연산 결과를 저장하는 컬렉션 타입 필드

    - 나눗셈에서 분모에 0이 들어오거나 연산자 기호가 잘못 들어온 경우 예외처리

2. main메서드에서 Calculator 클래스의 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)

    - Getter메서드, Setter메서드 구현

3. 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드 구현 -removeResults()

4. 저장된 연산 결과들을 조회하는 기능을 가진 메서드 구현 - inquiryResults()

5. Calculator 인스턴스 생성할 때 생성자를 통해 컬렉션 필드가 초기화

6. Calculator 클래스에 반지름을 매개변수로 전달받아 원의 넓이 계산하여 반환하는 메서드 구현

     - 원의 넓이 결과를 저장하는 컬렉션 타입의 필드 선언 및 생성

7. 사칙연산을 수행하는 ArithmeticCalculator클래스와 원과 관련된 연산을 수행하는 CircleCalculator클래스 2개 구현

     - Calculator 클래스를 상속 받는 ArithmeticCalculator, CircleCalculator 클래스를 구현

8. 사칙연산 각각을 담당하는 AddOperator, SubtractOperator, MultiplyOperator. DivideOperator 클래스 구현 (SRP)

9. ModOperator 클래스를 추가하였을 때 소스코드의 변경은 최소화하면서 기능을 쉽게 추가할 수 있는 방법 (OCP)

 

 

⚠️ 문제 해결 과정

예외처리 

num1을 0으로 나눈 경우와 연산자가 +,-,*,% 외의 다른 것이 입력되었을 때 어떻게 예외를 처리할 지 
오류(error) : 시스템 레벨, 환경적인 요인으로 회복이 불가능 한 경우
예외(exception) : 미리 예상할 수 있고 대응할 수 있는 회복이 가능한 경우

Java에서 예외가 발생하면 예외클래스로부터 객체를 생성하여 해당 인스턴스를 통해 예외처리를 한다

모든 예외의 상위클래스는 Exception클래스이다. 

 

1) 0으로 나눈 경우

java.lang.ArithmeticException : 산술 연산과정에서 오류가 있을 때 발생하는 예외, 정수는 0으로 나눌 수 없기 때문에 예외 발생

 

2) 잘못된 연산자를 입력했을 경우

java.lang.IllegalArgumentException : 잘못된 인수 값이 메소드에 전달 될 때 발생

try{ 
    Calculator calculator=new Calculator();
    result=calculator.calculate(num1,num2,operator);
    System.out.println("결과: "+result);
}catch (ArithmeticException e){ 
    System.out.println("0으로 나눌 수 없습니다");
    continue;
}catch(IllegalStateException e){
    System.out.println("연산자 형식이 맞지 않습니다");
    continue;
}

 

Exception 처리를 하지 않았을 때는 프로그램이 강제 종료가 되었는데 catch문에 Exception을 처리하고 난 후 강제 종료되지 않고, 계속 입력을 받을 수 있었다

 

 

GIt 에러

IntelliJ에서 git add . 명령어를 입력했을 때 다음과 같은 오류가 발생했다

찾아보니깐 ./gradlew을 실행할 수 있는 권한이 없어서 발생한 것이다

 ./gradlew clean build -x test

위 명령어 입력 후 chmod 명령어로 gradlew 파일에 실행 권한을 부여했다

chmod +x ./gradlew

해결완료!

 

 

깃허브 커밋 메시지 한글 깨짐 현상

깃허브에 push했을 때 커밋 한글 메시지가 깨짐 현상이 나타났다

git config --global -l

위 명령어를 입력하여 현재 세팅을 확인해보았는데, i18n.commitedcoding과 i18n.logoutputencoding의 설정 자체가 안되어 있었다

git config --global i18n.commitencoding "UTF-8"
git config --global i18n.logoutputencoding "UTF-8"

해결완료!

 

 

업캐스팅이 왜 안되지

자식클래스인 CircleCalculator와 ArithmeticCalculator의 객체가 부모클래스인 Calculator 타입으로 캐스팅하려고 선언하였는데 다음과 같은 오류가 발생했다
 //Calculator 인스턴스 생성
  Calculator circleCalculator=new CircleCalculator();
  Calculator arithmeticCalculator=new ArithmeticCalculator();
  ...
  
  result=arithmeticCalculator.calculate(num1,num2,operator); //에러 발생
public abstract class Calculator {
    protected ArrayList<Integer> list=new ArrayList<>();
    protected ArrayList<Double> circleAreaList=new ArrayList<>();
    //생성자 구현
    public Calculator(){
        list.clear(); // 필드 초기화
        circleAreaList.clear();
    }
}

그 이유는 업캐스팅한 레퍼런스 circleCalculator는 슈퍼클래스인 Calculator의 멤버만 접근 가능한데 calculate() 메소드는 ArithmeticCalculator클래스에 존재하기 때문이다

 

해결 방법으로 2가지가 있다

1) Calculator클래스에서 calculate()메소드를 선언해서 Override로 ArithmeticCalculator에서 구현

abstract class Calculator {
    abstract public double calculate();
}
public class ArithmeticCalculator extends Calculator{

...
    @Override
    public double calculate(){ 
        switch (operator){
            case '+' :
                AddOperator addOperator=new AddOperator();
                return addOperator.operate(num1,num2);
            ...
        }
    }
}

2) 다운캐스팅을 통해 접근

result=((ArithmeticCalculator)arithmeticCalculator).calculate(num1,num2,operator); //다운캐스팅

 

 

📝 궁금한 점

업캐스팅, 다운캐스팅은 왜하는 걸까

⇒ 공통적으로 할 수 있는 부분을 만들어 간단하게 다루기 위해

 

다운캐스팅

업캐스팅한 객체를 다시 자식클래스 타입으로 객체로 되돌리는데 목적 (복구)

메서드를 한번만 실행할 것이라 따로 변수에 저장해둘 필요성이 없다면, 다운캐스팅을 한줄로 표현 가능

  • 업캐스팅 된 객체에서 만일 자식 클래스에만 있는 메서드를 실행해야 하는 상황이 온다면, 다운 캐스팅을 통해 자식 클래스 타입으로 회귀 시킨 뒤 메서드를 실행하면 된다
  • 업캐스팅한 객체를 되돌리는데 적용된다 

 

 

추상클래스 vs 인터페이스

인터페이스

부모 자식 관계인 상속에 얽매이지 않고, 공통기능이 필요할 때마다 추상 메서드를 정의 해놓고 구현 → 추상클래스보다 자유롭게 붙였다 땟다 사용 가능

  • 클래스와 별도로 구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용하는 것에 초점
  • 인터페이스에 정의된 메서드를 각 클래스의 목적에 맞게 기능을 구현하는 느낌
  • 서로 관련성이 없는 클래스들을 묶어주고 싶을 때
  • 다중 상속을 통한 추상화 설계를 해야 할 때

추상클래스

  • 클래스 간의 연관 관계를 구축하는 것에 초점
  • 자신의 기능들을 하위 클래스로 확장시키는 느낌
  • 상속 받을 클래스들이 공통으로 가지는 메소드와 필드가 많아 중복 멤버 통합을 할때
  • public 이외의 접근자 선언이 필요할 때

 

 

더보기
package calculator;
import java.util.ArrayList;
import java.util.Scanner;

public class App{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        double result=0;
        //연산 결과 저장 배열 생성
        int n=10;
        int index=0;
        double results[]=new double[n];

        ArithmeticCalculator arithmeticCalculator=new ArithmeticCalculator();
        CircleCalculator circleCalculator=new CircleCalculator();
        while(true){
            System.out.print("사칙연산:1, 원의 넓이:2 >> ");
            int option=sc.nextInt();

            //사칙연산을 구하는 경우
            if(option==1){
                //1. 숫자입력
                System.out.print("첫 번째 숫자를 입력하세요: ");
                int num1=sc.nextInt();
                System.out.print("두 번째 숫자를 입력하세요: ");
                int num2=sc.nextInt();

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

                //3. 연산 후 결과값 출력 -> Calculator클래스의 calculate메소드 사용
                try{
                    arithmeticCalculator.setInput(num1,num2,operator);
                     result=arithmeticCalculator.calculate();
                    System.out.println("결과: "+result);
                }catch (ArithmeticException e){
                    System.out.println("0으로 나눌 수 없습니다");
                    continue;
                }catch(IllegalStateException e){
                    System.out.println("연산자 형식이 맞지 않습니다");
                    continue;
                }

                //6. 10개 초과하는 경우, 가장 먼저 저장된 결과 삭제, 새로운 연산 결과 저장
                if(index>n-1){
                    for(int i=1;i<index;i++) {
                        results[i -1] = results[i];
                    }
                    results[n-1]=result;
                }
                //5.연산 결과 10개 저장할 수 있는 배열 생성 및 결과 저장
                else{
                    results[index]=result;
                    index++;
                }

                //7. 연산 결과 고정되지 않고 무한히 저장 -> Calculator클래스의 Setter 메서드 사용
                arithmeticCalculator.setList(result);

                //remove입력 받으면 가장 먼저 저장된 결과 삭제 -> Calculator클래스의 removeResult()사용
                System.out.println("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (remove 입력 시 삭제)");
                String remove=sc.next();
                if(remove.equals("remove")){
                    arithmeticCalculator.removeResult();
                }

                //8. 저장된 연산 결과 전부 출력 -> Calculator클래스의 inquiryResult()사용
                System.out.println("저장된 연산결과를 조회하시겠습니까? (inquiry 입력 시 조회)");
                String inquiry=sc.next();
                if(inquiry.equals("inquiry")){
                    // 배열 출력
                    System.out.print("배열 : ");
                    for(double num:results){
                        System.out.print(num+" ");
                    }
                    System.out.println();
                    //ArrayList 출력
                    System.out.print("ArrayList: "+arithmeticCalculator.getList());
                    System.out.println();
                }

            //원의 넓이를 구하는 경우
            }else if(option==2){

                //반지름 입력
                System.out.print("반지름을 입력하세요: ");
                int radius=sc.nextInt();

                circleCalculator.setInput(radius);
                double circleArea=circleCalculator.calculate();
                //원의 넓이 저장
                circleCalculator.setList(circleArea);
                System.out.println("원의 넓이: "+circleCalculator.getList());

            }

            //4. 반복문 사용 (exit 입력 시 종료)
            System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
            String exit=sc.next();
            if(exit.equals("exit")) break;
        }
    }
}
package calculator;
import java.util.ArrayList;

//연산 수행 클래스
abstract class Calculator{
    abstract public double calculate();
    abstract public void setList(double result);
    abstract  public ArrayList<Double> getList();
}
package calculator;
import java.util.ArrayList;

public class ArithmeticCalculator extends Calculator{
    private int num1,num2;
    private char operator;
    private ArrayList<Double> list=new ArrayList<>();
    public void setInput(int num1,int num2,char operator){
        this.num1=num1;
        this.num2=num2;
        this.operator=operator;
    }
    //리스트 조회
    @Override
    public ArrayList<Double> getList(){
        return list;
    }
    @Override
    public void setList(double result){
        list.add(result);
    }

    //리스트 삭제
    public void removeResult(){
        list.remove(0);
    }
    //사칙 연산
    @Override
    public double calculate(){ //연산 메서드의 책임 분리
        /* 연산 결과를 저장하는 컬렉션 타입 필드 선언 및 생성 */
        switch (operator){
            case '+' :
                AddOperator addOperator=new AddOperator();
                return addOperator.operate(num1,num2);
            case '-':
                SubtractOperator subtractOperator=new SubtractOperator();
                return subtractOperator.operate(num1,num2);
            case '*':
                MultiplyOperator multiplyOperator=new MultiplyOperator();
                return multiplyOperator.operate(num1,num2);
            case '/':
                //num2이 0일때 예외처리
                if(num2==0){
                    throw new ArithmeticException();
                }
                DivideOperator divideOperator=new DivideOperator();
                return divideOperator.operate(num1,num2);

            case '%':
                ModOperator modOperator=new ModOperator();
                return modOperator.operate(num1,num2);
            default:
                throw new IllegalStateException();
        }
    }
}
package calculator;
import java.util.ArrayList;

public class CircleCalculator extends Calculator{
    private int radius;
    private  ArrayList<Double> list=new ArrayList<>();

    public void setInput(int radius){
        this.radius=radius;
    }
    @Override
    public double calculate(){
        final double PI=3.14; // final, 정적변수 더이상 수정이 불가능하기 때문에 사용
        return PI*radius*radius;
    }
    public void setList(double result) {
        list.add(result);
    }

    public ArrayList<Double> getList(){
        return list;
    }
}
package calculator;

public class AddOperator {
    public int operate(int num1,int num2){
        return num1+num2;
    }
}
728x90
반응형
Comments