리팩토링 18. 변수 쪼개기

    들어가기 전

    이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다. 


    리팩토링 18. 변수 쪼개기 (Split Variable)

    • 어떤 변수가 여러 번 재할당 되어도 적절한 경우
      • 반복문에서 순회하는데 사용하는 변수 또는 인덱스
      • 값을 축적시키는데 사용하는 변수 (result += 1...)
      • 이런 경우, 어떤 변수를 여러 번 재활용하는게 당연하다. 
    • 그밖에 재할당되는 변수가 있다면, 해당 변수는 여러 용도로 사용되는 것이다. 따라서 변수를 분리해야 더 이해하기 좋은 코드를 만들 수 있다.
      • 변수 하나 당 하나의 책임을 지도록 만든다
      • 상수를 활용하자. 

    어떤 변수가 여러 번 재할당 되어도 적절한 경우

    아래 코드에서 볼 수 있는 두 가지의 변수는 여러 번 재할당 되어도 괜찮은 경우다.

    • header : 값이 축적되는 경우. 비슷한 경우로는 result += 1이 있음. 
    • index : for문 내부에서 index를 표현하기 위해 사용되는 변수 

    이런 변수들은 재할당 되는 것이 자연스러워 허용해도 좋은 경우다. 그렇지만 이 외의 경우에는 변수를 재할당해서 사용한다면, '변수가 여러 용도로 사용되는 경우'로 의심할 수 있다. 이런 코드는 가독성이 떨어지기 때문에 재할당 되는 부분을 각각의 변수로 분리해서 명확한 이름을 주는 것이 더 좋다. 


    실습1

    Rectangle의 updateGeometry() 메서드를 살펴보자. temp라는 변수가 여러 번 할당된 것을 볼 수 있다. temp는 지름을 구할 때 한번, 너비를 구할 때 한번 사용되어 총 2번 사용되었다. 그러면 이 temp는 원래 역할을 잘 표현하고 있는 것일까? temp는 지름과 너비를 각각 표현하고 있다. 따라서 하나의 변수가 여러 의미를 가지기 위해서 재할당되었다.

    따라서 재할당되는 temp라는 변수를 분리해줘야한다. 

    public class Rectangle {
    
        private double perimeter;
        private double area;
    
        // temp 변수는 그 자체의 이름도 의미가 없으며, 여러 번 재할당 됨.
        public void updateGeometry(double height, double width) {
            double temp = 2 * (height + width);
            System.out.println("Perimeter: " + temp);
            perimeter = temp;
    
            temp = height * width;
            System.out.println("Area: " + temp);
            area = temp;
        }
    	...
    }

     

    여러 번 재할당 된 temp 변수를 perimeter / area 변수로 각각 분리해주었다. 

    // temp 변수는 그 자체의 이름도 의미가 없으며, 여러 번 재할당 됨.
    public void updateGeometry(double height, double width) {
        final double perimeter = 2 * (height + width);
        System.out.println("Perimeter: " + perimeter);
        this.perimeter = perimeter;
    
        final double area = height * width;
        System.out.println("Area: " + area);
        this.area = area;
    }

    실습2

    • Haggis의 distanceTravelled() 메서드를 살펴보자. 내부적으로 살펴보면, acc가 두 번 재할당 되어 사용되는데, 서로 다른 의미로 사용되었다. 따라서 이 acc라는 변수를 재할당하는 부분을 각각의 의미를 가지도록 변수로 분리해주자.
    • 변수 result도 재할당 되고 있다. 그렇지만 그대로 사용되어도 된다.  축적의 용도로 사용되고 있기 때문이다. 
    // Haggis.class
    // acc가 서로 다른 의미를 가지고 있는데, 여러 번 재할당 됨.
    // result는 축적의 의미를 가지고 있으므로 괜찮음.
    public double distanceTravelled(int time) {
        double result;
        double acc = primaryForce / mass;
        int primaryTime = Math.min(time, delay);
        result = 0.5 * acc * primaryTime * primaryTime;
    
        int secondaryTime = time - delay;
        if (secondaryTime > 0) {
            double primaryVelocity = acc * delay;
            acc = (primaryForce + secondaryForce) / mass;
            result += primaryVelocity * secondaryTime + 0.5 * acc * secondaryTime + secondaryTime;
        }
    
        return result;
    }

     

    acc가 재할당 되는 부분을 두 개의 변수로 쪼개어 PrimaryAcceleration, SecondaryAcceleration으로 분배되어서 사용된다. 

    public double distanceTravelled(int time) {
        double result;
        final double PrimaryAcceleration = primaryForce / mass;
        final int primaryTime = Math.min(time, delay);
        result = 0.5 * PrimaryAcceleration * primaryTime * primaryTime;
    
        int secondaryTime = time - delay;
        if (secondaryTime > 0) {
            final double primaryVelocity = PrimaryAcceleration * delay;
            final double secondaryAcceleration = (primaryForce + secondaryForce) / mass;
            result += primaryVelocity * secondaryTime + 0.5 * secondaryAcceleration * secondaryTime + secondaryTime;
        }
    
        return result;
    }

    실습3 

    Order 클래스의 discount()를 보자. 변수 inputValue는 두 가지의 의미로 사용된다. 처음 input 데이터가 있고, 리턴되는 값의 의미를 가지고 있다. 하나의 변수가 두 개의 책임을 가지고 있기 때문에 두 개의 변수로 나누어 주는 것이 좋다. 

    public class Order {
    
        // inputValue 변수는 입력값과 반환값을 동시에 의미함. (재할당됨)
        public double discount(double inputValue, int quantity) {
            if (inputValue > 50) inputValue = inputValue - 2;
            if (quantity > 100) inputValue = inputValue - 1;
            return inputValue;
        }
    }
    

    inputValue 자체는 그대로 사용하되, 반환값을 위해 result 변수를 하나 선언해서 재할당 되는 부분을 삭제시켰다.

    public class Order {
    
        // inputValue 변수는 입력값과 반환값을 동시에 의미함. (재할당됨)
        public double discount(double inputValue, int quantity) {
            double result = inputValue;
    
            if (inputValue > 50) result -= 2;
            if (quantity > 100) result -= 1;
    
            return result;
        }
    }

     


    FutureMore

    가변 인자를 다루고 있다. 가변 인자는 메서드 내부에서 의도치 않게 변수가 변할 수 있음을 의미하고, 이렇게 변한 변수가 다른 Scope까지 영향을 주는 것을 사이드 이펙트라고 한다. 이런 사이드 이펙트를 방지하기 위해서 메서드 내부에서 변수를 선언할 때 final 키워드를 명시적으로 사용해주는 것이 좋다.

    final 키워드가 명시적으로 사용되면, 이 값은 변하지 않는 값으로 이해를 하고 코드를 읽기 때문에 가독성이 더 좋다. 

    // final 키워드로 변수 선언.
    final double PrimaryAcceleration = primaryForce / mass;
    final int primaryTime = Math.min(time, delay);
    result = 0.5 * PrimaryAcceleration * primaryTime * primaryTime;

    댓글

    Designed by JB FACTORY