[아이템 23] 태그 달린 클래스보다는 클래스 계층구조를 활용하라

gang_shik·2022년 4월 27일
0

Effective Java 4장

목록 보기
9/11
  • 두 가지 이상의 의미를 표현할 수 있으며, 그 중 현재 표현하는 의미를 태그 값으로 알려주는 클래스가 있음, 아래와 같이 예를 볼 수 있음
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);
				}
		}
}
  • 위와 같은 태그 달린 클래스는 열거 타입 선언, 태그 필드, switch문 등 쓸데없는 코드가 많음

  • 여러 구현이 섞여서 가독성도 나쁘고 메모리도 많이 사용함

  • 엉뚱한 필드를 초기화해도 런타임에서 문제가 나오고 또다른 의미를 추가하려면 코드를 수정해야함

  • 한마디로 태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적임

  • 태그 달린 클래스는 클래스 계층구조를 어설프게 흉내낸 아류일 뿐임

클래스 계층구조

  • 위의 예시를 활용하면, 가장 먼저 계층구조의 루트(root)가 될 추상 클래스를 정의하고, 태그 값에 따라 동작이 달라지는 메서드들을 루트 클래스의 추상 메서드로 선언함

  • 그런 다음 태그 값에 상관없이 동작이 일정한 메서드들을 루트 클래스에 일반 메서드로 추가함

  • 모든 하위 클래스에서 공통으로 사용하는 데이터 필드들도 전부 루트 클래스로 올림

  • Figure 클래스에는 태그 값에 상관없는 메서드가 하나도 없고, 모든 하위 클래스에서 사용하는 공통 데이터 필드도 없음, 그 결과 루트 클래스에는 추상 메서드인 area 하나만 남음

  • 그 다음 루트 클래스를 확장한 구체 클래스를 의미별로 하나씩 정의함

  • 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);
		}
}
profile
측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다

0개의 댓글