호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지하는 특성
불변 객체(아이템 17)로 설계하는 방법
- 불변 객체는 태생적으로 실패 원자적인 특성을 가진다.
- 이유 : 기존에 존재하는 불변 객체의 상태는 생성 시점에 고정돼서 불변
가변 객체의 메서드를 대상으로 하는 방법
- 작업 수행에 앞서 매개변수의 유효성을 검사하는 방법(아이템 49)
- 잠재적인 예외 가능성이 있는 것들을 객체의 내부 상태를 변경하기 전에 대부분 걸러낼 수 있다.
- 예제 - Stack.pop()
아래의 소스에서 '이 부분'을 지우더라도 스택 비면 여전히 예외를 던지지만
size의 값이 음수가 돼서 다음번 호출도 실패하게 만들고
추상화 수준이 상황에 어울리지 않는 ArrayIndexOutOfBOundsException을 던지게 된다.public Object pop() { if (size == 0) throw new EmptyStackException(); // 이 부분 Object result = elements[--sizel; elements[sizel = null; // 다 쓴 침조 해제 return result; }
- 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법
- 계산을 수행해보기 전에는 인수의 유효성을 검사해볼 수 없을 때 앞선 방식에다가 덧붙여 쓸 수 있다.
- 예제 - TreeMap
- 원소들을 어떤 기준으로 정렬하는 특징을 가짐.
- 해당 객체에 추가할 수 있는 원소 타입 : TreeMap의 기준에 따라 비교할 수 있는 타입
- 일치하지 않는 원소 추가 시, 원소 삽입 위치 탐색 도중, ClassCastException 던짐
객체의 임시 복사본에서의 작업이 성공하면 원래 객체와 교체하는 방법
- 데이터를 임시 자료구조에 저장해서 작업하는 게 더 빠를 때 적용하기 좋다.
- 예제
- 어떤 정렬 메서드에서 정렬 수행 전에 입력 리스트의 원소들을 배열로 옮겨담을 때 배열을 사용하면, 정렬 알고리즘의 반복문에서 원소들에 훨씬 빠르게 접근할 수 있다.
=> 성능을 높이고 입력 리스트는 변하지 않게 할 수 있다.
- 어떤 정렬 메서드에서 정렬 수행 전에 입력 리스트의 원소들을 배열로 옮겨담을 때 배열을 사용하면, 정렬 알고리즘의 반복문에서 원소들에 훨씬 빠르게 접근할 수 있다.
작업 도중 발생하는 실패를 가로채는 복구 코드를 작성해서 작업 전 상태로 되돌리는 방법
- 자주 쓰이진 않지만 주로 (디스크 기반의) 내구성(durability)를 보장해야 하는 자료구조에 쓰인다.
-
권장되는 특성이지만 항상 만족할 수 있는 특성은 아니다.
- 예제
두 스레드가 동기화 없이 같은 객체를 동시 수정한다면 그 객체의 일관성이 깨져서 ConcurrentModificationException을 발생된다.
그렇다 해도 그 객체가 여전히 쓸 수 있는 상태라고 가정해서는 안된다. 에러는 복구할 수 없으므로 AssertionError에 대해 실패원자적으로 만들려는 시도조차 ㅏㄹ 필요 없다..(??)
- 예제
-
항상 권장되는 특성이 아니다.
- 이유 - 비용이나 복잡도가 큰 연산도 존재
- 하지만 문제가 무엇인지 알고 나면 실패 원자성을 얻어걸릴 수 있다.
-
메서드 명세에 기술한 예외라면 예외가 발생한다 해도, 실패 원자성을 가져야한다.
그렇지 않다면 실패 시의 객체 상태를 API 설명에 명시해야 하지만 이는 이상적이고 현실적으론 API 문서 상 잘 지켜지지 안혹 있음.