태그 달린 클래스(tagged class): 클래스 내부에 특정 "태그" 필드를 두어 객체의 유형을 나타내는 방식. 이러한 클래스는 보통 여러 유형의 객체를 하나의 클래스로 표현하고자 할 때 사용되며, 태그 값에 따라 다른 동작을 수행하도록 메서드들이 조건문(예: if-else, switch)을 사용해 구현된다.
// 태그 달린 클래스
public class Shape {
enum ShapeType { CIRCLE, RECTANGLE }
final ShapeType shapeType;
double radius; // 원일 경우 사용
double width; // 직사각형일 경우 사용
double height; // 직사각형일 경우 사용
// 원을 위한 생성자
Shape(double radius) {
shapeType = ShapeType.CIRCLE;
this.radius = radius;
}
// 직사각형을 위한 생성자
Shape(double width, double height) {
shapeType = ShapeType.RECTANGLE;
this.width = width;
this.height = height;
}
double area() {
switch (shapeType) {
case CIRCLE:
return Math.PI * radius * radius;
case RECTANGLE:
return width * height;
default:
throw new AssertionError(shapeType);
}
}
}
태그 달린 클래스는 단점이 한가득이다. 열거 타입 선언, 태그 필드, switch 문 등 쓸데없는 코드가 많다. 여러 구현이 한 클래스에 혼합돼 있어서 가독성도 나쁘다. 태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적이다. 태그 달린 클래스는 클래스 계층구조를 어설프게 흉내낸 아류이다.
클래스 계층구조(Class Hierarchy) : 객체 지향 프로그래밍에서 클래스들 사이의 상속 관계를 조직화하는 방법. 이 구조는 계층적이며, 상위 클래스(superclass)로부터 하위 클래스(subclass)가 파생되는 방식으로 구성된다. 클래스 계층구조는 실세계의 개념을 모델링하는 데 있어 상속과 다형성을 활용하여 코드의 재사용성, 확장성 및 유지보수성을 향상시키는 핵심적인 요소이다.
클래스 계층구조의 구성 요소 :
- 상위 클래스 (Superclass) / 부모 클래스 (Parent Class): 다른 클래스에 공통된 속성과 메서드를 제공하는 클래스. 하위 클래스는 이 상위 클래스의 속성과 메서드를 상속받는다.
- 하위 클래스 (Subclass) / 자식 클래스 (Child Class): 상위 클래스의 속성과 메서드를 상속받아, 추가적인 속성과 메서드를 가지며 더 구체적인 개념을 모델링하는 클래스.
- 추상 클래스 (Abstract Class): 인스턴스화할 수 없으며, 하나 이상의 추상 메서드(구현되지 않은 메서드)를 포함할 수 있는 클래스. 하위 클래스는 추상 클래스의 모든 추상 메서드를 구현해야 한다.
- 인터페이스 (Interface): 모든 메서드가 추상 메서드인 특별한 유형의 클래스로, 클래스가 특정 행동을 할 수 있음을 선언하는 데 사용된다.
// 클래스 계층구조
// 추상 클래스 Shape는 도형의 공통적인 특성을 정의하는 상위 클래스입니다.
// 모든 도형은 면적을 가지므로, 면적을 계산하는 추상 메서드 area()를 선언합니다.
// 이는 클래스 계층구조에서 공통 인터페이스 역할을 합니다.
abstract class Shape {
// 추상 메서드 area()는 하위 클래스에서 구체적인 면적 계산 로직을 구현해야 합니다.
// 이 메서드는 다형성을 가능하게 하는 중요한 부분입니다.
abstract double area();
}
// Circle 클래스는 Shape 클래스를 상속받아 원의 구체적인 특성을 모델링하는 하위 클래스입니다.
// Circle은 Shape의 구체적인 구현체로, 원의 면적을 계산하는 로직을 포함합니다.
class Circle extends Shape {
final double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double area() {
// 이 메서드는 Shape 클래스에서 선언된 추상 메서드 area()의 구체적인 구현입니다.
return Math.PI * radius * radius;
}
}
// Rectangle 클래스는 Shape 클래스를 상속받아 직사각형의 구체적인 특성을 모델링하는 하위 클래스입니다.
// Rectangle 역시 Shape의 구체적인 구현체로, 직사각형의 면적을 계산하는 로직을 포함합니다.
class Rectangle extends Shape {
final double width;
final double height;
Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
double area() {
// 이 메서드는 Shape 클래스에서 선언된 추상 메서드 area()의 구체적인 구현입니다.
return width * height;
}
}
결론 : 태그 달린 클래스를 써야 하는 상황은 거의 없다. 새로운 클래스르 작성하는 데 태그 필드가 등장한다면 태그를 없애고 계층구조로 대치하는 방법을 생각해보자. 기존 클래스가 태그 필드를 사용하고 있다면 계층구조로 리팩토링하는 걸 고민해보자.
'이펙티브 자바' 카테고리의 다른 글
[이펙티브 자바] 아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2024.03.25 |
---|---|
[이펙티브 자바] 아이템 24. 멤버 클래스는 되도록 static으로 만들라 (0) | 2024.03.25 |
[이펙티브 자바] 아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2024.03.22 |
[이펙티브 자바] 아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) | 2024.03.22 |
[이펙티브 자바] 아이템 20. 추상 클래스보다는 인터페이스를 우선하라 (0) | 2024.03.21 |