불변 클래스에 대한 설명이다.

일단 먼저 클래스를 불변으로 만들기 위한 규칙은 다음과 같다.

  • 객체의 상태를 변경하는 메소드(변경자)를 제공하지 않는다.
  • 클래스를 확장할 수 없도록 한다.
    • 하위 클래스에서 부주의하게 혹은 나쁜 의도로 객체의 상태를 변하게 만드는 사태를 막아준다.
    • 상속을 막는 대표적인 방법은 클래스를 final로 선언하는 것이지만, 다른 방법도 있다.
  • 모든 필드를 final로 선언한다.
    • 시스템이 강제하는 수단을 이용해 설계자의 의도를 명확히 드러내는 방법
    • 새로 생성된 인스턴스를 동기화 없이 다른 스레드로 건네도 문제없이 동작하게끔 보장하는데도 필요
  • 모든 필드를 private으로 선언
    • 필드가 참조하는 가변 객체를 클라이언트에서 직접 접근해 수정하는 일을 막아줌
    • 기술적으로는 기본 타입 필드나 불변 객체를 참조하는 필드를 public final로만 선언해도 불변객체가 되지만, 이렇게 하면 다음 릴리즈에서 내부 표현을 바꾸지 못하므로 권하지는 않는다.
  • 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
    • 클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 그 객체의 참조를 얻을 수 없도록 해야 한다.생성자, 접근자, readObject 메소드 모두에서 방어적 복사를 수행하라.
    • 이런 필드는 절대 클라이언트가 제공한 객체 참조를 가리키게 해서는 안 되며, 접근자 메소드가 그 필드를 그대로 반환해서도 안된다.

 

 

책에서의 예시이다.

public final class Complex{

    private final double re;
    private final double im;

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

    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,
                           re * c.im + im * c.re);
    }

    @Override public boolean equals(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);
    }

    @Override
    public int hashCode() {
        return 31 * Double.hashCode(realNumber) + Double.hashCode(imaginaryNumber);
    }

    @Override
    public String toString() {
        return "(" + realNumber + " + " + imaginaryNumber + "i)";
    }
}

복소수를 표현한 클래스다. 함수형 프로그래밍의 모습과 비슷한데

사용자가 Complex의 변수에 접근해서 사용하는 것이 아닌 사칙연산 메소드를 제공하여

이용하게끔 만들어 놓은 클래스이다.

 

그럼 이렇게 불변으로 클래스를 만들면 장점이 무엇일까?

  • 불변 객체는 단순하다.
    • 불확실한 변수를 줄여준다.
  • 불변 객체는 근본적으로 스레드 안전하여 따로 동기화 할 필요 없다.
    • 여러 스레드가 동시에 사용해도 훼손되지 않는다.
  • 불변 객체는 안심하고 공유할 수 있다.
  • 불변 객체는 자유롭게 공유할 수 있음은 물론, 불변객체끼리는 내부 데이터를 공유할 수 있다.
  • 불변 객체는 그 자체로 실패 원자성을 제공한다.

 

단점

  • 값이 다르면 반드시 독립된 객체로 만들어야 한다 -> 비용이 커진다.

 

 

인프런 수업중에서도 강사님이 자주 언급하시는 부분이 있다

바로 Getter는 만들지언정 Setter는 함부로 만들지 말라고 하신다.

 

이 처럼 컴포넌트에 접근 할 수 있는 요소를 최소화 하고 접근 할 수 있는 부분을 정해놔야

나중에 데이터가 변경되어도 어떤 부분에서 변경되었는지 추적할 수 있기 때문이다.

+ Recent posts