2. 계산기 구현

choizz156·2022년 8월 13일
0

TDD

목록 보기
5/6
post-thumbnail

계산기

  • 숫자는 2개만 입력한다.
  • 숫자와 연산자를 입력한다.
  • 숫자는 숫자만을 입력한다고 가정한다.
  • 기본적으로 소수 2번째 자리 까지 출력하고 , 소수 아래 자리가 0일때는 0을 생략한다.
  • 테스트 코드를 사용하고 최대한 극단적으로 리팩토링을 해본다.

테스트 주도 개발로 계산기를 구현해 보았다. 숫자 2개 만을 계산하는 간단한 계산기이다. 다음에는 숫자를 연속으로 계산하는 계산기를 구현해 볼 것이다.

힘들었던 점.

  • double형의 파라미터를 보내서 계산을 했다. 1.2, 1.434 등의 숫자는 상관이 없지만 2, 3 등의 int형이 double형으로 자동 변환되어 들어갔을 때,
    뒤에 3.0 소숫점 첫째 자리까지 나왔다. 이 소숫점을 없애는게 힘들었다. double형을 String으로 변환하고 format을 써서 뒤에 소숫점을 없애고 출력하는데 성공했다.
  • Scanner로 사용자가 직접 값을 입력할 때 어떻게 테스트 코드를 작성해야할 지 모르겠다. 이것은 검색해 보아도 이해가 잘 되지 않았다.
    그래서 사용자가 입력했다고 가정하고 테스트 코드를 작성하였다.
  • 어떤 부분을 테스트해야 하는지 아직 100퍼센트 확신이 들지 않았다. 조금더 많이 해봐야 될 것 같다.

새롭게 배운 것들.

String.format("%.2f", num1 - num2) = %.2f는 소수점 3째 자리에서 반올림해서 두 번째 자리까지 출력해 준다. %.0f는 첫 째자리에서 반올림 하고 소숫점은 출력되지 않는다.

Test Code

  • CalculatorTest 코드는 단순한 연산을 테스트하기 위해 만들었다.
package calculator;

import calculator.Calculator;
import calculator.CalculatorRunner;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class CalculatorTest {

    Calculator cal = new Calculator(); //공통으로 사용하는 객체를 메서드 밖으로 뺏다.

    @Test
    void 덧셈() {
        double result = cal.add(1, 3); //더하기
        assertThat(result).isEqualTo(4.0);
    }


    @Test
    void 뺄셈() {
        double result = cal.substract(2, 3); //빼기
        assertThat(result).isEqualTo(-1);
    }
    @Test
    void 곱셈() {
        double result = cal.multiply(2, 3); //곱하기
        assertThat(result).isEqualTo(6);
    }
    @Test
    void 나눗셈() {
        double result = cal. divide(4, 2); //나누기
        assertThat(result).isEqualTo(2);
    }
}
  • RunningTest 코드는 값을 입력받았을 때 의도한 숫자가 출력되는지를 테스트 했다.
package calculator;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Scanner;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class RunningTest {



    @Test
    void add실행() {

        String str = String.valueOf(Calculator.add(1, 2)); // int 값이 double 형으로 자동 변환되어 들어올 경우, add메서드에 의해 계산된 값을 문자열로 만든다.
        String result = String.format("%.0f", Double.parseDouble(str)); // 문자열로 만든 값에서 소숫점을 제외시킨다.
        Assertions.assertThat(result).isEqualTo("3");

        double result1 = Calculator.add(1.5, 0.469);//double형으로 들어온 경우, double형과 int형도 마찬가지
        assertThat(result1).isEqualTo(0.97);

        str = String.valueOf(Calculator.add(1.8, 1.2)); // double 형으로 들어왔지만 계산했을 때 소수 아래 자리가 0인 경우
        result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("3");
    }
    @Test
    void substract실행(){
        String str = String.valueOf(Calculator.substract(1, 2));
        String result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("-1");

        double result1 = Calculator.substract(1.5, 1.2);
        assertThat(result1).isEqualTo(0.3);

        str = String.valueOf(Calculator.substract(1.8, 0.8));
        result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("1");
    }

    @Test
    void multiply실행(){
        String str = String.valueOf(Calculator.multiply(1, 2));
        String result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("2");

        double result1 = Calculator.multiply(1.5, 1.254);
        assertThat(result1).isEqualTo(1.88);

        str = String.valueOf(Calculator.multiply(1.8, 0)); // 0을 곱했을 때 0이 나오는지 테스트
        result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("0");
    }

    @Test
    void devide실행(){
        String str = String.valueOf(Calculator.divide(4, 2));
        String result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("2");

        double result1 = Calculator.divide(1.5, 1.254);
        assertThat(result1).isEqualTo(1.20);

        str= String.valueOf(Calculator.divide(0, 1)); // 분자가 0일 때 0이 나오는지 테스트
        result = String.format("%.0f", Double.parseDouble(str));
        Assertions.assertThat(result).isEqualTo("0"); 
    }

    @Test
    @DisplayName("0으로 나눌때")
    void divide0(){ // 0으로 나눌 수 없기 때문에 0이 들어오면 멘트와 함께 랜덤 숫자가 나온다.
        double result1 = Calculator.divide(1.5, 0);
        assertThat(result1).isEqualTo(-1);
    }

}

Production Code

  • CalculatorRunner 클래스는 실제로 main메서드가 있어 기능이 실행되는 클래스이다. 최대한 극단적으로 리팩토링을 해보았다.
package calculator;

import java.util.Scanner;

public class CalculatorRunner {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        Comments.numberInputComment();//동일한 멘트가 출력되는 코드를 메서드로 출력시킨다.
        double first = scanner.nextDouble();

        System.out.print("연산 기호를 입력해 주세요 : "); //변하지 않을 코드이기 때문에 따로 리팩토링하지 않았다.
        String operator = scanner.next();

       operator = OperatorException.correctOperator(scanner, operator); // 연산 기호를 잘못 입력했을 때 다시 재입력하게하는 메서드이다.

        Comments.numberInputComment();
        double second = scanner.nextDouble();


        if (operator.equals("+")) Output.addResult(first, second);//실제 결과를 출력시키는 메서드이다. 최대한 else를 지양한다.
        if (operator.equals("-")) Output.substractResult(first, second);
        if (operator.equals("*")) Output.multiplyResult(first, second);
        if (operator.equals("/")) Output.divideResult(first, second);

    }

}
  • Calculator 클래스를 만들어 계산을 구현하는 메서드를 한 클래스에 모았다.
package calculator;



public class Calculator {
    public static double add(double num1, double num2) {
        String result = String.format("%.2f", num1 + num2); //소수 2번째 자리까지 출력
        return Double.parseDouble(result);
    }
    public static double substract(double num1, double num2) {
        String result = String.format("%.2f", num1 - num2);
        return Double.parseDouble(result);
    }

    public static double multiply(double num1, double num2) {
        String result = String.format("%.2f", num1 * num2);
        return Double.parseDouble(result);
    }

    public static double divide(double num1, double num2) { 
        if(num2 == 0){// 나누기기를 수행하는 메서드에서 0으로 나눌수 없다.
            System.out.println("0으로 나눌 수 없습니다.");
            return Math.random();//0이 인자로 들어왔을 때 난수를 출력한다.
        }

        String result = String.format("%.2f", num1 / num2);
        return Double.parseDouble(result);
    }
}
  • Comments 클래스는 굳이 만들지 않아도 되지만 극단적으로 리팩토링해 보았다.
package calculator;

public class Comments {
    public static void numberInputComment(){
        System.out.print("숫자를 입력해 주세요 : "); //중복으로 사용되는 코드를 메서드로 묶었다.
    }

}
  • OperatorException 클래스는 잘못된 연산기호를 입력했을 때 수행되는 메서드들을 모았다.
package calculator;

import java.util.Scanner;

public class OperatorException {
    public static String correctOperator(Scanner scanner, String operator) {// scanner를 사용해서 재입력을 받아야하므로 파라미터로 넣는다., 잘못된 연산자를 파라미터로 넣는다.
        
        while (!(isOperator(operator)) {//만약 사칙연산기호가 아니면 while문을 수행한다.
            System.out.println("올바른 연산 기호를 입력해 주세요(+, - , * ,/) : ");
            operator = scanner.next();
            
            if (isOperator(operator)) return operator; // 올바른 연산자가 재입력됐을때 while문을 빠져나온다.
        }
        
        return operator;// 제대로 입력이 됐다면 그 연산자를 리턴한다.
    }

    public static boolean isOperator(String operator) { // 올바른 코드를 입력했을 때 반복문을 빠져나올 메서드를 따로 만들었다. while문 안에 있으면 너무 복잡해 진다.
        if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/")) {
            return true;
        }
        return false;
    }
}
  • Output 클래스는 실제로 결과를 출력시키는 메서드들을 모았다.
package calculator;

public class Output {
    public static void divideResult(double first, double second) {
        String result = String.valueOf(Calculator.divide(first, second));
        if (result.contains(".0")) {
            System.out.println(String.format("%.0f", Double.parseDouble(result))); // return할 것이 없기 때문에 else를 쓸 수 밖에 없다.
        } else {
            System.out.println(Calculator.divide(first, second));
        }
    }

    public static void addResult(double first, double second) {
        String result = String.valueOf(Calculator.add(first, second));
        if (result.contains(".0")) {
            System.out.println(String.format("%.0f", Double.parseDouble(result)));
        } else {
            System.out.println(Calculator.add(first, second));
        }
    }

    public static void substractResult(double first, double second) {
        String result = String.valueOf(Calculator.substract(first, second));
        if (result.contains(".0")) {
            System.out.println(String.format("%.0f", Double.parseDouble(result)));
        } else {
            System.out.println(Calculator.substract(first, second));
        }
    }

    public static void multiplyResult(double first, double second) {
        String result = String.valueOf(Calculator.multiply(first, second));
        if (result.contains(".0")) {
            System.out.println(String.format("%.0f", Double.parseDouble(result)));
        } else {
            System.out.println(Calculator.multiply(first, second));
        }
    }
}
profile
조금씩 성장하는 개발자...!

0개의 댓글