아이템17. 변경 가능성을 최소화하라
2021-08-22 00:00:00
# Effective_Java
- 불변 클래스: 그 인스턴스의 내부 값을 수정할 수 없는 클래스다.
- 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며, 오류가 생길 여지도 적고 훨씬 안전하다.
클래스를 불변으로 만들기 위한 5가지 규칙
- 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
- 클래스를 확장할 수 없도록 한다.
- 모든 필드를 final로 선언한다.
- 모든 필드를 private으로 선언한다.
- 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
불변 클래스 예시
- Complex.java
- 사칙연산 메서드들이 인스턴스 자신을 수정하는 것이 아닌 새로운 Complex 인스턴스를 만들어내고 있음
- 함수형 프로그래밍: 피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴
불변 클래스의 특징
- 불변 객체는 단순하다.
- 불변 객체는 근본적으로 thread-safe 하여 따로 동기화할 필요가 없다.
- 불변 객체는 안심하고 공유할 수 있다.
- 아무리 복사해봐야 원본 객체이기에..!
- clone 메서드나 복사 생성자를 제공하지 않는 게 좋다. (String의 복사 생성자는 필요 없다.)
- 불변 객체끼리는 내부 데이터를 공유할 수 있다.
- 객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다!
- 불변 객체 그 자체로 실패 원자성을 제공한다. (메서드에서 예외가 발생한 후에도 그 객체는 여전히 유효한 상태여야 한다.)
불변 클래스의 단점 및 대처 방법
- 값이 다르면 반드시 독립된 객체로 만들어야 한다.
- 흔히 쓰일 다단계 연산들을 예측하여 기본 기능으로 제공하는 방법
- String과 String의 가변 동반 클래스 StringBuilder(StringBuffer도 있지..)
클래스가 불변임을 보장하기 위한 여러 방법들
- final 클래스로 선언하는 가장 쉬운 방법도 있다.
- 모든 생성자를 private 혹은 package-private으로 만들고 public 정적 팩터리를 제공하는 방법
- 다수의 구현 클래스를 활용한 유연성을 제공하고, 다음 release에서 객체 캐싱 기능을 추가해 성능을 끌어올릴 수도 있다.
1 | // Complex 클래스를 생성자 대신 정적 팩터리를 사용해 불변 클래스로 만들어보기 |
불변 클래스의 예외 조건?
- 성능을 위해 완화: “어떤 메서드도 객체의 상태 중 외부에 비치는 값을 변경할 수 없다.”
- 방식: final이 아닌 필드에 캐시해두기
- 이유: 똑같은 값을 다시 요청하면 캐시해둔 값을 반환하여 계산 비용을 절감할 수 있다.
정리
- getter가 있다고 무조건 setter를 만들지 말라
- 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
- 모든 클래스를 불변으로 만들 수도 없다. 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자
- 다른 합당한 이유가 없다면 모든 필드는 private final이어야 한다.
- 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.