[아이템 17] 변경 가능성을 최소화하라
아이템 17
변경 가능성을 최소화하라
*불변 클래스: 인스턴스의 내부 값을 수정할 수 없는 클래스
자바 플랫폼에는 예를 들어 String, 기본 타입의 박싱된 클래스들, BigInteger, BigDecimal가 있다.
불변 클래스로 설계하는 이유는 (1) 가변보다 사용하기 쉬우며, (2) 오류가 생길 여지가 적고 안전하다.
불변 클래스의 규칙
- 객체의 상태를 변경하는 메서드를 제공하지 않는다. (Setter)
- 클래스를 확장할 수 없도록 한다.
상속을 막는 대표적인 방식은 final로 선언하는 것이다. - 모든 필드를 final로 선언한다.
- 모든 필드를 private으로 선언한다.
- 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 그 객체의 참조를 얻을 수 없도록 해야 한다. 참조를 얻을 수 없게 하는 대신, 방어적 복사를 수행해야 한다.
불변 클래스
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 Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im); //새로운 객체 반환
}
}
위의 코드를 보면, 인스턴스 자신은 수정하지 않고 새로운 인스턴스를 만들어 반환한다. (함수형 프로그래밍 패턴이라 한다.) 즉, 생성된 시점의 상태를 파괴될 때까지 그대로 간직하고 있다.
불변 클래스의 특징
-
근본적으로 스레드 안전하여 따로 동기화할 필요가 없다.
여러 스레드가 동시에 사용해도 훼손되지 않는다. 불변 객체에 대해서는 그 어떤 스레드도 다른 스레드에 영향을 줄 수 없으니 불변 객체는 안심하고 공유할 수 있다.
가장 쉬운 재활용 방법으로는 자주 쓰이는 값을 상수public static final
로 제공하는 것이다.//상수 예시 pulbic static final complex ZERO = new complex(0, 0);
-
불변 객체는 자유롭게 공유할 수 있음은 물론, 불변 객체끼리는 내부 데이터를 공유할 수 있다.
예를 들어, BigInteger 클래스는 부호를int변수
, 크기를ìnt배열
을 사용한다. 여기서 negate 메서드는 크기는 같고 부호만 반대인 새로운 BigInteger를 생성하는데, 크기는 복사하지 않고 원본 인스턴스와 공유해도 된다. -
객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다.
값이 바뀌지 않는 구성요소들로 이뤄진 객체라면 불변식을 유지하기에 수월하다. 예를 들어 불변 객체는 맵의 키와 Set의 원소로 쓰기에 좋다. -
불변 객체는 그 자체로
실패 원자성
을 제공한다.
failure atomicity: 메서드에서 예외가 발생한 후에도 객체 상태는 메서드 호출 전과 같아야 한다. -
불변 클래스는 값이 다르면 반드시 독립된 객체로 만들어야 한다.
비트 하나라도 다르다면 비트 하나만 다른 새로운 인스턴스를 생성하도록 한다.대처하는 방법 중 하나는 다단계 연산들을 예측하여 기본 기능으로 제공하는 방법이다. 다단계 연산을 기본으로 제공한다면 더 이상 각 단계마다 객체를 생성하지 않아도 된다. (BigInteger는 모듈러 지수 같은 다단계 연산 속도를 높여주는 가변 동반 클래스를 Package-private으로 두고 있다.) 복잡한 연산을 예측하기 어렵다면 public 으로 제공한다.
불변 클래스 설계 방법
자신을 상속하지 못하게 해야 한다.
즉, 모든 생성자를 private
혹은 package-private
으로 만들고 public 정적 팩터리를 제공하는 방법이다.
public final class Complex {
private final double re;
private final double im;
private Complex(double re, double im) { //생성자는 private
this.re = re;
this.im = im;
}
public static Complex valueOf(double re, double im){ //정적 팩터리
return new Complex(re, im);
}
}
BigInteger과 BigDecimal는 재정의할 수 있도록 설계가 되어 있다. 이 경우, 신뢰할 수 없는 하위 클래스의 인스턴스라고 확인된다면 방어적으로 복사해 사용해야 한다.
public static BigInteger safeInstane(BigInteger val) { //클래스 확인
return val.getClass() == BigInteger.class ? val : new BigInteger(val.toByteArray());
}
정리
- 게터가 있다고 해서 세터를 만들지는 말자. (불변을 위해)
- 단순한 값 객체 (예를 들어 phoneNumber, Complex)는 항상 불변으로 만들자
- 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자.
변경해야 할 필드를 뺀 나머지 모두는 합당한 이유가 없다면 private final로 선언하자 - 생성자는 불변식 설정이 모두 완료된 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.
초기화 메서드 또한 public으로 제공해서는 안된다. (객체 재활용 목적의 초기화 메서드 또한 마찬가지)
Author And Source
이 문제에 관하여([아이템 17] 변경 가능성을 최소화하라), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jimin3263/아이템-17-변경-가능성을-최소화하라저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)