반응형
자신이 만든 클래스의 클라이언트가 불변식을 망가뜨리기 위해 최선을 다할 것이라는 가정하에 방어적으로 프로그래밍을 해야한다.
불변식을 망가뜨리는 자
· 시스템 보안을 무너뜨리려는 악의적인 사용자
· 실수로 API를 이상하게 사용하는 프로그래머
//변경 불가능성이 보장되지 않는 변경 불가능 클래스(!)
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 + " after " + end);
}
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
public class Rule39 {
public static void main(String[] args) {
//Period 내부공격
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
// end.setYear(78);
System.out.println(p.start() +" start, "+p.end() +"end");
}
}
//Tue Mar 06 08:45:15 KST 2018 start
//Tue Mar 06 08:45:15 KST 2018 end
//주석 해제
//Tue Mar 06 08:45:29 KST 2018 start
//Mon Mar 06 08:45:29 KST 1978 end
public Period(Date start, Date end) {
//방어적 복사
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if(start.compareTo(end)>0) {
throw new IllegalArgumentException(start + " after " + end);
}
}
//Tue Mar 06 08:50:58 KST 2018 start
//Tue Mar 06 08:50:58 KST 2018 end
인자의 유효성을 검사하기 전에 (규칙38) 방어적 복사본을 만들었다는것에 유의.
유효성 검사는 복사본에 대해서만 시행한다.
Date를 복사 시 Clone을 사용하지 않았다.
· Date 클래스는 final이 아니므로 하위 클래스의 객체가 반환될 수 있다.
인자로 전달된 객체의 자료형이 제 3자가 계승할 수 있는 경우, 방어적 복사본을 만들 때 Clone을 사용하지 않도록 해야 한다.
하지만 위의 예시는 생성자의 인자의 공격은 막을 수 있으나 접근자를 이용한 공격은 막을 수 없다.
접근자를 호출하여 얻은 객체를 통해 Period객체 내부를 변경할 수 있다.
p.end().setYear(78);
System.out.println(p.start() +" start");
System.out.println(p.end() +" end");
//Tue Mar 06 08:56:58 KST 2018 start
//Mon Mar 06 08:56:58 KST 1978 end
이러한 공격을 막으려면 변경 가능 내부필드에 대한 방어적 복사본을 반환하도록 접근자를 수정해야한다.
//내부 필드에 방어적 복사본 생성
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
//Tue Mar 06 08:58:25 KST 2018 start
//Tue Mar 06 08:58:25 KST 2018 end
결론
· 객체의 컴포넌트로는 가능하다면 변경 불가능 객체를 사용해야한다.
· 변경 불가능 객체가 아닐 경우에 방어적 복사본을 사용하도록 한다.
반응형
'IT > Programming' 카테고리의 다른 글
<Effective Java> RULE 37 자료형을 정의할 때 표식 인터페이스를 사용하라 (0) | 2023.04.27 |
---|---|
<Effective Java> RULE 38 인자의 유효성을 검사하라 (0) | 2023.04.27 |
<Effective Java> RULE 40 메서드 시그너처는 신중하게 설계하라 (0) | 2023.04.27 |
<Effective Java> RULE 41 오버로딩할 때는 주의하라 (0) | 2023.04.26 |
<Effective Java> RULE 42 var args는 신중히 사용하라 (0) | 2023.04.26 |