태그 달린 클래스
태그 클래스란
두가지 이상의 의미를 표현할 때 그 중 현재 표현하는 의미를 태그값으로 알려주는 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Figure {
enum Shape {
RECTANGLE, CIRCLE
}
// 태그 필드
final Shape shape;
// RECTANGLE 용 필드
double length;
double width;
// CIRCLE 용 필드
double radius;
// RECTANGLE
Figure(double length, double width) {
shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
// CIRCLE
Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
double area() {
switch(shape) {
case RECTANGLE:
return length * width;
case CIRCLE:
return Math.PI * (radius * radius);
default:
throw new AssertionError(shape);
}
}
}
태그 클래스의 단점
- Enum 타입 선언, 태그 field인 shape, switch문 등 여러 구현이 혼합 되어 있다.
- 이로 인해 가독성이 떨어진다.
- 사용하지 않는 필드로 인해 메모리도 추가적으로 사용한다.
- 또 다른 타입을 추가하게 되면 switch 문의 수정이 일어난다.
- 인스턴스 타입만으로 객체의 의미를 알 수 없다.
태그가 달린 클래스는 코드가 길어지고 확장성에 취약하다.
또한, 태그가 추가 될 때마다 사용하지 않는 필드들이 추가 될 수 있다.
계층 구조의 클래스를 만드는 방법
- 계층구조의 최상위(root)가 될 추상클래스를 정의한다.
- 태그값에 따라 달라지는 동작(메서드)들을 최상위 클래스의 추상 메서드로 선언한다.
- 태그값에 상관없이 동작이 일정한 메서드는 최상위 클래스에 일반 메서드로 정의한다.
- 모든 하위 클래스에 공통으로 사용하는 상태값(필드)들은 루크 클래스에 정의한다.
계층구조로 변환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
abstract class Figure {
abstract double area();
}
class Rectangle extends Figure {
final double length;
final double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
double area() {
return length * width;
}
}
class Circle extends Figure {
final double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double area() {
return Math.PI * (radius * radius);
}
}
클래스 계층구조의 장점
- 독립된 의미를 가지는 상태값(필드)들이 제거 되어 각 구현 클래스는 간결해진다
- 살아남은 field는 모두 final이므로 정의할 수 있다
- 실수로 빼먹은 case 문으로 인해 runTime 에러를 방지할 수 있다
- 최상위 클래스를 수정하지 않고 타입을 확장 할 수 있다
- 타입사이의 자연스러운 계층 관계를 반영할 수 있다