태그(플래그) 달린 클래스
// 원과 사각형을 하나의 클래스로 표현
class Figure {
enum Shape { RECTANGLE, CIRCLE }
// 태그 필드 - 이 도형의 모양을 나타냄
final Shape shape;
// RECTANGLE일 때만 사용되는 필드
double length;
double width;
// CIRCLE일 때만 사용되는 필드
double radius;
// 원을 위한 생성자
Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
// 사각형을 위한 생성자
Figure(double length, double width) {
shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
double area() {
switch(shape) {
case RECTANGLE:
return length * width;
case CIRCLE:
return Math.PI * (radius * radius);
default:
throw new AssertionError(shape);
}
}
}
위와 같이 태그 역할을 하는 필드를 통해 분류를 하는 방법이 존재한다. 하지만 이것은 OOP를 지원하는 Java에서 별로 좋지 못한 방법이다.
태그 클래스 단점
복잡해진다
보일러 플레이가 필요해진다. switch를 만들어거 각 태그에 맞는 구현도 필요해진다.
class Figure {
enum Shape { RECTANGLE, CIRCLE } // 1. enum 선언
final Shape shape; // 2. 태그 필드
// 3. switch 문이 모든 메서드에 반복
double area() {
switch(shape) { /* ... */ }
}
double perimeter() {
switch(shape) { /* ... */ }
}
}
메모리 낭비
아래 원의 경우 length, width이 필요 없음에도 다른 태그를 지원하기 위해서 변수가 필요하다.
Figure circle = new Figure(5.0); // 원 생성
final 선언 불가
아래 필드들은 final로 사용할 수 없다. 생성자가 전부 초기화 하지 않기 때문이다.
class Figure {
final Shape shape;
// final로 만들 수 없음
// 생성자에서 사용하지 않는 필드도 초기화해야 하기 때문
double length;
double width;
double radius;
}
계층구조로 리팩토링
1. 추상 클래스 생성
// 루트 클래스
abstract class Figure {
abstract double area();
}
2. 구체 클래스 생성
// 원 클래스
class Circle extends Figure {
final double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double area() {
return Math.PI * (radius * radius);
}
}
// 사각형 클래스
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;
}
}
정리
위와 같이 태그를 사용하는 대신 계층구조로 설계하여 이전보다 좋은 설계가 될 수 있다.