DEV ℧ Developer Diary

[Refactoring] 냄새 5. 전역 데이터

해당 포스트는 inflearn의 백기선님의 강의인 리팩토링 을 듣고 정리한 글입니다.

냄새 5. 전역 데이터

  • 전역 데이터 (예, 자바의 public static 변수)
  • 전역 데이터는 아무곳에서나 변경될 수 있다는 문제가 있다.
  • 어떤 코드로 인해 값이 바뀐 것인지 파악하기 어렵다.
  • 클래스 변수 (필드)도 비슷한 문제를 겪을 수 있다.
  • “변수 캡슐화하기 (Encapsulate Variable)”를 적용해서 접근을 제어하거나 어디서 사용하는지 파악하기 쉽게 만들 수 있다.
  • 파라켈수스의 격언, “약과 독의 차이를 결정하는 것은 사용량일 뿐이다.”

리팩토링 17. 변수 캡슐화하기

  • 메소드는 점진적으로 새로운 메소드로 변경할 수 있으나, 데이터는 한번에 모두 변경해야 한다.
  • 데이터 구조를 변경하는 작업을 그보다는 조금 더 수월한 메소드 구조 변경 작업으로 대체할 수 있다.
  • 데이터가 사용되는 범위가 클수록 캡슐화를 하는 것이 더 중요해진다.
    • 함수를 사용해서 값을 변경하면 보다 쉽게 검증 로직을 추가하거나 변경에 따르는 후속 작업을 추가하는 것이 편리하다.
  • 불변 데이터의 경우에는 이런 리팩토링을 적용할 필요가 없다.

예제 코드

  • Home
public class Home {
    public static void main(String[] args) {
        System.out.println(Thermostats.targetTemperature);
        Thermostats.targetTemperature = -1111600;
        Thermostats.fahrenheit = false;
    }
}
  • Thermostats
public class Thermostats {

    public static Integer targetTemperature = 70;

    public static Boolean heating = true;

    public static Boolean cooling = false;

    public static Boolean fahrenheit = true;
}

Home은 집의 온도를 설정한다. 간단한 로직이며,Thermostats은 온도에 대한 전역변수들이 모인 클래스이다.

Home클래스에서 Thermostats클래스의 targetTemperature 변수를 직접적으로 호출해서 값을 변경해주는데 이것은 정말 위험하다. 자칫 잘못하면, 예제코드에 나와있는 것처럼 -1111600같은 말이 안되는 값을 넣어도 해당 값이 적용되기 때문이다.

이러한 경우를 피하기 위해서는 메소드로 변수를 감싸서 validation을 적용하거나, final로 선언하여 불변하게 만들어야 한다.

메소드로 감싸는 대표적인 예시는 Getter/Setter이 있다. 모두가 잘 아는 예시라고 생각된다.

public class Thermostats {

    public static Integer targetTemperature = 70;

    public static Boolean heating = true;

    public static Boolean cooling = false;

    public static Boolean readInFahrenheit = true;

    public static Integer getTargetTemperature() {
        return targetTemperature;
    }

    public static void setTargetTemperature(Integer targetTemperature) {
        Thermostats.targetTemperature = targetTemperature;
    }

    public static Boolean getHeating() {
        return heating;
    }

    public static void setHeating(Boolean heating) {
        Thermostats.heating = heating;
    }

    public static Boolean getCooling() {
        return cooling;
    }

    public static void setCooling(Boolean cooling) {
        Thermostats.cooling = cooling;
    }

    public static Boolean getReadInFahrenheit() {
        return readInFahrenheit;
    }

    public static void setReadInFahrenheit(Boolean readInFahrenheit) {
        Thermostats.readInFahrenheit = readInFahrenheit;
    }
}

이렇게 Getter/Setter를 만들었다면, Home에서는 아래와 같이 전역변수들을 호출 및 변경할 수 있을 것이다.

public class Home {
    public static void main(String[] args) {
        System.out.println(Thermostats.getTargetTemperature());
        Thermostats.setTargetTemperature(68);
        Thermostats.setReadInFahrenheit(false);
    }
}

여기서 더 안전하게 간다면 각 메소드에 validation을 추가한다던지 또는 변수의 이름을 변경하는 대책을 추가할 수 있다.

public static void setTargetTemperature(Integer targetTemperature) {
    if(targetTemperature <= 100&&targetTemperature > -101) Thermostats.targetTemperature = targetTemperature;
}