본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] 필요하면 방어 복사본을 만들자.

by 돼지왕 왕돼지 2017. 1. 10.
반응형

 [Effective Java] 필요하면 방어 복사본을 만들자.


CLASS, clone, Date, date 가변 객체, defensive copy, Effective JAVA, Final, gettime, Handoff, Return, sub class, toctou, [Effective Java] 필요하면 방어 복사본을 만들자., 가급적 피하자, 가변, 가변 객체, 가변 필드, 검사시간, 겸험, 길이가 0, 내부 데이터 구조, 데이터 구조, 매개변수, 메소드, 무방, 문서화, 반환, 방어 복사, 방어 복사본, 방어복사, 방어복사본, 방어적, 배개 변수, 배열, 복사본, 불변, 불변 객체, 불변 규칙, 불변 클래스, 사용시간, 상호 신뢰, 생성자, 서브 클래스, 성능, 소유권 이양, 신뢰, 안전, 안전한 언어, 약속, 영향, 원본, 위험한 복사방법, 유효성 검사, 이슈, 자바, 접근자 메소드, 집단, 컴포넌트, 클라이언트, 클래스, 통제권 이양, 패키지, 프로그래머, 필요, 항상


-
자바는 꽤나 안전한 언어이지만, 우리 클래스의 클라이언트가 불변 규칙을 파괴하기 위해 최선을 다할 거라는 가정하에 방어적으로 프로그램을 작성해야 한다.


-
가변 객체인 매개 변수는 각각의 방어복사본(defensive copy)을 만들어서 생성자에 전달해야 한다.
그렇지 않으면 예상치 못한 여러 상황이 발생할 수 있다.


-
방어복사본은 매개 변수의 유효성 검사에 앞서 만들어야 하며, 유효성 검사는 원본이 아닌 복사본을 대상으로 해야 한다!!
( TOCTOU 공격 ( 검사시간/사용시간) 이슈 )


-
clone 은 위험한 복사방법이므로 가급적이면 다른 방법으로 복제하자.
final 이 아닌 Class 는 sub class 가 clone 을 상속하여 이상한 짓을 할지도 모르고, clone 자체가 안전하지도 않다.
즉, 신뢰할 수 없는 집단에서 서브 클래스를 만들 수 있는 그런 타입의 매개 변수에 대한 방어 복사본을 만들 때는 clone 메소드를 사용하지 않아야 한다.


-
접근자 메소드 역시 가변 필드를 return 할 경우 방어 복사본을 반환해야 한다.
생성자와는 달리 접근자 메소드에서는 방어 복사본을 만들기 위해 clone 메소드를 사용해도 무방하다.
하지만 역시나 clone 자체의 문제는 신뢰할 수 없기 때문에 가급적 피하자.


-
매개변수의 방어복사는 불변 클래스를 위한 것만이 아니다.
클라이언트가 제공하는 객체를 내부 데이터 구조로 넣는 메소드나 생성자를 작성할 때는 언제든 해당된다.
클라이언트가 제공하는 객체가 가변적이라면 내부 데이터 구조에 넣은 객체가 변경되어도 우리 클래스에 영향을 안 주는지 고려하자.
만일 영향이 있다면 원본 대신 그 객체이 방어 복사본을 만들어 데이터 구조에 넣어야 한다.


-
내부 컴포넌트를 클라이언트에게 반환하기 전에 방어 복사하는 것도 마찬가지이다.
불변이건 아니건 가변적일 수 있는 내부 컴포넌트의 객체 참조를 반환할 떄는 다시 한번 생각해야 한다.
길이가 0이 아닌 배열은 항상 가변적이라는 것도 명심해야 한다.
내부 배열은 클라이언트에게 반환하기 전에 항상 방어 복사본을 만들어야 한다.


-
우리 객체의 컴포넌트로는 될 수 있는대로 불변 객체를 사용해야 한다.
그럼 방어 복사에 대해 염려할 필요가 없다.
경험 많은 프로그래머는 내부적으로 시간을 나타낼 때 Date 객체 대신 Date.getTime() 을 주로 사용한다.
Date 가 가변 객체이기 때문이다.


-
방어 복사는 성능 면에서 불리하므로 항상 사용해야 하는 것은 아니다.


-
사용되는 클래스와 클라리언트가 서로 다른 패키지에 있다고 가변 매개 변수의 방어 복사본을 항상 만들어야 하는 것은 아니다.
자신이 호출될 때 매개 변수가 참조하는 객체의 통제권 이양(handoff)을 명시적으로 나타내는 메소드나 생성자가 있다.
그런 메소드를 호출할 때는 해당 객체를 더 이상 변경하지 않을 거라는 약속을 클라이언트가 하는 것이다.
소유권을 이양 받는 메소드나 생성자는 그런 내용을 명확히 문서화해야 한다.


-
클래스와 클라이언트간의 상호 신뢰가 있을때 또는 그 클래스의 불변 규칙이 손상을 입어도 클라이언트 외의 어느 것에도 해를 끼치지 않을 때에만 방어 복사 없이 생성자나 메소드에서 가변 객체를 받는 것이 좋다.



Summary


만일 클라이언트로부터 받거나 또는 반환하는 가변 컴포넌트를 갖는 클래스가 있다면, 그 클래스에서는 그런 컴포넌트를 반드시 방어 복사해야 한다.
만일 방어 복사 비용이 엄청나게 비싸면서 클라이언트가 그런 컴포넌트를 변경하지 않는다고 클래스에서 신뢰할 수 있다면,
방어 복사를 하지 않는 대신 영향을 받는 컴포넌트를 변경하지 않는 것이 클라이언트의 책임이라는 사실을 문서화해야 한다.





반응형

댓글2