불변객체
불변객체란?
- 불변객체란 상태값을 수정할 수 없는 객체를 의미한다.
- 즉, 객체를 처음 생성(초기화) 후에는 객체가 가지는 상태를 변경할 수 없는 것을 의미한다.
불변객체를 만드는 방법
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