카테고리 없음

불변객체

casylm 2024. 7. 16. 22:32

불변객체란?

  • 불변객체란 상태값을 수정할 수 없는 객체를 의미한다.
  • 즉, 객체를 처음 생성(초기화) 후에는 객체가 가지는 상태를 변경할 수 없는 것을 의미한다.

불변객체를 만드는 방법

1) 캡슐화를 통해 내부 변수 접근 불가

public class Immutable {
    String address = "대한민국 서울시";
    String gender = "female";
    String identityNumber = "9706192034623";

    public String getAddress() {
        return address;
    }

    public String getGender() {
        return gender;
    }

    public String getIdentityNumber() {
        return identityNumber;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setIdentityNumber(String identityNumber) {
        this.identityNumber = identityNumber;
    }
}

다음은 개인 정보를 가진 class 이다. 주소, 성별, 주민등록번호를 가지고 있으며 외부에서는 getter(), setter() 를 통해 데이터를 조회, 수정 할 수 있다. 여기서 문제는 setter()를 통해 변경하면 안되는 값이 변경 될 가능성이 있다는 것이다.

 

public class Immutable {
    private String address = "대한민국 서울시";
    private String gender = "female";
    private String identityNumber = "9706192034623";

    public String getAddress() {
        return address;
    }

    public String getGender() {
        return gender;
    }

    public String getIdentityNumber() {
        return identityNumber;
    }
}

1. 내부 인스턴스 변수는 getter() 메소드를 통해서만 접근 가능하도록 private으로 변경하였다.

2. setter() 메소드를 삭제하여 외부에서 값을 변경 할 수 없도록 하였다.

 

캡슐화를 통해 공개된 인터페이스를 통해서만 접근 할 수 있으며, Getter만 열려있는 상태이기에 수정자를 통한 멤버 수정이나, 직접 객체의 멤버에 접근해서 수정할 수 있는 방법은 막혔다.

 

다음과 같은 변경은 객체의 불변성을 보장할까?

캡슐화를 통해 보장된 불변성은 상속을 사용하는 순간 깨진다.

 

2) final을 통해 객체의 값 변경을 막자

 

다음은 Immutale 클래스를 상속받은 extendsImmutable 클래스이다. 

public class extendsImmutable extends Immutable{
    private String address;
    private String gender;
    private String identityNumber;

    public extendsImmutable(String address,String gender,String identityNumber){
        super();
        this.address = address;
        this.gender = gender;
        this.identityNumber = identityNumber;
    }

    @Override
    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String getIdentityNumber() {
        return identityNumber;
    }

    public void setIdentityNumber(String identityNumber) {
        this.identityNumber = identityNumber;
    }

    @Override
    public String toString() {
        return "extendsImmutable{" +
                "address='" + address + '\'' +
                '}';
    }
}

 

1) extendsImmutable은 상속을 받으며 setter를 추가할 수 있고, extendsImmutable Instance는 thread safety를 보장받을 수 없다.

2) Immutable에서는 정상적이던 동작도 extendsImmutable가 메소드 오버라이딩을 통해 오작동하게 만들 수 있다.

  • 다형성을 통해서 Immutable 객체를 제공 받는다면 이 객체가 Immutable객체인지, 상속을 받은 신뢰할수 없는 객체인지 알기 어렵다.
  • final class로 선언하여 상속을 하지 못하게 함으로써 해결 가능하다.
public final class Immutable

 

또 다른 문제에 대하여 생각해보자. 만일, Immutable 클래스에 가족 정보를 나타내는 Family Reference Type을 멤버변수로 추가했다고 해보자. 다음과 같은 코드에서는, 외부에서 getFamily() 호출 시 객체 내부의 참조 값을 넘겨주게 된다. 그리고 객체 외부에서는 그 참조값을 통해서 객체 내부의 맴버를 변경할 수 있게 되어 객체의 불변성을 무너지게 한다.

public final class Immutable {
    private String address = "대한민국 서울시";
    private String gender = "female";
    private String identityNumber = "9706192034623";
    private Family family = new Family();

    public Family getFamily() {
        return family;
    }

 

3) 생성자나 상태 접근자를 호출 시 DeepCopy를 사용

이는, DeepCopy를 이용하여 해결 할 수 있다.

public Family getFamily() {
    return new Family();
}

다음과 같이 함수 호출 시마다, 새로운 객체를 생성, 반환하여 외부로부터의 접근을 방지한다.

 

https://mangkyu.tistory.com/131

 

[Java] 불변 객체(Immutable Object) 및 final을 사용해야 하는 이유

클린코드를 읽어도, 이펙티브 자바를 읽어도, 개발을 잘하는 팀의 얘기를 들어도 항상 좋은 코드를 얘기할 때면 불변의 객체를 필연적으로 접하게 되는 것 같습니다. 그래서 이번에는 불변의 객

mangkyu.tistory.com