본문 바로가기
독서에서 한걸음

Change Reference to Value

by oncerun 2022. 12. 11.
반응형

참조를 값으로 변경하는 리팩터링 기법이다. 

 

참조를 값으로 변경하는 것이니까 이는 mutable 한 값을  immutable 하게 변경하겠다는 의미를 내포한다.

 

객체를 크게 두 가지 객체로 볼 수 있다.

 

참조 객체는 얼마든지 그 내부의 값이 변경될 수 있는 객체를 말한다.

 

값 객체는 객체의 필드의 값들을 보고 동등성을 판단하는 보통 불변 객체라고 하는 객체이다.

 

보통 사용 기준을 나눌 때 객체의 변경사항을 다른 코드에도 전파시키고 싶다면 보통 참조 객체를 사용하고

반면 값 객체인 경우 그러한 사이드 이펙트 없이 사용하고 싶다면 값 객체를 사용하게 할 수 있다. 

 

 

다음 코드는 값이 변경될 수 있는 참조 객체를 의미하는 코드이다.

 

public class TelephoneNumber {

    private String areaCode;

    private String number;

    public String areaCode() {
        return areaCode;
    }

    public void areaCode(String areaCode) {
        this.areaCode = areaCode;
    }

    public String number() {
        return number;
    }

    public void number(String number) {
        this.number = number;
    }
}

 

해당 필드의 값을 setter를 통해 값을 변경할 수 있다. 

 

이러한 객체를 참조로 가지고 있는 또 다른 클래스를 확인해보자.

public class Person {

    private TelephoneNumber officeTelephoneNumber;

    public String officeAreaCode() {
        return this.officeTelephoneNumber.areaCode();
    }

    public void officeAreaCode(String areaCode) {
        this.officeTelephoneNumber.areaCode(areaCode);
    }

    public String officeNumber() {
        return this.officeTelephoneNumber.number();
    }

    public void officeNumber(String number) {
        this.officeTelephoneNumber.number(number);
    }

}

 

Person 클래스는 TelephoneNumber를 참조하고 있다. 

 

그리고 메서드를 통해서 래퍼런스 객체의 값을 변경하고 있습니다.

 

만약 TelephoneNumber를 VO로 변경하고 싶다면 다음과 같이 리팩터링 할 수 있습니다.

 

public class TelephoneNumber {
    private final String areaCode;
    private final  String number;

    public TelephoneNumber(String areaCode, String number) {
        this.areaCode = areaCode;
        this.number = number;
    }

    public String areaCode() {
        return areaCode;
    }
    public String number() {
        return number;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TelephoneNumber that = (TelephoneNumber) o;
        return Objects.equals(areaCode, that.areaCode) && Objects.equals(number, that.number);
    }

    @Override
    public int hashCode() {
        return Objects.hash(areaCode, number);
    }
}

 

이 경우 반드시 equals와 hashCode를 생성해주어야 합니다. 

 

즉 VO의 데이터를 기준으로 판단할 수 있도록 해야 합니다.  동등성이라고도 합니다.

 

실제 클라이언트에서는 이제 새로운 VO 객체를 만들도록 변경해주어야 합니다.

public class Person {

    private TelephoneNumber officeTelephoneNumber;

    public String officeAreaCode() {
        return this.officeTelephoneNumber.areaCode();
    }

    public void officeAreaCode(String areaCode) {
        this.officeTelephoneNumber = new TelephoneNumber(areaCode, this.officeNumber());
    }
    public String officeNumber() {
        return this.officeTelephoneNumber.number();
    }

    public void officeNumber(String number) {
        this.officeTelephoneNumber = new TelephoneNumber(this.officeAreaCode(), number);
    }

}

 

그리고 사실 자바 14 버전 이상으로는 record로 간단하게 표현할 수 있다.

public record TelephoneNumber(String areaCode, String number) {
}
@Test
public void testEquals() throws Exception {
    TelephoneNumber telephoneNumber = new TelephoneNumber("123", "1234");
    TelephoneNumber telephoneNumber1 = new TelephoneNumber("123", "1234");
    
    Assertions.assertEquals(telephoneNumber1, telephoneNumber);
}

 

마지막으로 불변 객체를 사용하는 의도는 다양할 수 있지만, 가장 흔한 이유는 안정성과 보안을 위해서입니다.

 

불변 객체는 생성된 후에는 값이 변하지 않는 객체를 말하며, 이는 코드의 오류를 줄이고 애플리케이션의 안정성을 높일 수 있습니다.

 

불변 객체는 일반적으로 여러 스레드가 동시에 접근해도 안전하게 사용할 수 있으며, 이는 병렬 애플리케이션을 개발할 때 유용합니다.

 

불변 객체는 일반적으로 캐시 하거나 공유하기에도 적합합니다.

 

추가적으로 JPA 엔티티 내부 @Embedded을 사용하는 필드도  값 객체로 사용하는 것이 좋습니다.

실제 참조 객체로 사용하다보면 의도치 않게 여러 엔티티에 공유되어 원치 않는 값 변경에 버그를 발생할 수 있기 때문인데요.

 

이를 불변객체로 만들지 않고 코드 레벨에서 강제로 막을 수 있는 부분은 없다고 봐도 무방할 것 같습니다. 

 

 

반응형

'독서에서 한걸음' 카테고리의 다른 글

Shotgun Surgery  (0) 2022.12.18
Divergent Change  (0) 2022.12.14
Replace Derived Variable with Query, Combine Functions into Transform  (0) 2022.12.11
Separate Query from Modifier && Remove Setting Method  (0) 2022.12.10
Split Variable  (0) 2022.12.05

댓글