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

[Effective Java] 가변성을 최소화 하자.

by 돼지왕 왕돼지 2016. 10. 24.
반응형

 [Effective Java] 가변성을 최소화 하자.


BigInteger, bitset, CLASS, clone, copy constructor, Effective JAVA, Final, final field, Functional, imperative, ObjectInputStream, objectinputstream.readunshared, objectoutpustream, objectoutpustream.wrtieunshared, pacakge, Private, procedural, Public, public static final, readobject, readresolve, readunshared, Serializable, static factory method, StringBuilder, Synchronization, [Effective Java] 가변성을 최소화 하자., 가변 컴포넌트, 가변 클래스, 가변성, 가변성 제한, 값 변경, 객체, 객체 상태, 구조, 구현, 규칙, 내부에서 사용, 다단계, 단점, 동기화, 메소드, 명령적, 반대, 반환, 방어 복사본, 별개, 보안, 복사 생성자, 불변 객체, 불변 클래스, 불변 클래스 규칙, 사용, 상속, 상수, 상태, 새로운 인스턴스, 생성, 생성 비용, 생성자, 설계, 성능 향상, 스레드, 악의적인 코드, 안전, 엄격, 에러, 연산, 외부 접근, 인스턴스, 자유로운 공유, 절차적, 접근자 메소드, 초기화, 최소화, 특정 상황, 패키지 전용 가변 클래스, 피연산자, 피연산자 변경, 함수, 함수적 방법


-

불변 클래스는 자신의 인스턴스가 갖는 값을 변경할 수 없는 클래스.


-
불변 클래스는, 가변 클래스에 비해 설계와 구현 및 사용이 더 쉽다.
에러 발생이 적으며 보안이나 사용 측면에서 더 안전하다.


-
불변 클래스를 만들 때는 다음 다섯 가지 규칙을 따르자.
1. 객체의 상태를 변경하는 그 어떤 메소드도 제공하지 않는다.
2. 상속을 할 수 없도록 하자. ( class 에 final 을 주어 막을 수 있다. )
3. 모든 필드를 final 로 지정한다.
4. 모든 필드를 private 으로 지정한다.
5. 가변 컴포넌트의 직접적인 외부 접근을 막자.
생성자와 접근자 메소드 및 readObject 메소드에서 해당 객체의 방어 복사본을 만들어 사용하도록 하자.


-
어떤 연산 명령이 내려지면 현재 인스턴스를 변경하지 않고 대신에 새로운 인스턴스를 생성하고 반환한다.
대부분의 불변 클래스에서는 이런 형태가 사용된다.
메소드에서 피연산자를 변경하지 않고 함수를 적용할 결과를 반환하기 때문에 함수적(functional) 방법이라고 한다.
절차적(procedural), 명령적(imperative) 방법은 반대되는 개념으로 메소드에서 피연산자에 대한 처리를 수행하여 피연산자의 값이 변경된다.


-
함수적 방법은 친숙하지 않으면 이상하게 보일 수 있지만, 많은 장점을 갖는 불변성을 유지할 수 있게 한다.
불변 객체는 단순하여 생성될 당시의 상태 하나만을 가질 수 있다.


-
불변 객체는 본질적으로 스레드에서 사용시 안전하므로 동기회(synchronization)이 필요 없다.
그래서 불변 객체는 자유롭게 공유될 수 있다.
또한 매번 인스턴스를 생성할 필요 없이 클라이언트가 기존 인스턴스를 재사용하도록 하기도 쉽다.
가장 쉬운 방법 중 하나는 public static final 상수로 제공하는 것.
또 public static 팩토리 메소드로도 사용 가능하다.


-
불변 객체는 자유로운 공유가 가능하므로, 방어 복사본을 만들 필요가 없다.
불변 클래스에서는 clone 메소드나 복사 생성자(copy constructor)를 둘 필요가 없고 두어서도 안 된다.


-
불변 객체는 객체의 공유는 물론이고 그 객체의 불변 내부 구조들도 공유할 수 있다.


-
불변 객체의 유일한 단점은 객체가 가질 수 있는 각 값마다 별개의 객체가 필요하다는 것이다.
여러 개의 객체를 생성하면 비용이 많이 들며, 객체가 크다면 특히 더 그렇다.


-
불변 클래스에 대해 클라이언트가 어떤 복잡한 다단계 연산을 원하는지 정확하게 알 수 있다면 BigInteger 처럼 패키지 전용의 가변 클래스를 내부에서 사용하는 방법이 좋다. 

그렇지 않다면 가변 클래스를 public 으로 하는 것이 가장 좋은 방법이다.
그 예가 StringBuilder( String 의 가변 ) 와 BitSet ( BigInteger 의 가변 )


-
불변성을 보장하려면 서브 클래스를 만들지 못하게 해야 한다.
일반적으로는 불변 클래스를 final 로 지정하면 그렇게 할 수 있지만 더 유연한 방법은
클래스의 모든 생성자를 private 이나 package 전용으로 지정하고, public static 팩토리 메소드를 추가하는 것.


-
불변 객체의 어떤 메소드에서도 자신의 상태를 변경해서는 안 되고 모든 필드는 final 이어야 한다고 하지만,
이런 규칙은 필요 이상으로 엄격하여 성능 향상을 위해 완화될 수 있다.
실제로는 외부에서 접근할 수 있는 변경 메소드만 없으면 된다.


-
불변 클래스에서 Serializable 인터페이스를 구현하면서 가변 객체를 참조하는 하나 이상의 필드를 갖고 있다면, 반드시 readObject 나 readResolve 메소드를 명시적으로 정의하거나
ObjectOutputStream.writeUnshared 와 ObjectInputStream.readUnshared 메소드를 사용해야 한다.
그렇지 않으면 악의적인 코드에서 불변 클래스의 인스턴스를 가변 객체로 생성할 수 있다.



Summary


인스턴스가 가변적이어야 할 타당한 이유가 없다면, 그 클래스는 불변 클래스가 되어야 한다.
불변 클래스는 많은 장점을 가지며, 유일한 단점은 특정 상황에서 성능 이슈가 있을 수 있다는 것.
성능 향상에 필요하다고 확신이 설 때에 한해 불변 클래스에서 사용할 public 가변 클래스를 만든다.

현실적으로 불변 클래스가 되기 어려운 클래스들이 있다.
그런 경우에는 가능한 가변성을 제한하자.
객체가 가질 수 있는 상태수를 줄이면 그 객체를 이해하기 쉬우며 에러 가능성도 줄어든다.

필드를 final 로 하지 않아야 하는 특별한 이유가 없는 한 모든 필드를 final 로 만든다.
생성자에서는 모든 불변 규칙이 확립되어 완벽하게 초기화된 객체를 생성해야 한다.
재 초기화 메소드도 제공하지 말아야 한다.





반응형

댓글