Clean Code

Java 리팩토링 - 변수 캡슐화하기 (with. Getter/Setter)

1. 알고자 하는 것

자바를 통해 개발을 하면서 클래스 내 필드를 변경하거나 가져올 때 주로 Getter, Setter를 많이 사용한다.

Getter, Setter를 통해 필드를 캡슐화 함으로써 다양한 곳에서의 추적 불가능한 값의 변경을 막기 위해 사용하는 정도로만 이해하고 있었다면 이 내용을 통해 한 단계 더 깊게 알아보자.

 

  • 변수 캡슐화하기 (Encapsulate Variable)

 

 

 

2. 알게 된 것

  • 클래스 내 가지고 있는 변수는 말 그대로 상황에 따라 변경 되는 값이다.
  • 특정 조건에 따라서 필드값이 변경되어야 하는 등의 상황이 그 예시에 속한다.
  • 그 변수는 클래스 내 인스턴스 변수일 수도 있고, 클래스 변수일 수도 있다.
  • 이 때, 단순히 변수를 public으로 열어두고 모든 곳에서 변경이 쉽게 가능하게 된다면 문제가 발생한다.
  • validation이 필요한 변수에 대해서도 유효성이 검증되지 않은 값이 들어갈 수 있다.
  • 또한 변경점이 모든 곳에 열려있으므로 이러한 값이 어디서 변경되었는지 추적이 어렵다.
public class Thermostats {

    public static Integer targetTemperature = 70;

    public static Boolean heating = true;

    public static Boolean cooling = false;

    public static Boolean fahrenheit = true;
    
}

public class Home {

    public static void main(String[] args) {
        System.out.println(Thermostats.targetTemperature);
        Thermostats.targetTemperature = -1111600; // 들어오면 안되는 온도값이 들어감
        Thermostats.fahrenheit = false;
    }
}

 

  • 이러한 변수값의 변경을 대입이 아닌 메서드로 감싸게 된다면(캡슐화), 메서드 내에 유효성 검증 로직을 추가할 수도 있고 변경 후 후속작업(알림, 로그 등) 역시 추가할 수 있다.
  • 또한, 변수를 private으로 설정해 마음대로 변경이 불가능하게 하고 변경점을 단일 메서드로 둠으로써 추적이 쉬워진다.
  • 변수를 private으로 설정하고 Setter 사용을 권장하는 이유이기도 하다.
public class Thermostats {

    public static Integer targetTemperature = 70;

    public static Boolean heating = true;

    public static Boolean cooling = false;

    public static Boolean fahrenheit = true;

    public static void setTargetTemperature(Integer targetTemperature) {
        // validation 처리
        if (targetTemperature >= 100) throw new IllegalArgumentException("설정 불가능한 값입니다.");
        Thermostats.targetTemperature = targetTemperature;
        // 후속작업 - 로깅 처리
        System.out.println("targetTemperature를 " + targetTemperature + "로 변경하였습니다.");
    }

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

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

    public static void setFahrenheit(Boolean fahrenheit) {
        Thermostats.fahrenheit = fahrenheit;
    }
}

public class Home {

    public static void main(String[] args) {
        System.out.println(Thermostats.targetTemperature);
        Thermostats.setTargetTemperature(68);
        Thermostats.setFahrenheit(false);
    }
}

 

  • 불변 데이터(상수)의 경우에는 변경이 불가능하므로 해당 리팩토링을 적용할 필요는 없다.
  • (+) 개인적인 견해로는 setter의 경우 변경이 필요한 변수에 대해서만 열어두고, setXX라는 네이밍 대신 명확하게 해당 변수의 변경 목적을 서술할 수 있는 메서드명으로 변경 메서드를 만드는 것도 좋다고 생각한다.

 

 

3. 정리

  • 변수를 public으로 두고 모든 곳에서 접근 / 변경이 가능하게 된다면 변경을 추적하기 어렵고, 유효성 검사 등 들어오면 안되는 값이 들어올 수 있는 문제가 있다.
  • 또한 변경 시 공통적으로 수행해야 하는 후속 작업도 변경하는 곳에서 일일히 다 추가해주어야 한다.
  • 변수의 변경을 메서드로 감싸게 된다면(캡슐화), 단일 변경점을 두게 되어 변경의 추적이 쉬워지고, 메서드 내에 유효성 검사 / 공통 후속 작업(로깅, 알림)을 한 곳에서 설정할 수 있다.
  • 불변 데이터(상수)는 변경이 불가능 한 형태이므로 해당 리팩토링을 적용할 필요는 없다.

 

 


 

Reference

강의 - 코딩으로 학습하는 리팩토링 (백기선 강사님)