IT/Programming / / 2023. 4. 27. 09:19

<Effective Java> RULE 39 필요하다면 방어적 복사본을 만들라.

반응형

 

자신이 만든 클래스의 클라이언트가 불변식을 망가뜨리기 위해 최선을 다할 것이라는 가정하에 방어적으로 프로그래밍을 해야한다.

 

불변식을 망가뜨리는 자

· 시스템 보안을 무너뜨리려는 악의적인 사용자

· 실수로 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
 

결론

· 객체의 컴포넌트로는 가능하다면 변경 불가능 객체를 사용해야한다.

· 변경 불가능 객체가 아닐 경우에 방어적 복사본을 사용하도록 한다.

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유