본문 바로가기

JAVA

Enum 및 예외처리를 적용한 계산기 만들기

기존 OperatorType

  • 연산자 클래스별로 연산자를 나타내는 char값만 존재함
public enum OperatorType {
    ADD('+'),
    SUBTRACT('-'),
    MULTIPLY('*'),
    DIVIDE('/');
    private final char operator;
    public char getOperator(){
        return operator;
    }
    OperatorType(char operator) {
        this.operator = operator;
    }
}
public class ArithmeticCalculator {
    private OperatorType addOperator = OperatorType.ADD;
    private OperatorType subtractOperator = OperatorType.SUBTRACT;
    private OperatorType divideOperator = OperatorType.DIVIDE;
    private OperatorType multiplyOperator = OperatorType.MULTIPLY;
    private ArrayList<Double> results = new ArrayList<>();
    public <T extends Number> Double calculate(T number1, T number2, char operator){
        double answer = 0.0;
        //연산 수행
        if (operator == addOperator.getOperator()) {
            answer = number1.doubleValue() + number2.doubleValue();
        } else if (operator == subtractOperator.getOperator()) {
            answer = number1.doubleValue() - number2.doubleValue();
        } else if (operator == multiplyOperator.getOperator()) {
            answer = number1.doubleValue() * number2.doubleValue();
        } else if (operator == divideOperator.getOperator()) {
            answer = number1.doubleValue() / number2.doubleValue();
        }
        results.add(answer);
        return answer;
    }
}

 

이런식으로 Enum 클래스에 연산자만 선언해서 계산기 클래스 내부에서 해당 연산자값을 비교해오는 방식으로 사용했었는데, 아무리 생각해봐도 뭔가 연산자만 비교하기에는 아쉬운 것 같다는 생각이 들었다. 연산자 char 값만 비교하기에는 너무 메모리상으로도 낭비같고 차라리 계산기 클래스 내부에서 static final로 상수를 선언하면 되지 않나 싶었다.

 

그래서 연산자 값 뿐만 아니라 차라리 메서드를 Enum 클래스에서 따로 구현해서 수행하는 편이 훨씬 의미가 있을 거라는 생각이 들었다. 나중에 더하기 로직을 내부적으로 +10 씩 더한다거나 등등 유지보수성과 명확성을 위해서는 훨씬 유용할 것 같다는 생각이 들어서 수정하게 되었다

 

수정된 OperatorType

  • calculate 메서드를 추상 메서드로 선언 후 각각의 연산자 타입에서 오버라이딩 하여 다르게 구현
package com.example.calculator3;

import com.example.common.exception.DivideByZeroException;

// 연산자 및 해당 연산자에 해당하는 연산메서드를 선언한 enum class
public enum OperatorType {
    PLUS("+") {
        @Override
        public <T extends Number> double calculate(T firstInput, T secondInput) {
            return firstInput.doubleValue() + secondInput.doubleValue();
        }
    },
    MINUS("-") {
        @Override
        public <T extends Number> double calculate(T firstInput, T secondInput) {
            return firstInput.doubleValue() - secondInput.doubleValue();
        }
    },
    MULTIPLY("*") {
        @Override
        public <T extends Number> double calculate(T firstInput, T secondInput) {
            return firstInput.doubleValue() * secondInput.doubleValue();
        }
    },
    DIVIDE("/") {
        @Override
        public <T extends Number> double calculate(T firstInput, T secondInput) throws DivideByZeroException {
            if (secondInput.doubleValue() == 0) {
                throw new DivideByZeroException();
            }
            return firstInput.doubleValue() / secondInput.doubleValue();
        }
    };

    private final String operator;

    OperatorType(String operator) {
        this.operator = operator;
    }

    //계산을 수행하는 메서드를 추상메서드로 선언하여 각각의 연산자 객체가 오버라이딩하여 구현
    public abstract <T extends Number> double calculate(T firstInput, T secondInput) throws DivideByZeroException;
}
public class ArithmeticCalculator {

    private List<Double> results = new ArrayList<>();

    public <T extends Number> Double calculate(T firstInput, T secondInput, String operator) throws DivideByZeroException {
        double answer = switch (operator) {
            case "+" -> OperatorType.PLUS.calculate(firstInput, secondInput);
            case "-" -> OperatorType.MINUS.calculate(firstInput, secondInput);
            case "*" -> OperatorType.MULTIPLY.calculate(firstInput, secondInput);
            case "/" -> OperatorType.DIVIDE.calculate(firstInput, secondInput);
            default -> throw new IllegalArgumentException("Invalid operator: " + operator);
        };

        results.add(answer);
        return answer;
    }
    }

 

예외처리 추가

  • 잘못 입력받은 연산자 값과 피연산자 값, 0으로 나눈 경우의 예외 처리 클래스를 생성

  • ArithmeticCalculator에 입력값 예외처리에 대한 내용을 넣기에는 ArithmeticCalculator의 책임이 커지는 것 같다고 생각했기에 Parser를 생성하여 Parser객체에서 입력값에 대한 예외처리에 대한 내용을 처리하도록 책임을 분리
  • ArithmeticCalculator에서는 연산 수행과 관련된 예외 처리만 수행하도록 함
/*
* 입력값을 검증하는 class, ArithmeticCalculator에 입력값 검증까지 포함하면 책임이 너무 커지기에 분리
*/
public class Parser {
    private static final String OPERATION_REG = "[+\\-*/]";
    private static final String NUMBER_REG = "^[0-9]*$";

    private String firstInput;
    private String secondInput;
    private String operator;

    private final ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculator();

    public ArithmeticCalculator getArithmeticCalculator() {
        return arithmeticCalculator;
    }

    //입력받은 피연산자가 숫자가 아닌 경우룰 처리하는 예외 처리 메서드
    public void validateInputNumbers(String firstInput, String secondInput) throws BadInputException {
        if (!Pattern.matches(NUMBER_REG, firstInput) || !Pattern.matches(NUMBER_REG, secondInput)) {
            throw new BadInputException("숫자타입");
        }
        setFirstInput(firstInput);
        setSecondInput(secondInput);
    }

    //입력받은 연산자가 사칙연산자에서 벗어난 경우룰 처리하는 예외 처리 메서드
    public void validateOperator(String operator) throws InvalidOperatorException{
        if (!Pattern.matches(OPERATION_REG, operator)) {
            throw new InvalidOperatorException();
        }
        setOperator(operator);
    }

    //필드로 가지고 있는 ArithmeticCalculator를 호출하여 연산을 수행
    public Double executeCalculator() throws DivideByZeroException {
        // 여기서 String 타입의 피연산자들을 실수형이나 정수형으로 변환하여 자유롭게 넣어 줄 수 있음, Integer도 가능
        Double firstNumber = Double.parseDouble(firstInput);
        Double secondNumber = Double.parseDouble(secondInput);
        return arithmeticCalculator.calculate(firstNumber, secondNumber, operator);
    }

    public void setFirstInput(String firstInput) {
        this.firstInput = firstInput;
    }

    public void setSecondInput(String secondInput) {
        this.secondInput = secondInput;
    }

    public void setOperator(String operator) {
        this.operator = operator;
    }

}
public class ArithmeticCalculator {

    private List<Double> results = new ArrayList<>();

    public <T extends Number> Double calculate(T firstInput, T secondInput, String operator) throws DivideByZeroException {
        double answer = switch (operator) {
            case "+" -> OperatorType.PLUS.calculate(firstInput, secondInput);
            case "-" -> OperatorType.MINUS.calculate(firstInput, secondInput);
            case "*" -> OperatorType.MULTIPLY.calculate(firstInput, secondInput);
            case "/" -> OperatorType.DIVIDE.calculate(firstInput, secondInput);
            default -> throw new IllegalArgumentException("Invalid operator: " + operator);
        };

        results.add(answer);
        return answer;
    }
    }