[Effective Java] item23 - 태그 달린 클래스보다는 클래스 계층구조를 활용하라

신민철·2023년 5월 9일
0

Effective Java

목록 보기
23/23
post-thumbnail
public class Figure {
	enum Shape { RECTANGLE, CIRCLE };

	final Shape shape;

	double length;
	double width;

	double radius;

	public Figure(double radius) {
		shape = Shape.CIRCLE;
		this.radius = radius;
	}

	public Figure(double length, double width) {
		shape = Shape.RECTANGLE;
		this.length = length;
		this.width = width;
	}

	public double area() {
		switch (shape) {
			case RECTANGLE:
				return length * width;
			case CIRCLE:
				return Math.PI * (radius * radius);
			default:
				throw new AssertionError(shape);
		}
	}
}

다음 예시와 같이 현재 표현하는 의미를 태그로 알려주는 클래스를 가끔 볼 수 있다. shape 멤버 변수로 나타내는 것이다.

이러한 코드는 일단 문제가 많다. 열거 타입을 선언해야 하고, area 메소드의 switch 문 등의 비효율적인 코드가 들어가게 된다. 물론 이렇게 되면 가독성까지 나빠지게 된다.

shape 멤버 변수가 final 이므로 초기화를 해야되는데 이 상황에서 쓰레기 값이 들어가야 할 수도 있다.

정리하면 태그 달린 클래스는 장황하, 오류를 내기 쉽고, 클래스의 계층 구조를 흉내내다가 잘못된 케이스라고 할 수 있다.

그래서 이 태그 달린 클래스를 클래스의 계층 구조로 쪼갤 수 있는데, 먼저 공통적인 멤버 변수나 메소드가 있는지 봐야 한다. 여기서는 CIRCLE과 RECTANGLE의 공통적인 메소드는 area()밖에 없기 때문에 공통적 조상 클래스로 뺀다.

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;
	}
}

클래스 계층 구조로 뽑아내게 되면 이런 식으로 작성될 수 있을 것이다. 위에서 포함되어 있는 case와 같은 불필요한 코드도 모두 빠진 모습이다. 모든 필드는 final로 작성될 수 있게 되었다. 그래서 런타임 오류가 발생할 케이스도 적어지는 것이다. 또한 정사각형과 같은 클래스를 추가하려면 Rectangle을 상속받아 아주 간단하게 모양을 추가할 수 있을 것이다.



핵심 정리
태그 달린 클래스는 계층적 클래스로 대체할 수 있다.

0개의 댓글