Link
Today
Total
05-20 15:19
Archives
관리 메뉴

초보개발자 긍.응.성

(이펙티브 자바 3) 17. 변경 가능성을 최소화하라 본문

책 정리/이펙티브 자바 3

(이펙티브 자바 3) 17. 변경 가능성을 최소화하라

긍.응.성 2020. 10. 16. 00:07
반응형

불변 클래스의 장단점과 생성 방법을 알아보자.

불변 클래스

  • 불변 클래스는 인스턴스의 내부 값을 수정할 수 없는 클래스다. 이 정보는 객체가 파괴되는 순간까지 절대 달라지지 않는다.
  • 불변 클래스의 예시로는 String, 기본 타입의 박싱 된 클래스, BigInteger, BigDecimal이 존재한다.

불변 클래스로 만들기 위한 규칙

  1. 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
  2. 클래스를 확장할 수 없도록 한다.
  3. 모든 필드를 final로 선언한다.
  4. 모든 필드를 private로 선언한다.
  5. 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
public final class Complex { // final 클래스 - 확장 불가
    private final double re; // 필드를 private final
    private final double im;

    public Complex(double re, double im) { // 생성자로 초기화 - 지정자가 없으므로 생성이후 불변
        this.re = re;
        this.im = im;
    }

    public double realPart() {
        return this.re;
    }

    public double imaginaryPart() {
        return re;
    }

    public Complex plus(Complex c) {
        return new Complex(this.re + c.re, this.im + c.im); // 새로운 인스턴스를 반들어 반환
    }

    // minus

    public Complex times(Complex c) {
        return new Complex(this.re * c.re - this.im * c.im,
                            this.re * c.im + this.im * c.im);
    }

    // divideBy

    @Override
    public boolean equals(@Nullable Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof Complex)) {
            return false;
        }

        Complex c = (Complex) o;

        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);
    }
}

Complex는 피연산자에 함수를 적용할 경우 그것을 적용하여 결과를 반환하지만, 피연산자 자체는 그대로인 함수형 프로그래밍 패턴을 따른다.

불변 객체의 장점

  • 불변 객체는 단순하다.
    • 생성된 시점의 상태를 파괴될 때까지 그대로 간직하며 불변을 위한 추가적인 노력이 필요하지 않다.
  • 불변 객체는 근본적으로 Thread-safe하여 동기화할 필요가 없다.
    • 불변 객체에 대해서는 그 어떤 스레드도 다른 스레드에 영향을 줄 수 없다.
  • 불변 객체는 자유롭게 공유할 수 있음은 물론, 불변 객체끼리는 내부 데이터를 공유할 수 있다.
  • 객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다
  • 불변 객체는 그 자체로 실패 원자성을 제공한다
    • 실패원자성 (메서드에서 예외가 발생한 후에도 호출 전과 같은 상태여야 한다)

불변 객체의 단점과 대처법

  • 값이 다르면 반드시 독립된 객체로 만들어야 한다
    • 다단계 연산을 예측하여 기본 기능으로 제공하여 중간단계에 만들어지는 객체들이 생기지 않도록 한다

불변 클래스를 만드는 다른 설계 방법

상속을 막기위해 final 클래스로 선언하는 대신 모든 생성자를 private 혹은 package-private 하게 만들고 public 정적 팩토리를 제공한다.

public class Complex { // final 키워드 제거
    private final double re;
    private final double im;

    private Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public static Complex valueOf(double re, double im) { // 정적 팩터리 제공
        return new Complex(re, im);
    }

    // ... 
}

new 키워드 없이 만들어 낼 수 있으며, 클라이언트 입장에서 public이나 protected 생성자가 없으니 final 처럼 인식된다.

불변 클래와 캐시

불변 클래스는 상태가 변하지 않는다. 그리고 클라이언트의 시선에서는 이렇게 말할 수 있다.

어떤 메서드도 객체의 상태 중 외부에 비치는 값을 변경할 수 없다.

어떤 불변 클래스는 계산 비용이 큰 값을 나중에 계산하여 final이 아닌 필드에 캐시해놓기도 한다. 똑같은 값을 요청하면 캐시 해둔 값을 반환하여 계산 비용을 절감하는 것이다. 이는 불변 객체이므로 몇 번을 계산하여도 항상 같은 결과가 만들어짐이 보장되기 때문이다.

정리

클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다. 불변 객체는 많은 장점들을 제공한다. 단점이라고는 잠재적 성능 저하 뿐이다. 불변으로 만들지 못한다면 변경할 수 있는 부분을 최소한으로 줄이자.

반응형
Comments