아이템 23 : 태그 달린 클래스보다는 클래스 계층구조를 활용하라
태그 달린 클래스
두 가지 이상의 의미를 표현하며, 이를 특정 필드(이 책에서는 태그)로 구분하는 클래스가 종종 있습니다.
class Figure {
enum Shape { RECTANGLE, CIRCLE };
// 태그 필드 - 현재 모양을 나타낸다.
final Shape shape;
// 아래 필드는 RECTANGLE 일 때만 쓰임
double length;
double width;
// 아래 필드는 CIRCLE 일 때만 쓰임
double radius;
// RECTANGLE 생성자
Figure(double length, double width) {
this.shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
// CIRCLE 생성자
Figure(double radius) {
this.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);
}
}
}
위 클래스의 경우, Shape Enum의 값을 기준으로 필드들의 사용 여부와 생성자가 구분되고, 메서드의 내부 로직도 분기가 발생합니다.
이렇게 태그 달린 클래스를 사용하는 경우 아래와 같은 단점이 있습니다.
- 분기를 위한 쓸데없는 코드가 많아짐
- 여러 구현이 혼합되어있어 가독성이 떨어짐
- 메모리를 많이 사용함
- 필드를 final로 선언 시, 사용하지 않는 필드까지 초기화해야함
- 엉뚱한 필드를 초기화 해도 런타임에서 문제가 드러남
- 새로운 태그를 추가 시, 전체 코드를 수정해야함. 빠뜨릴 경우 나락행...
이러한 단점을 종합했을 때, 책에서는 아래와 같이 강하게 비추합니다.
태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적이다.
태그 달린 클래스는 클래스 계층구조를 어서르게 흉내낸 아류일 뿐이다.
그리고 클래스 계층구조를 활용하는 서브 타이핑을 대안으로 제시합니다.
클래스 계층구조
태그 달린 클래스를 클래스 계층구조로 바꾸는 방법
- 계층구조의 root가 될 추상 클래스를 정의하고, 태그 값에 따라 동작이 달라지는 메서드들을 추상 메서드로 선언
- 태그 값에 상관없이 동작이 일정한 메서드들을 일반 메서드로 선언
- 모든 하위 클래스에서 공통으로 사용하는 데이터 필드를 루트 클래스에 선언
- 루트 클래스를 확장한 구체 클래스를 의미별로 하나씩 정의
- 각 하위 클래스에 각자 의미에 해당하는 데이터 필드를 선언
- 루트 클래스의 추상 메서드를 하위 클래스의 의미에 맞게 구현
위 Figure 클래스를 클래스 계층구조로 변경하면 아래와 같이 바꿀 수 있습니다.
abstract class Figure {
abstract double area();
}
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;
}
}
이렇게 변경된 클래스 계층구조는 태그 달린 클래스 대비 아래와 같은 장점을 가집니다.
- 간결하고 명확해진 클래스
- 관련 없는 데이터 필드 제거
- 컴파일러가 구현이 제대로 되었는지 확인
- 루트 클래스의 코드 수정 없이 독립적으로 계층구조를 확장하고 사용할 수 있음
만약, 정사각형 타입이 추가된다면 클래스 계층구조에서는 아래와 같이 정사각형 클래스만 추가해주면 됩니다.
class Square extends Rectangle {
Square(double side) {
super(side, side);
}
}
Reference
반응형
'☕️ JAVA > Effective JAVA' 카테고리의 다른 글
[Effective Java] 아이템 68 : 일반적으로 통용되는 명명 규칙을 따르라 (0) | 2021.12.18 |
---|---|
[Effective Java] 아이템 49 : 매개변수가 유효한지 검사하라 (0) | 2021.12.04 |
[Effective Java] 아이템 33 : 타입 안전 이종 컨테이너를 고려하라 (0) | 2021.11.28 |
[Effective Java] 아이템 13 : clone 재정의는 주의해서 진행하라 (0) | 2021.11.14 |
[Effective Java] 아이템 10 : equals는 일반 규약을 지켜 재정의하라 (0) | 2021.11.02 |