NMP-Study / EffectiveJava2022

Effective Java Study 2022
5 stars 0 forks source link

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

Closed okhee closed 2 years ago

mbyul commented 2 years ago

태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적임 (부제 : 태그 달린 클래스는 클래스 계층구조를 어설프게 흉내낸 아류)

: 두 가지 이상의 의미를 표현할 수 있으며, 그중 현재 표현하는 의미를 태그값으로 알려주는 클래스 : 다음 코드는 원과 사각형을 표현할 수 있는 클래스이다.

/* 태그 달린 클래스 */
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 = legnth;
  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로 선언하려면 해당 의미에 쓰이지 않는 필드들까지 생성자에서 초기화해야 함
  • 쓰지 않는 필드를 초기화하는 불필요한 코드가 생김
  • 엉뚱한 필드를 초기화해도 런타임에야 문제가 드러날 수 있음
  • 또 다른 의미를 추가하려면 코드를 수정해야 함
  • 인스턴스의 타입만으로는 현재 나타내는 의미를 알 길이 전혀 없음

태그 달린 클래스는 지양하고, 클래스 계층구조를 지향하자

/* 계층 구조 */
// 추상 클래스
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;
 }

 @Overide double area() { return length * width; }
}

장점

  • 간결하고 명확하며, 쓸데없는 코드가 모두 제거됨
  • 각 의미를 독립된 클래스에 담아 관련 없던 데이터 필드를 모두 제거함
  • 실수로 빼먹은 case 문 때문에 런타임 오류가 발생할 일도 없음
  • 루트 클래스의 코드를 건드리지 않고도, 다른 프로그래머들이 독립적으로 계층구조를 확장하고 함께 사용할 수 있음
  • 타입이 의미별로 따로 존재하니 변수의 의미를 명시하건, 제한할 수 있고, 또 특정 의미만 매개변수로 받을 수 있음
  • 타입 사이의 자연스러운 계층 관계를 반영할 수 있어서 유연성은 물론 컴파일타임 타입 검사 능력을 높여줌
class Square extends Rectangle {
    Square(double side) {
        super(side, side);
    }
}