Java/TDD

[TDD] 문자열 계산기 && 1차 리팩토링

Gamii 2024. 5. 28. 07:50
728x90

 

 

 

1. 1차 실습 코드(caclulator_v1)

package calculator_v1;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Scanner;

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


public class RealCalculator {

    public static Calculator calClass = new Calculator();

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

        int calResult = calculator(line);
        System.out.println(calResult);
    }

    public static int calculator(String text){
        int total = 0;
        String[] split_text = text.split(" ");

        if(split_text.length > 0){
            total = Integer.valueOf(split_text[0]);
            for(int i = 1; i < split_text.length; i++){
                int num2 = Integer.valueOf(split_text[i+1]);
                String str = split_text[i];
                total = calClass.cal(total, str, num2);

                i = i+1;
            }
        }

        return total;
    }
}

 

package calculator_v1;

public class Calculator {

    public int cal(int num1, String type, int num2){
        int result = 0;

        switch(type){
           case "+":
               result = plus(num1, num2);
               break;
           case "-":
               result = minus(num1, num2);
               break;
           case "*":
               result = multiply(num1, num2);
               break;
           case "/":
               result = divide(num1, num2);
               break;
       }
        return result;
    }

    public int plus(int i, int j){
        return i + j;
    }

    public int minus(int i, int j){
        return i - j;
    }

    public int multiply(int i, int j){
        return i * j;
    }

    public int divide(int i, int j){
        try{
            return i / j;
        }catch (Exception e){
            System.out.println(j + "로 나눌 수 없습니다.");
        }

        return 0;
    }
}

 

 

 

main 메서드를 이용해서 "2 + 3 * 4 / 2" 실행 결과인 10을 출력을 했지만, 테스트 코드를 활용하지 못해서 다시 되도림표로 돌아왔다. 

 

 

다른 블로그 글을 참고하면서 현재 코드에서 리팩토링 되어야 할 부분을 정리해 보았다.

 

 

 

[1차 리팩토링 할 부분]

  • Test코드와 실제 운영되는 코드 분리하기(src/main/java 패키지와 src/test/java 패키지 용도에 맞춰서 분리하기)
  • 예외 처리하기
    • 입력 값이 빈 값이거나 null일 경우 -> throw new IllegalArgumentException();
    • switch문 default 예외 처리 -> throw new IllegalArgumentException();
    • 숫자만 입력됐을 때 (예- 1 => return 1;) 
  • 변수 이름 변경
  • Validation (사용할 수 있는 연산 부호인지 확인)

 

 

 

 

 

2. 2차 실습 코드(caclulator_v2)

[수정된 부분]

복잡하게 i를 계산하는 로직이 없어졌다.

변수 이름을 직관적으로 변경하여 이해하기 쉬워졌다.

테스트 코드 패키지와 분리되어서 test를 작성하고 삭제하는 행위가 없어졌다. (더 깔끔해지기도 했고..)

 

 

 

[src/main/java/calculator_v2]

package calculator_v2;

import java.util.Scanner;


public class RealCalculator {

    public Calculator calClass = new Calculator();

    private String[] operationType = {"+", "-", "*", "/"};
    
    public Integer calculator(String formula){
        int result = 0;
        String[] split_text = formula.split(" ");

        if(split_text.length > 0){
            result = Integer.valueOf(split_text[0]);
            String operator = "";

            for(int i = 1; i < split_text.length; i++){
                String input_i = split_text[i+1];

                if(input_i.isEmpty()){
                    throw new IllegalArgumentException();
                }

                if(isPermittedOperator(input_i)){
                    operator = input_i;
                }else{
                    int num2 = Integer.valueOf(input_i);
                    result = calClass.cal(result, operator, num2);
                }
            }
        }

        return result;
    }

    private boolean isPermittedOperator(String input){

        for(String operator : operationType){
            if(input.equals(operator)) return true;
        }

        return false;
    }
}

 

 

package calculator_v2;

public class Calculator {

    public int cal(int num1, String type, int num2){
        int result = 0;

        switch(type){
           case "+":
               result = plus(num1, num2);
               break;
           case "-":
               result = minus(num1, num2);
               break;
           case "*":
               result = multiply(num1, num2);
               break;
           case "/":
               result = divide(num1, num2);
               break;
            default:
                throw new IllegalArgumentException();
       }
        return result;
    }

    public int plus(int i, int j){
        return i + j;
    }

    public int minus(int i, int j){
        return i - j;
    }

    public int multiply(int i, int j){
        return i * j;
    }

    public int divide(int i, int j){
        try{
            return i / j;
        }catch (Exception e){
            System.out.println(j + "로 나눌 수 없습니다.");
        }

        return 0;
    }
}

 

 

[src/test/java/study]

package study;

import calculator_v2.RealCalculator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CalculatorTest {
    public RealCalculator rCal;

    @BeforeEach
    public void setUp()  {
        rCal = new RealCalculator();
    }

    @Test
    public void formula_test()  {
        assertEquals(10, rCal.calculator("2 + 3 * 4 / 2"));
    }

    @DisplayName("단일 항일 경우")
    @Test
    public void formula_single()  {
        assertEquals(1, rCal.calculator("1"));
    }

}

 

 

 

 

 

[2차 리팩토링 할 부분]

  • 유틸리티 클래스와 객체 구분해서 사용하기
    • 객체 지향 관점에서 최대한 클래스로 묶을 수 있는 부분은 묶자. (switch 부분, 연산하는 부분, for문으로 split 하는 부분)
  • Case 별로 Test코드를 작성하기
    • 인수 1개가 null 일 경우 (예 : 2 + + 4)

 

 

 

 

 

 

 

 

 

[참고]

 

https://wikidocs.net/91126

 

1차 피드백

##### 클래스 분리 필요성 (1) ```{.java} public class StringCalculator { ``` > StringCalculator 내용을 한 클래스에 구…

wikidocs.net

 

 

 

 

 

'Java > TDD' 카테고리의 다른 글

[TDD] 문자열 계산기 && 2차 리팩토링  (0) 2024.06.17