[리팩토링] 악취 23 : 데이터 클래스

 

✍️ 악취 23 : 데이터 클래스

필드와 getter/setter만 가지는 클래스를 '데이터 클래스'라고 한다. 데이터 클래스의 존재는 코드의 위치가 적절하지 않다는 방증이기도 한데, 일반적으로 필드와 필드를 사용하는 메서드는 한 클래스에 모아서 최소한의 정보만 노출하며 제 기능을 제공해야 하기 때문이다. 다만 예외적으로 데이터 전달을 위한 용도라면 필드와 getter 만으로 클래스를 작성할 수 있다. 

 

public 필드를 가지고 있다면 '레코드 캡슐화하기'를 적용해 보고, 불필요하게 공개된 필드는 'getter 제거하기'를 변경되지 않아야 할 필드에는 'setter 제거하기'를 통해 외부로 공개되는 정보는 최소화하고 클래스가 제공하는 기능에만 집중하도록 유도할 수 있다. 파편화된 필드와 메서드를 한 클래스로 모으기 위해 '함수 추출하기' '함수 옮기기' 적용할 수 있다. 

 

여기 데이터 클래스 악취를 해결하기 위한 세 가지 리팩토링 기법이 있다.

1. "레코드 캡슐화하기" public 필드가 있다면

2. "getter/setter 제거하기" 불필요한 공개와 수정을 제거

3. "함수 추출하기/옮기기" 파편화된 필드와 메서드를 한 곳으로

 

본 포스팅에서 관심 있게 살펴볼 리팩토링은 public 필드를 제거하기 위한 '레코드 캡슐화하기'이다.

 

🍊 레코드 캡슐화하기

리팩토링의 저자 마틴 파울러는 다음과 같은 클래스를 '레코드'라고 정의했다. 

레코드란 public 필드로 구성된 클래스를 말한다.

 

외부의 누구나 필드를 직접 참조하고 동시에 수정 가능하다면 객체의 상태를 예측하기 어려워 문제가 발생할 여지가 많아진다. 이런 경우 캡슐화를 적용해서 허용된 메서드로만 값을 참조하고 수정하도록 제어한다면 객체의 상태를 보다 수월하게 관리하고 추적할 수 있다.

 

레코드 클래스

//before
public class Organization {

    public String name;

    public String country;

}

 

 getter/setter 메서드가 필드를 감추는 목적도 있지만 내부에 로직을 작성함으로써 추상화의 이점도 가져갈 수 있다.

//after
@Getter
@AllArgsConstructor
public class Organization {

    public String name;

    public String country;

    public void setCountry(String country) {
        if (country == null || country.isEmpty())
            throw new IllegalArgumentException();

        this.country = country.toLowerCase();
    }
}