Skip to content

Latest commit

 

History

History
105 lines (76 loc) · 4.59 KB

17.변경 가능성을 최소화하라. 불변 객체를 만들자.md

File metadata and controls

105 lines (76 loc) · 4.59 KB

불변 클래스🦴란?

인스턴스의 내부 값을 수정할 수 없는 클래스

불변 클래스의 장점👍은?

  • 가변 클래스보다 설계하고 구현하고 사용하기 쉽다.
  • 오류가 생길 여지가 적고 훨씬 안전하다.

불변 클래스로 만드는 방법🔨

객체의 상태를 변경하는 메서드를 제공하지 않는다.

Setter... 등등 알죠?

클래스를 확장할 수 없도록 한다.

상속이 가능하면 하위 객체는 오버라이딩등 불변을 보장할 수 없게 된다.

상속하지 못하게 하는 방법 모든 생성자를 `private` 혹은 `package-private`으로 만들고, `public 정적 팩토리`를 제공한다.
    private Yield(float yield) {
           this.yield = yield;
       }
    
       public static Yield calculate(LottoMoney lottoMoney, Long totalWinningMoney) {
           return new Yield(lottoMoney.divide(totalWinningMoney));
       }

모든 필드를 final로 선언한다.

설계자의 의도를 명확하게 드러내고 멀티 쓰레드에서도 안전할 수 있게 한다.

모든 필드를 private으로 선언한다.

필드가 참조하는 가변 객체를 클라이언트에서 직접 접근해 수정하는 일을 막아준다.

자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

가변 컴포넌트(배열, Collection 등)를 가지는 필드가 하나라도 있다면 클라이언트에서 참조를 얻을 수 없도록 해야한다. 방어적 복사를 수행하라.

함수형 프로그래밍을 이용한다.

피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴

class Car {
        private final int position;
    
        Car(int position) {
            this.position = position;
        }
            
        Car move() {
            return new Car(position + 1);
        }
    }

##불변 객체의 장점👍은?

단순하다.

생성 시점의 상태를 파괴될 때까지 간직하므로 믿고 사용할 수 있다.

스레드 안전하여 동기화할 필요가 없다.

어떤 스레드도 다른 스레드에 영향을 줄 수 없으므로 안심하고 공유할 수 있다.

자주 사용되는 인스턴스를 캐싱하여 인스턴스를 중복 생성하지 않게 해주는 정적 팩터리를 제공할 수 있다.

  • 여러 클라이언트가 인스턴스를 공유하여 메모리 사용량과 GC 비용이 줄어든다.
  • 방어적 복사도 필요 없으므로 clone 메서드나 복사 생성자를 제공하지 않는 게 좋다.

자유롭게 공유할 수 있으며, 불변 객체끼리는 내부 데이터를 공유할 수 있다.

public class BigInteger extends Number implements Comparable<BigInteger> {
    final int signum;
    final int[] mag;

    public BigInteger negate() {
            return new BigInteger(this.mag, -this.signum);
        }

mag배열은 비록 변이지만 복사하지 않고 원본 인스턴스와 공유해도 된다. 그 결과 새로 만든 BigInteger 인스턴스도 원본 인스턴스가 가리키는 내부 배열을 그대로 가리킨다.

불변 객체를 구성요소로 사용하면 사용 객체도 불변을 유지하기 쉽다.

불변 객체로 이뤄진 객체라면 아무리 복잡해도 불변식을 유지하기 훨씬 쉽다. 불변 객체는 맵의 키집합(Set)의 원소로 쓰기에 좋다.

public class LottoResult {
    private final Map<Rank, Long> result;

실패 원자성을 제공한다.

실패 원자성(failure atomicity)이란 메서드에서 예외가 발생한 후에도 그 객체는 여전히 호출 전과 같은 유효한 상태여야 한다는 성질이다. 불변 객체의 메서드는 내부 상태를 바꾸지 않으니 이 성질을 만족한다.

불변 객체의 단점👎은?

값이 다르면 반드시 독립된(식별자가 다른) 객체로 만들어야 한다.

객체 생성 비용이 높고 상태가 다른 객체를 자주 만들어야 하는 객체라면 큰 비용이 들 수 있다.

  • 다단계 연산을 제공하여 각 단계마다 객체를 생성하지 않도록 한다.
    • StringBuilder

불변 객체로 만들기 어려운 상황😥이라면?

  • 변경할 수 있는 부분을 최소한으로 줄인다.

    나머지 모두를 final로 선언한다. 다른 합당한 이유가 없다면 모든 필드는 private final이어야 한다.

  • 생성자는 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.