값 타입과 불변 객체

PPakSSam·2022년 1월 17일
0
post-thumbnail

값 타입은 복잡한 객체 세상을 조금이라도 단순화하려고 만든 개념이다.
따라서 값 타입은 단순하고 안전하게 다룰 수 있어야 한다.


값 타입 공유 참조

Address address = new Address("city", "street", "10000");

Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member);

member member2 = new Member();
member2.setUsername("member2");
member2.setHomeAddress(address);
em.persist(member2);

// member1이 아닌 member 객체의 city만 바꾸고 싶어!
member.getHomeAddress().setCity("newCity");

// 그러나 member와 member2의 city가 모두 newCity로 바뀐다.
// 이런 버그는 찾아내기가 힘들다.
// 지금이야 바로 밑에 코드를 작성했지만 실제로는 한참떨어진 곳에 작성하므로

위의 코드를 보면 알 수 있듯이 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험하다. 위와 같은 예상치 못한 사이드 이펙트(부작용)가 발생할 수 있고, 디버깅하기도 힘들기 때문이다.

값 타입 깊은 복사

Address address = new Address("city", "street", "10000");

Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member);

// 깊은 복사
Address copyAddress = new Address(address.getCity(),
                                  address.getStreet(), 
                                  address.getZipcode());
                                  
Member member2 = new Member();
member2.setUsername("member2");
member2.setHomeAddress(copyAddress);
em.persist(member2);

// 이렇게 하면 member의 city만 변하게 된다.
member.getHomdAddress().setCity("newCity");

값 타입의 공유참조는 매우 위험하다.
대신 깊은 복사(참조값을 복사하는 것이 아닌 값을 복사하는 것)를 해서 사용하면 부작용을 없앨 수 있다.

객체 타입의 한계

1. 기본타입은 값을 복사한다.

int a = 10;
int b = a;
b = 4;

System.out.println(a); // 10
System.out.println(b); // 4

2. 객체 타입은 참조값을 복사한다.

Address a = new Address("old");
Address b = a; // 객체 타입은 참조값을 복사한다.
b.setCity("new");

System.out.println(a.getCity()); // new
System.out.println(b.getCity()); // new

  • 항상 깊은 복사를해서 사용하면 공유 참조로 인한 부작용은 막을 수 있다.
  • 문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본타입이 아니라 객체 타입이라는 것이다.
  • 객체타입이므로 깊은 복사를 강제할 수 없다. 즉 객체의 공유 참조는 피할 수가 없다!!

불변 객체

[Address]

@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Embeddable
public class Address {
    
    private String city;
    private String street;
    private String zipcode;
    
    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }
}

[JpaMain]

Address address = new Address("city", "street", "10000");

Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member);

Address newAddress = new Address("newCity", 
                                  address.getStreet(),
                                  address.getZipCode());
member.setHomeAddress(newAddress);

  • 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단하게 된다.
  • 값 타입은 불변 객체(immutable object)로 설계해야 한다.
  • 불변객체란 생성 시점 이후 절대 값을 변경할 수 없는 객체이다.
  • 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않는 것이 방법 중 하나이다.
  • 또한 수정자(Setter)를 private으로 두는 것도 방법이다.
  • 참고 : Integer, String은 자바가 제공하는 대표적인 불변객체이다.

불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있다.

profile
성장에 대한 경험을 공유하고픈 자발적 경험주의자

0개의 댓글