2023. 11. 30. 00:05ㆍBook/이펙티브 자바
불변 클래스의 장점과 단점
장점
1. 함수형 프로그래밍에 적합하다.
// 코드 17-1 불변 복소수 클래스 (106-107쪽)
public final class Complex {
private final double re;
private final double im;
public static final Complex ZERO = new Complex(0, 0);
public static final Complex ONE = new Complex(1, 0);
public static final Complex I = new Complex(0, 1);
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public double realPart() { return re; }
public double imaginaryPart() { return im; }
public Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im);
}
// 코드 17-2 정적 팩터리(private 생성자와 함께 사용해야 한다.) (110-111쪽)
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
public Complex minus(Complex c) {
return new Complex(re - c.re, im - c.im);
}
public Complex times(Complex c) {
return new Complex(re * c.re - im * c.im,
re * c.im + im * c.re);
}
public Complex dividedBy(Complex c) {
double tmp = c.re * c.re + c.im * c.im;
return new Complex((re * c.re + im * c.im) / tmp,
(im * c.re - re * c.im) / tmp);
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Complex))
return false;
Complex c = (Complex) o;
// == 대신 compare를 사용하는 이유는 63쪽을 확인하라.
return Double.compare(c.re, re) == 0
&& Double.compare(c.im, im) == 0;
}
@Override public int hashCode() {
return 31 * Double.hashCode(re) + Double.hashCode(im);
}
@Override public String toString() {
return "(" + re + " + " + im + "i)";
}
}
이 클래스는 복소수를 표현한다.('a+bi' 로 표현 가능한 모든 수) 이 클래스의 사칙연산 메서드들은 자기 값을 수정하지 않고 새로운 Complex 인스턴스를 반환한다.
=> 이처럼 피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴을 함수현 프로 그래밍이라고 한다.
함수현 프로그래밍을 하면 코드의 불변 비울이 높아지는 장점이 있다.
2. 불변 객체는 단순하다
가변 객체는 에러 상황에 값이 변하면 안되는 경우가 있을 수 있고 프로그래밍을 하면서 다른 인스턴스에게 영향을 주지 않게 고민하는 등 복잡한 상황에 놓일 수 있지만 불변 객체는 영원히 불변이다.
3. 불변 객체는 스레드 안전하다
멀티 스레드 환경에서 어떤 스레드가 불변 객체를 참조하든 따로 동기화 할 필요가 없다. 항상 값이 같기 때문이다.
4. 불변 객체는 안심하고 공유할 수 있다.(public static final)
모든 인스턴스에서 상수값 처럼 불변 객체를 공유해도 안심할 수 있다.
public static final Complex ZERO = new Complex(0, 0);
public static final Complex ONE = new Complex(1, 0);
public static final Complex I = new Complex(0, 1);
더 나아가 자주 쓰이는 인스턴스를 캐싱하여 같은 인스턴스를 중복 생성하지 않게 해주는 정적 팩터리를 제공할 수 있다.(Item1)
5. 불변 객체 끼리는 내부 데이터를 공유할 수 있다.
앞에 했던 얘기랑 같은 연장선인데 새로운 불변 객체를 만들 때 다른 불변 객체와 같은 내부 데이터를 가진다고 한다면 생성할 때 그대로 다른 불변 객체의 내부 데이터를 공유해서 만들 수 있다. => 어차피 내부 데이터는 생성 이후 바뀌지 않기 때문이다.
6. 이점이 많다
불변 객체 구성요소들로 이뤄진 객체라면 구조가 아무리 복잡해도 불변식을 유지하기 수월하다. 맵이나 집합은 안에 담긴 값이 가변 객체라면 불변이 깨지는데 불변 객체라면 괜찮다.
7. 실패 원자성을 제공한다.
상태가 절대 변하지 않으니 실패하더라도 값은 절대 변하지 않는다.
단점 : 값이 바뀔 경우 새로운 인스턴스를 만들어야 한다.
=> 대안
1. 다단계 연산자 제공
-연산이 +, -, *, / 등 연산 할 때마다 새로운 인스턴스를 만들지 말고 자주 쓰일 것 같은 연산자들을 묶어서 한번에 제공한다.
2. 가변 동반 클래스 제공
- String과 StringBuilder 의 관계처럼 클라이언트가 원하는 복잡한 연산들을 예측해서 대신 연산을 도와줄 수 있다.
불변 클래스를 만들 때 고려할 것
상속을 막을 수 있는 다른 방법들
1. private 또는 package-private 생성자 + 정적 팩터리
=> 중첩 클래스나 패키지 외부에 상속을 막고 내부에는 상속을 할 수 있다. 그렇게 된다면(아래 내용과 이어짐)
2. 확장이 가능하다. 다수의 package-private 구현 클래스를 만들 수 있다.
=> 구현 클래스가 생긴다면 (아래 내용과 이어짐)
3. 정적 팩터리를 통해 여러 구현 클래스중 하나를 활용할 수 있는 유연성을 제공하고 객체 캐싱 기능으로 성능을 향상 시킬 수 있다.
=>(Item1)
4. 재정의가 가능한 클래스는 방어적인 복사를 사용해야 한다.(불변을 위해서)
5.모든 "외부에 공개하는" 필드가 final이어야 한다.(어쩔 수 없는 경우 완화)
- 계산 비용이 큰 값은 해당 값이 필요로 할 때(나중에) 계산하여 final이 아닌 필드에 캐시하여 쓸 수도 있다.(hashCode 같은 메서드)
'Book > 이펙티브 자바' 카테고리의 다른 글
Item19 - 상속을 고려해 설계하고 문서화하라. 그렇지 않다면 상속을 금지하라 (1) | 2023.12.02 |
---|---|
Item18 - 상속보다는 컴포지션을 사용하라 (2) | 2023.11.30 |
Item17 - 변경 가능성을 최소화 하라(1) (0) | 2023.11.29 |
Item16 - public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2023.11.28 |
Item15 - 클래스와 멤버의 접근 권한을 최소화하라 (2) | 2023.11.27 |