Home [Effective Java] Item23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라!
Post
Cancel

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

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

  • 두 가지 이상의 의미를 표현할 수 있으며, 그 중 현재 표현하는 의미를 태그 값으로 알려주는 클래스에 대한 부분이다.

태그 달린 클래스

  • 클래스 계층구조보다 훨씬 나쁘다.

태그 달린 클래스의 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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.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 문 등 쓸데없는 코드가 많다.
  • 여러 구현이 한 클래스에 혼합되어 가독성이 나쁘다.
  • 항상 다른 의미를 위한 코드를 사용하니 메모리를 많이 차지한다.
  • final로 필드 선언하려면 해당 의미(태그)에 사용하지 않는 필드까지도 초기화해야 한다.
  • 엉뚱한 필드를 초기화하면 런타임 에러를 던진다. 에러를 던지기 전까지 문제 파악이 힘들다.
  • 다른 의미를 추가하려면 코드 수정이 불가피하다.
  • 인스턴스 타입만으로는 현재 나타내는 의미를 알 길이 없다.

클래스 계층구조

  • 자바와 같은 객체 지향 언어는 타입 하나로 다양한 의미의 객체를 표현하는 훨씬 나은 수단을 제공한다.
    • 클래스 계층구조를 활용하는 서브타이핑(subtyping)
  • 태그 달린 클래스는 클래스 계층구조를 어설프게 흉내낸 아류일 뿐이다.

태그 달린 클래스 -> 클래스 계층구조

  • 계층구조의 루트(root)가 될 추상 클래스 정의
    • 태그 값에 따라 동작이 달라지는 메서드들을 루트 클래스의 추상 메서드로 선언
    • 태그 값에 상관없이 동작이 일정한 메서드 -> 루트 클래스에 일반 메서드로 추가
    • 모든 하위 클래스에서 공통으로 사용하는 데이터 필드 -> 루트 클래스에 올리기
  • 루트 클래스를 확장한 구체 클래스를 의미별로 하나씩 정의
    • 각 하위 클래스에는 각자의 의미에 해당하는 데이터 필드들을 넣는다.
    • 루트 클래스가 정의한 추상 메서드를 각자의 의미에 맞게 구현한다.

변환 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
abstract class Figure { // root class
  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;
  }
}

클래스 계층구조 사용의 장점

  • 간결하고 명확해졌다.
  • 태그 달린 클래스 사용의 단점에서 언급한 쓸데없는 코드들이 모두 사라졌다.
  • 각 의미를 독립시켰으니 관련없는 데이터 필드는 모두 제거되었다.
  • 독립된 데이터 필드들은 모두 final 이다.
  • 태그 달린 클래스와 달리 컴파일러의 도움을 받을 수 있다.
    • 각 클래스의 생성자가 모든 필드를 남김없이 초기화하고, 추상 메서드를 모두 구현했는지 확인해준다.
  • 루트 클래스의 코드를 수정할 필요 없이, 다른 프로그래머들이 독립적으로 계층구조를 확장하고 함께 사용하는 것이 가능해졌다.
  • 타입 사이의 자연스로운 계층 관계 반영이 가능해져 유연성 및 컴파일타임 타입 검사 능력을 높여준다.

핵심 정리

  • 태그 달린 클래스를 써야 하는 상황은 거의 없다.
  • 새로운 클래스 작성 시, 태그 필드가 등장한다면 태그를 없애고 계층구조로 대체하는 방법을 생각해보자.
  • 기존 클래스가 태그 필드를 사용한다면 계층구조 사용으로 리팩터링하자.
This post is licensed under younghwani by the author.

[Effective Java] Item22. 인터페이스는 타입을 정하는 용도로만 사용하라!

[Effective Java] Item24. 멤버 클래스는 되도록 static으로 만들라!

Comments powered by Disqus.