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

📝 아이템 17: 변경 가능성을 최소화하라

🔹 요약

✅ 불변 클래스는 인스턴스의 내부 값을 수정할 수 없는 클래스 ✅ 불변 클래스는 가변 클래스보다 설계, 구현, 사용이 쉬우며 오류가 생길 여지가 적음 ✅ 불변 객체는 스레드 안전하여 동기화할 필요가 없음 ✅ 불변 클래스를 만드는 5가지 핵심 규칙:

  • 객체의 상태를 변경하는 메소드(변경자)를 제공하지 않기

  • 클래스를 확장할 수 없도록 하기 (final 클래스로 선언)

  • 모든 필드를 final로 선언하기

  • 모든 필드를 private으로 선언하기

  • 자신 외에는 내부 가변 컴포넌트에 접근할 수 없도록 하기

🔹 주의사항

📌 불변 객체는 값이 다르면 반드시 독립된 객체로 생성해야 함 📌 성능을 위해 다단계 연산을 제공하는 가변 동반 클래스 고려 (예: StringBuilder) 📌 모든 클래스를 불변으로 만들 수는 없으나, 변경할 수 있는 부분을 최소화하는 것이 좋음


📚 추가 개념

💡 불변 객체의 장점

불변 객체
  • 단순함: 생성된 시점의 상태를 파괴될 때까지 그대로 간직합니다.

  • 스레드 안전: 내부 상태가 절대 변하지 않기 때문에 여러 스레드가 동시에 사용해도 훼손되지 않습니다.

  • 안전한 공유: 불변 객체는 어떤 스레드든 신경 쓰지 않고 공유할 수 있습니다.

  • 방어적 복사 불필요: 복사해도 원본과 같으므로 방어적 복사가 필요 없습니다.

  • 실패 원자성 제공: 상태가 절대 변하지 않으므로 잠깐이라도 불일치 상태에 빠질 일이 없습니다.

🔑 불변 클래스 설계 방법

  • 클래스를 final로 선언하여 상속을 막습니다.

  • 모든 필드를 private final로 선언합니다.

  • 생성자나 정적 팩터리 메소드를 통해서만 객체를 생성할 수 있게 합니다.

  • 어떤 메서드도 객체의 상태를 변경할 수 없습니다.

💡 불변 객체와 정적 팩터리

  • 불변 클래스는 정적 팩터리 메소드를 통해 객체 캐싱 기능을 제공할 수 있습니다.

  • 예를 들어, Integer.valueOf(int) 메소드는 객체를 캐싱하여 메모리 사용량을 줄이고, 동일한 값에 대해 새로운 객체를 생성하지 않고 캐시된 객체를 반환합니다.

💡 함수형 프로그래밍

  • 불변 객체를 활용한 프로그래밍 패러다임으로, 피연산자에 함수를 적용해 그 결과를 반환하지만 피연산자 자체는 변경하지 않습니다.

  • 이러한 방식은 코드의 예측 가능성을 높이고 버그를 줄일 수 있습니다.

💡 불변 객체와 방어적 복사

  • 불변 객체는 내부 데이터가 변하지 않으므로 방어적 복사가 필요 없습니다.

  • 그러나 가변 객체를 포함하는 경우에는 생성자에서 받은 가변 객체를 방어적으로 복사해야 합니다.


🎯 중요한 점

🔹 불변 클래스는 값이 다르면 반드시 독립된 객체로 만들어야 함 🔹 불변 클래스에서는 모든 필드가 final이어야 함 (일부 JVM 최적화에도 도움) 🔹 getter가 있다고 해서 setter도 반드시 있어야 하는 것은 아님 🔹 성능이 중요한 경우 불변 클래스와 함께 가변 동반 클래스를 제공하는 것이 좋음


💡 코드 예제 및 설명

✅ 불변 클래스 예제

❌ 가변 클래스의 문제점

✅ 불변 클래스 - private 생성자 + 정적 팩터리 메서드


❗ 어려웠던 점

⚠️ 불변 객체가 포함하는 필드가 가변 객체인 경우 방어적 복사를 통해 보호해야 하는데 이 개념이 처음에는 이해하기 어려웠음

➡️ 불변 클래스에 가변 컴포넌트가 있다면, 해당 컴포넌트를 외부에서 수정할 수 없도록 방어적 복사(직접 반환하는 것이 아니라 복사본을 반환)를 해야 함을 이해함

⚠️ 불변 객체의 경우 함수형 프로그래밍 패러다임(불변성, 순수 함수)을 따름으로 필드 값마다 새 객체를 만들어야 해서 성능 저하의 우려가 있었음

➡️ 매번 새로운 객체를 생성하는 대신 내부적으로 캐싱 메커니즘을 사용하거나, 가변 동반 클래스(예: StringStringBuilder)를 제공하는 방식으로 개선할 수 있음을 배움


💭 느낀 점

💡 처음에는 객체를 불변으로 만드는 것이 제약이 많다고 생각했는데, 실제로는 코드의 안정성과 신뢰성을 크게 높여준다는 것을 알게 되었다.

💡 멀티스레드 환경에서 불변 객체를 사용하면 동기화에 대해 걱정할 필요가 없다는 점이 큰 장점이라고 느꼈다.

💡 객체 지향 설계에서 "가변성은 필요한 경우에만 제공하라" 는 원칙의 중요성을 깨달았다.

Last updated