자바에서는 클래스와 인터페이스를 설계하는데 사용할 수 있는 강력한 요소들을 제공한다. 이 장에서는 우리가 만드는 클래스와 인터페이스가 쓸모있고 강력하며 유연성이 있도록 하기 위해 도움을 주는 지침을 설명한다.
클래스와 그 멤버의 접근성을 최소화하자
잘 설계된 모듈은 외부 API 부분과 내부 구현 부분을 명쾌하게 분리하며 내부의 상세한 구현 부분 모두를 감춘다.
이 개념은 소프트웨어 설계 기본 원리 중 하나로, 정보 은닉(information hiding) 또는 캡슐화(encapsulation)라고 한다.
정보 은닉이 중요한 이유
시스템을 구성하는 모듈들 간의 결합도를 낮추어 모듈별로 개발, 테스트, 최적화, 사용 및 수정이 가능하도록 한다. 각 모듈별로 병행 개발할 수 있으므로 시스템 개발이 빨라진다. 모듈을 더 빨리 파악할 수 있다. 다른 모듈에 영향을 주지 않고 수정이 가능하므로, 유지 보수의 부담을 덜 수 있다. 정보 은닉 자체만으로 성능을 좋게 할 수 없지만 효과적인 성능 튜닝이 가능하다. (프로파일링을 통해 어떤 모듈이 문제를 유발하는지 결정되면, 다른 모듈에 영향을 주지 않고 최적화할 수 있다.) 모듈간의 결합도가 높지 않으므로 소프트웨어의 재사용성을 증가시킨다. 시스템 전체가 덜 개발 되었더라도 각 모듈별 성공 여부를 입증할 수 있으므로, 큰 시스템 개발 시 위험 부담이 줄어든다.
public 클래스에서는 public 필드가 아닌 접근자(accessor) 메소드를 사용한다
// 이와 같은 클래스는 public으로 사용하면 안된다!
class Point
{
public double x;
public double y;
}
이런 클래스는 외부에서 직접 접근이 가능하므로 캡슐화(encapsulation)이 장점을 제공하지 못한다.
객체 지향의 본질을 추구하려면 private 필드와 public 접근자(accessor) 메소드(게터:getter)를 갖는 클래스로 바꿔야 하며, 가변 클래스의 경우는 변경자(mutator) 메소드(세터:setter)를 추가해야한다.
// 접근자와 변경자 메소드를 사용한 데이터 캡슐화
class Point
{
private double x;
private double y;
public Point(double x, double y){
this.x = x;
this.y = y;
}
public double getX() { return x; }
public double getY() { return y; }
public void setX(double x) { this.x = x; }
public void setY(double y) { this.y = y; }
}
가변성을 최소화 하자
불변(immutable) 클래스는 자신의 인스턴스가 갖는 값을 변경할 수 없는 클래스이다.
불변 클래스가 필요한 이유는 가변 클래스에 비해 설계와 구현 및 사용이 쉽고, 또한 에러 발생이 적으며 보안이나 사용 측면에서 더 안전하기 때문이다.
불변 클래스를 만드는 다섯가지 규칙
1. 객체의 상태를 변경하는 그 어떤 메소드(변경자라고 하는)도 제공하지 않는다. 2. 상속(inheritance)을 할 수 없도록 하자. 실수건 고의건 서브 클래스에서 불변성을 저해하는 것을 막기 위함이다. 일반적으로 클래스를 final로 지정하면 상속을 막을 수 있다. 더 유연한 방법으로 public static 팩토리 메소드를 사용할 수 있다. public이나 protected 생성자가 없으므로 다른 패키지의 클래스가 상속받을 수 없기 때문이다.
public class Complex {
private final double re;
private final double im;
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
// public static 팩토리 메소드
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
... // 나머지 코드 생략
}
3. 모든 필드를 final로 지정한다. 4. 모든 필드를 private로 지정한다. 5. 가변 컴포넌트의 직접적인 외부 접근을 막자.
그 대신 생성자와 접근자 메소드 및 readObject 메소드에서(항목 76) 해당 객체의 방어 복사본(defensive copy)(항목 39)을 만들어 사용하도록 하자.
불변 클래스의 장점
불변 객체는 단순하여 생성될 당시의 상태 하나만 가질 수 있다. 불변 객체는 본질적으로 스레드에서 사용시 안전하므로 동기화가 필요없다. 불변 객체는 자유로운 공유가 가능하므로 그 객체의 방어 복사본을 만들 필요가 없다. 그러므로 불면 클래스에 clone 메소드나 복사 생성자(copy constructor)(항목 11)를 둘 필요가 없고 두어서도 안된다. 불변 객체는 객체의 공유는 물론이고 그 객체의 내부 구조들도 공유할 수 있다. 불변객체는 다른 객체(불변이나 가변을 불문하고)를 만들 때 사용할 수 있는 훌륭한 컴포넌트이다.
불변 클래스의 유일한 단점
객체가 가질 수 있는 각 값마다 별개의 객체가 필요하다는 것이다. 이렇게 여러 개의 객체를 생성하면 비용이 많이 들며, 객체가 크다면 특히 더 그렇다.
요약 : 인스턴스가 가변적이어야 할 타탕한 이유가 없다면, 그 클래스는 불변 클래스가 되어야 한다. 성능 향상에 필요하다고 확신이 설 때에만 불변 클래스에서 사용할 public 가변 클래스를 만든다(항목 55).
creative by javacafe