Item 50 : 적시에 방어적 복사본을 만들라
2021. 8. 18. 09:44
- 우리는 클라이언트가 자바의 불변식을 깨트리려 혈안이 되어 있다고 가정하고 방어적으로 프로그래밍 해야한다.
- 어떤 객체는 그 객체의 허락 없이는 외부에서 내부를 수정하는 일이 불가능하게 해야한다.
예를 들어 기간을 표현하는 다음 클래스는 한번 값이 정해지면 변하지 않도록 할 생각이었다.
//불변식을 지키지 못한 클래스
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if(start.compareTo(end) > 0) {
throw new IllegalArgumentException(start + " 가 " + end + " 보다 늦다.");
}
this.start = start;
this.end = end;
}
public Date start() { return start; }
public Date end() { return end; }
// ... 생략
}
Date는 가변이기 때문에 불변식처럼 보이는 이 객체를 깨트릴 수 있다.
//Period 인스턴스를 향한 공격
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // p의 내부를 수정함
- Date 대신 불변인 Instant를 사용하면 해결 할 수 있다. (혹은 LocalDateTime 이나 ZoendDateTime)
- Date는 낡은 API이기 때문에 새로운 코드를 작성할 때는 더 이상 사용하면 안된다.
- 하지만 사용하게 된다면 생성자에서 받은 가변 매개변수 각각을 방어적으로 복사해 내부를 보호해야 한다.
public Perid(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0) {
throw new IllegalArgumentException
(this.start + " 가 " + this.end + " 보다 늦다.");
}
}
- 이렇게 만들면 앞선 코드(end.setYear) 는 위협이 되지 않는다.
- 매개변수의 유효성 검사( if문 )전에 방어적 복사본을 만들었고 이것으로 유효성 검사를 했기 때문이다.
- 검사시점/사용시점 공격 = TOCTOU을 막을 수 있다.
- 또한 매개변수가 제 3자에 의해 확장될 수 있는 타입이라면 방어적 복사본을 만들 때 clone을 사용하면 안된다.
//Period 인스턴스를 향한 공격
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
p.end().setYear(78); // 이렇게 변경이 또 된다.
- 접근자가 가변 필드의 방어적 복사본을 반환하면 막을 수 있다.
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(start.getTime());
}
- 매개변수를 방어적으로 복사하는 목적이 불변 객체를 만들기 위해서만은 아니다.
- 메소드는 생성자든 클라이언트가 제공한 객체의 참조를 내부의 자료구조에 보관해야 할 때면 항시 그 객체가 잠재적으로 변경될 수 있는지를 생각해야 한다.
- 방어적 복사에는 성능 저하가 따르고, 또 항상 쓸 수 있는 것도 아니다.
- 호출자가 컴포넌트 내부를 수정하지 않으리라 확신하면 생략 할 수 있다.
'책 > 이펙티브자바' 카테고리의 다른 글
Item 52 : 다중정의는 신중히 사용하라 (0) | 2021.08.20 |
---|---|
Item 51 : 메소드 시그니처를 신중히 설계하라 (0) | 2021.08.19 |
Item 49 : 매개변수가 유효한지 검사하라 (0) | 2021.08.17 |
Item 48 : 스트림 병렬화는 주의해서 적용하라 (0) | 2021.08.16 |
Item 47 : 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2021.08.16 |