Item 20. 태그 달린 클래스 대신 클래스 계층을 활용하라
태그 달린 클래스...?
=> 태그 필드가 있는 클래스
=> 태그 필드 : 인스턴스의 형태 지정
ex. 도형 (식상하다)
class Figure {
enum Shape { RECTANGLE, CIRCLE };
// tag field : 인스턴스의 형태 지정
final Shape shape;
// Shape.RECTANGLE 일 때 사용하는 필드
double width;
double height;
// Shape.CIRCLE 일 때 사용하는 필드
double radius;
// Shape.CIRCLE 생성자
Figure(dobule radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
// Shape.RECTANGLE 생성자
Figure(double height, double width) {
shape = Shape.RECTANGlE;
this.height = height;
this.width = width;
}
// 도형의 넓이를 구하는 메서드
double area() {
switch(shape) {
case RECTANGLE :
return width * height;
case CIRCLE :
return Math.PI * (radius * radius) ;
default :
thorw new AssertionError();
}
}
boolean isFigure() {
return true;
}
}
문제점
- 필요없는 필드가 많다
- 메모리 요구량 상승
- 관리 이슈
- 모든 필드에 final 추가시 : 관련 없는 필드를 초기화 해주어야 함
- 그렇다고 안하면? 생성자에서 잘못된 필드 초기화 시 runtime시에 뻗을 수 있음 (컴파일 단계에서 알 수 없다)
- final로 선언된 필드는 반드시 초기화가 되어야함. 안되면 컴파일 시 에러
Variable 'var' might not have been initialized.
- final로 선언된 필드는 반드시 초기화가 되어야함. 안되면 컴파일 시 에러
- boilerplate가 반복된다 : enum, tag field, switch-case
- 새로운 기능 추가시 switch-case 또 추가
- 인스턴스의 자료형만 보고, 인스턴스가 어떤 기능을 하는지 알기 어렵다 : 가독성 떨어짐
해결방법 : subtyping(하위 자료형 정의)
태그 필드에 따라 다르게 동작하는 메서드를 abtract method로 갖는 추상 클래스 정의
- area()
ex.
abstract class Figure { abstract double area(); boolean isFigure() { return true; } }
- 1의 추상 클래스에 태그 필드 값에 좌우되지 않는 메서드/필드 추가
- isFigure()
- ex.
abstract class Figure { abstract double area(); }
태그 필드에 맞춰 동작하는 기능을 추상 클래스의 하위 클래스로 정의
- Rectangle, Circle class
ex.
abstract class Figure { abstract double area(); } class Circle extends Figure { double area { return Math.PI * (radius * radius); } } class Rectangle extends Figure { double area { return width * height; } }
3의 하위 클래스에 기능을 위한 메서드/필드 추가
- constructor, radius/width,height
ex.
abstract class Figure { abstract double area(); } class Circle extends Figure { final dobule radius; Circle(dobule radius) { this.radius = radius; } double area { return Math.PI * (radius * radius); } } class Rectangle extends Figure { final double width; final double height; Rectangle(double height, double width) { this.height = height; this.width = width; } double area { return width * height; } }
subtyping의 장점은?
- 태그 기반 클래스의 문제를 해결
- 태그 값들의 계층 관계도 반영 가능
- ex. Rectangle - Sqaure