- mutable 객체는 일시적으로 결합도가 높은 코드를 작성하게 한다.(temporal coupling)
- 아래의 코드는 의도한 값을 출력하기 위해서 코드 작성의 순서가 중요해진다. (bad)
Cash price = new Cash();
50줄 뒤
price.setDollars(29);
30줄 뒤
price.setCents(95);
25줄 뒤
System.err.print(price); // “$29.95”
- immutable 객체는 인스턴스화 및 초기화를 동시에 하기 때문에
- 코드 작성의 순서에 따른 값 변경에 걱정이 없다. (good)
Cash price = new Cash(29, 95);
- 객체를 수정하는 경우 새로운 객체를 반환해보자
class Cash { // 가변객체
private int dollars;
public void multiply(int factor) {
this.dollars *= factor;
}
}class Cash { // 불변객체
private int dollars;
public Cash multiply(int factor) {
return new Cash(this.dollars * factor);
}
}
- 불변객체를 이용하여 더욱 명확하게 나타낼 수 있다
- 아래 세가지 경우에서 가장 이해하기 수월할 것 같은 것은?
Cash five = new Cash(5);
five.mul(10);
System.err.print(five); // 50Cash five = new Cash(5);
Cash ten = five.mul(2);
System.err.print(ten); // 10Cash money = new Cash(5);
money.mul(2);
System.err.print(money); // 10
- mutability of identity
- 정체성의 가변성은 유지보수성 및 가독성을 떨어트린다
Map<Cash, String> map = new HashMap<>();
Cash five = new Cash(“$5”);
Cash ten = new Cash(“$10”);map.put(five, “five”);
map.put(ten, “ten”);five.mul(2); // ?!?!?
map.get(five) // “ten” or “five”- failure atomicity
- 실패 원자적으로(failure atomic) 만들면 호출된 함수가 실패하여도
해당 객체는 호출 전 상태가 유지되어 오류를 복구 할 수도 있다case 1) mutable class Cash {
private int dollars;
private int cents;
public void multiply(int factor) {
this.dollars *= factor; // modified already
if (error) {
throw new RuntimeException(“hooray!”); }
this.cents *= factor; // unreached
}case 2) mutable
class Cash {
private int dollars;
private int cents;
public void multiply(int factor) {
int before = this.dollars;
this.dollars *= factor; // modified already
if (error) {
this.dollars = before;
throw new RuntimeException(“hooray!”);
}
this.cents *= factor; // undone
}
}case 3) immutable
class Cash {
private int dollars;
private int cents;
public void multiply(int factor) {
if (error) {
throw new RuntimeException(“hooray!”);
}
return new Cash(this.dollars * factor, this.cents * factor);
}