NMP-Study / EffectiveJava2018

Effective Java Study
9 stars 0 forks source link

아이템 76. 가능한 한 실패 원자적으로 만들라 #76

Closed madplay closed 5 years ago

staywithjoy commented 5 years ago

실패 원자적(failure-atomic)이란?

방법 1. 불변 객체(#17)로 설계한다.

방법 2. 로직을 수행하기에 앞서 매개변수의 유효성을 검사한다.

// if(size == 0)이 없어도 예외는 던져지긴 하지만, // 이후 로직에서 size의 값이 음수가 되기 때문에 다음번 호출에서 발생할 수 있는 ArrayIndexOutOfBoundsException은 상황에 어울리지 않음!


### 방법 3. 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치한다.
- 계산을 수행해보기 전에 인수의 유효성을 검사하기 어려울 때 사용할 수 있는 방법입니다.
- ```TreeMap.java```를 예시로 생각해봅시다.
> 참고(StackOverflow) : [Why does TreeSet throw a ClassCastException?](https://stackoverflow.com/questions/15943031/why-does-treeset-throw-a-classcastexception)
```java
public static void main(String[] args) {
    Map<FakeKey, String> s = new TreeMap<>();
    s.put(new FakeKey(), "1"); // ClassCastExcpetion 발생
    s.put(new FakeKey(), "2");
}
Exception in thread "main" java.lang.ClassCastException: org.nypark.study.effectivejava3.item76.FakeKey cannot be cast to java.base/java.lang.Comparable
    at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
    at java.base/java.util.TreeMap.put(TreeMap.java:536)
    at org.nypark.study.effectivejava3.item76.Item76Application.main(Item76Application.java:14)
// TreeMap.put
/**
 * Associates the specified value with the specified key in this map.
 * If the map previously contained a mapping for the key, the old
 * value is replaced.
 *
 * ...생략...
 *
 * @throws ClassCastException if the specified key cannot be compared
 *         with the keys currently in the map
 * ...생략...
 *
 */
public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
        compare(key, key); // type (and possibly null) check, 요기서 Exception 발생!

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    // 생략 ...
}
// TreeMap.compare
/**
 * Compares two keys using the correct comparison method for this TreeMap.
 */
@SuppressWarnings("unchecked")
final int compare(Object k1, Object k2) {
    return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
        : comparator.compare((K)k1, (K)k2);
}

방법 4. 객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체한다.

방법 5. 작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌린다.

실패 원자성은 무조건 지켜야 하나요?