Effective Java 4강 ITEM17 변경 가능성을 최소화 하라

park geonwoo·2024년 10월 24일

Effective Java

목록 보기
18/56

Item 17 : 변경 가능성을 최소화 하라

Conclusion

불변 객체를 사용해라.
불변 객체가 연산이 될때마다 새로운 객체를 생성하므로 이부분만 주의하면 될듯.

In case

자바 표준 라이브러리의 String, Integer, Long 등의 타입은 불변 타입이다.
불변 객체는 단순하며, 유지보수하기에 가변 객체보다 쉽다.
여기서 잠깐 불변 객체에 대한 설명을 하자면 <<keynote파일>>

책의 저자는 불변 객체를 만들기 위한 5가지 규칙을 제시한다.

1. 객체의 상태를 변경하는 메서드를 제공하지 않는다. 자신의 상태(멤버 변수)를 변경하는 메서드를 제공하지 말라는 이야기다.

상태를 변경해야 할때는(타입의 리턴이 필요할땐) 새로운 객체를 만들어 되돌려 줘야한다.

2. 클래스를 확장할수 없도록 한다. 불변 객체를 상속할수 있게 만들면, 서브 타입이 불변함의 계약을 깨트릴수가 있다.

자세한건 scratch_32.java 참고

3. (모든) 멤버를 final로 선언한다. 멤버를 final로 선언함으로 내부 상태 변경을 막는다.

물론 final로 선언했다고 해서 해당 객체의 상태 변경을 완전히 막을수 있는것은 아니므로 주의 해야한다.
scratch_33.java 참고

4. 모든 멤버를 private으로 선언한다. 클라이언트가 멤버를 직접 사용하지 못하게 해야함.

final 선언만으로는 완벽하게 불변함을 보장할수 없음.

5. 자신 이외는 내부 가변 멤버의 참조를 얻을수 없도록 한다. 내부 가변 객체의 참조를 넘길경우 클라이언트가 직접 사용하게 되므로 불변함을 보장할수 없음.

scratch_33.java, scratch_34.java 참고

불변 객체를 사용해야 하는 이유

  • 스레드에 안전함
    가변 객체와 달리 스레드 작업 수행시 값이 바뀔 상황이 없으므로.
  • 불변 객체를 멤버로 사용할때, 따로 방어적 복사를 할 필요가 없다
    불변 객체를 넘겨줘도 값 객체처럼 동작할것이므로 안전함
  • 내부 상태가 변경되지 않으므로 hash에 의존하는 컬렉션 사용에 적합함
    hashcode 생성기준은 내부 상태에 의존할것이고 hashcode가 변경되지 않을것이므로.
    실제로, 가변 객체에는 이것과 관련해 '식별자 가변성'에 의한 버그가 쉽게 발생할수 있음
  • 불변객체는 캐시에 용이함
    내부 상태가 변하지 않으므로 여러개를 만들 이유도 없고, 이에 착안해서 캐시해두면 재사용하기 편하고, gc도 효율적이다
  • (스레드 세이프와 더불어) 실패 원자성을 가지는 메소드를 만들수 있다
    불변객체는 메소드 실행중에 예외가 발생해도 값이 바뀌지 않았으므로 메소드 실행 전과 같은 상태를 유지함

불변 객체 사용시 유의점

  • 가변 객체처럼 사용할때는 메모리 낭비가 발생할수 있음
    예를들면 String 사용시 반복되는 조작은 메모리 낭비가 발생하고 결과적으로 gc가 필요하게 되므로 애초에 변경이 필요 없는 객체를 만들던지, 컴패니언 클래스를 이용해 생성하라.
  • 불변 객체 생성시 전달받은 파라미터를 그대로 멤버로 삼는 경우 외부에서 참조값을 얻을수 있음
    scratch_34.java 참고

in my opinion

변경가능성을 최소화 하라는 제목만 보고 setter 혐오를 1스택 더 쌓을 기회라고 생각했으나,
사실 제목을 바꿔야한다고 생각한다. 불변 객체를 사용해라.

불변 객체는 (가변 객체보다는)단순하고, 단순한 코드가 이해하기 쉽고, 이해하기 쉬운 코드는 유지보수하기도 쉽다.
하드웨어의 가격은 점점 내려가고 있고, 마침내 유지보수성이 속도와 메모리보다 중요해진 시점이 왔다.

불변 객체의 조작을 통해 새로운 객체가 생성될때 오버헤드가 발생할수 있다는 단점을 제외하면, 불변 객체를 쓰지 않을 이유가 없다.

책에서는 알려주지 않았던 불변 객체의 장점을 몇가지 더 설명하자면,
시간적 결합 제거, 부수효과 제거, 완전한 객체 생성, 작아지는 클래스.
앞의 3개는 예제를 통해 스스로 판단하길 바라며,
작아지는 클래스에 대해 이야기하자면,
불변 객체는 일반적으로 가변 객체보다 작다.
왜냐면 생성자 안에서만 상태를 초기화 할수 있기 때문이다.
처음부터 생성자의 인자를 10개씩 받게끔 클래스를 만드는 사람은 없을것이며,
처음에는 작은 갯수의 인자를 가지는 생성자를 만들고, 클래스의 기능이 추가 될수록 인자를 추가하게 될것이다.
코드냄새를 알아챌수있는 개발자라면 생성자의 인자 갯수로 분명 문제를 인지하고 클래스를 더 잘게 쪼갤것이다.
가변 객체의 경우 setter에 해당하는 기능을 제공할것이므로 이런 문제인식에 어려움이 있을수 있다.

참고
https://github.com/SeolYoungKim/effective_java_study

0개의 댓글