Item 23. 태그 달린 클래스보다는 클래스 계층 구조를 활용하라.

심규환·2022년 2월 6일
0

Effective Java

목록 보기
22/29

태그 달린 클래스란, 클래스 안에 Enum 타입이던 String에 값을 넣어 클래스를 구분하는 클래스를 말한다.
아래는 하나의 클래스 안에 태그로 원과 사각형을 구분하는 예시이다. (안좋은 예시)

class Figure{
	// 태그
    enum Shape { RECTANGLE, CIRCLE } ;

	final Shape shape;
    
    // RECTANGLE 태그에만 쓰일 필드
    double length;
    double width;
    
    // CIRCLE에만 쓰일 필드
    double radius;
    
    // CIRCLE용 생성자
    Figure(double radius){
    	shape = Shape.CIRCLE;
    	this.radius = radius;
    }
    
    // RECTANGLE용 생성자
    Figure(double length, double width){
    	shape = Shape.RECTANGLE;
        this.lenght = lenght;
        this.width = width;
    }
    
    
    // 넓이 계산 
    double area(){
    	switch(case){
        	case RECTANGLE:
            	return length * width;
            case CIRCLE:
            	return Math.PI * (radius * radius);
            default:
            	throw new AssertionError(shape);
        }
    }
}

이 코드에는 단점이 한가득이다.
1. 열거 타입 선언, 태그 필드, switch 쓸데 없는 코드
2. 가독성 안좋음
3. 쓰이지 않는 필드들이 존재하니 final로 변경하려면 모두 안써도 초기화 해줘야 한다.
4. 새로운 의미를 추가할 때마다 switch 문을 찾아서 하나하나 변경해줘야 한다.
5. 인스턴스 타입만 보고 어떤 클래스인지 알 수가 없다. 태그를 꼭 확인해야 한다.

보기도 어렵고 오류를 내기도 쉽기 때문에 태그 달린 클래스는 지양해야 한다. 이에 대한 대체로는 클래스의 계층구조를 사용해야한다.

다음은 태그 달린 클래스를 계층구조로 바꾸는 방법이다.
1. 루트 클래스를 하나 추상 클래스로 정의한다.
2. area() 메서드 같이 태그 값에 따라 달라지는 메서드를 추상 메서드로 구분한다.
3. 공통적으로 사용하는 필드들을 전부 루트 클래스에 올린다.
4. 공통적으로 사용하는 메서드들도 루트 클래스에 일반 메서드로 추가한다.
이 결과 Figure로 만들었던 클래스는 추상 메서드 area 하나만 남게 된다.

클래스 계층구조 변환

// root 클래스
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 width;
    final double length;
    
    Rectangle(double width, double length){
    	this.width = width;
        this.length = length;
    }
    
    @Override double area() { return length * width; }
}

이제 태그 달린 클래스들의 단점들을 모두 해결하게 된다. 남은 필드들은 모두 final로 선언했기 때문에 실수로 인해 다른 값에 값을 잘못 넣어서 발생할 런타임 오류도 잡아준다. 인스턴스의 변수명만으로 구분할 수 있고, 또한 타입의 자연스러운 계층 관계를 반영할 수 있어서 유연성을 제공해준다.
만약 정사각형을 표현하고 싶다면 사각형의 다른 버전이니 아래와 같이 간단하게 표현할 수 있다.

class Square extends Rectangle{
	Square(double side){
    	super(side, side);
    }
}
profile
장생농씬가?

0개의 댓글