티스토리 뷰
(Effective Java) 규칙23. 태그달린 클래스보다는 클래스 계층구조를 활용하라
태그 달린 클래스
-
두 가지 이상의 의미를 표현할 수 있으며, 그중 현재를 표현하는 의미를 태그 값으로 알려주는 클래스를 본 적이 있을 것이다.
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
문 등 쓸데 없는 코드가 많음
- 여러 구현이 한 클래스에 혼합돼 있어서 가독성도 나쁨
- 다른 의미를 위한 코드도 언제나 함께 하니 메모리도 많이 사용함
필드들을 final
로 선언하려면 해당 의미에 쓰이지 않는 필드들까지 생성자에서 초기화해야 함
- 생성자가 태그 필드를 설정하고 해당 의미에 쓰이는 데이터 필드들을 초기화하는 데 컴파일러가 도와줄 수 있는 건 별로 없음
- 엉뚱한 필드를 초기화해도 런타임에야 문제가 드러남
또 다른 의미를 추가하려면 코드를 수정해야 함
- 새로운 의미를 추가할 때마다 모든
switch
문을 찾아 새 의미를 처리하는 코드를 추가해야 하는데, 하나라도 빠뜨리면 런타임에 문제가 불거져 나올 것임
인스턴스의 타입만으로는 현재 나타내는 의미를 알 길이 전혀 없음
- 태그 달린 클래스는 장황하고, 오류를 내기 쉽고 비효율 적임
클래스 계층 구조
- 다행히 자바와 같은 객체 지향 언어는 타입 하나로 다양한 의미의 객체를 표현하는 훨씬 나은 수단을 제공한다.
- 클래스 계층구조를 활용하는 서브타이핑(subtyping)임
- 태그 달린 클래스는 클래스 계층구조를 어설프게 흉내낸 아류일 뿐이다.
태그 달린 클래스를 클래스 계층구조로 바꾸는 방법
- 계층구조의 루트(root) 가 될 추상 클래스를 정의하고, 태그 값에 따라 동작이 달라지는 메서드들을 루트 클래스의 추상메서드로 선언한다.
- 위 예제에서
Figure
클래스의area
가 이러한 메서드에 해당함
- 위 예제에서
- 태그 값에 상관없이 동작이 일정한 메서드들을 루트 클래스에 일반 메서드로 추가한다.
- 모든 하위 클래스에서 공통으로 사용하는 데이터 필드들도 전부 루트 클래스로 올림
Figure
클래스에서는 태그 값에 상관없는 메서드가 하나도 없고, 하위 클래스에서 사용하는 공통 데이터 필드도 없음- 그 결과 루트 클래스에는 추상 메서드인
area
하나만 남게 됨
- 그 결과 루트 클래스에는 추상 메서드인
- 루트 클래스를 확장한 구체 클래스를 의미별로 하나씩 정의한다.
- 위 예제에서는
Figure
를 확장한 원(Circle
)클래스와 사각형(Rectangle
)클래스를 만들면 됨- 각 하위 클래스에는 각자의 의미에 해당하는 데이터 필드들을 넣음
- 위 예제에서는
- 루트 클래스가 정의한 추상 메서드를 각자의 의미에 맞게 구현한다.
클래스 계층구조로의 수정
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; }
}
-
기존의 쓸데없는 코드들을 전부 날리면서 각 의미를 독립된 클래스에 담아 관련 없던 데이터 필드를 모두 삭제 했다.
- 살아 남은 필드들은 모두
final
임
- 살아 남은 필드들은 모두
-
각 클래스의 생성자가 모든 필드를 남김없이 초기화하고 추상 메서드를 모두 구현했는지 컴파일러가 확인해준다.
- 실수로 빼먹은
case
문 때문에 런타임 오류가 발생한 일도 없음
- 실수로 빼먹은
-
타입 사이의 자연스러운 계층 관계를 반영할 수 있어서 유연성은 물론 컴파일타임 타입 검사 능력을 높여준다는 장점도 있다.
-
만약 정사각형도 지원하도록 수정한다고 생각해보자
//클래스 계층구조에서라면 다음과 같이 정사각형이 사각형의 특별한 형태임을 아주 간단하게 반영이 가능함 class Square extends Rectangle { Square(double side) { super(side, side); } }
-
결론
-
태그 달린 클래스를 써야 하는 상황은 거의 없다.
-
새로운 클래스를 작성하는 데 태그 필드가 등장한다면 태그를 없애고 계층구조로 대체하는 방법을 생각해보자.
- 기존 클래스가 태그 필드를 사용하고 있다면 리팩터링을 고민해볼 것
끝으로
이 글이 도움이 되었다면, 하단의 Google 광고 👎👎👎 한번씩 클릭 부탁 드립니다. 🙏🙏🙏
광고 클릭은 많은 힘이 됩니다!
반응형
'프로그래밍 > EffectiveJava' 카테고리의 다른 글
(이펙티브 자바) 규칙24. 멤버 클래스는 되도록 static으로 만들라 (0) | 2020.03.17 |
---|---|
(이펙티브 자바) 규칙25. 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2020.03.15 |
(이펙티브 자바) 규칙26. 로 타입은 사용하지 말라 (0) | 2020.03.13 |
(이펙티브 자바) 규칙63. 문자열 연결은 느리니 주의하라 (0) | 2020.03.12 |
(이펙티브 자바) 규칙64. 객체는 인터페이스를 사용해 참조하라 (0) | 2020.03.11 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- Java UI
- 이펙티브
- JavaFX
- 배낭 여행
- JavaFX Table View
- 일본여행
- 이펙티브자바
- 스프링부트
- java
- 텐트
- JavaFX 종료
- effective java
- 자전거 여행
- JavaFX 테이블뷰
- 자바
- 일본 배낭여행
- 일본 여행
- 일본 자전거 여행
- 배낭여행
- JavaFX Window Close
- windows
- intelij
- 이펙티브 자바
- effectivejava
- git
- springboot
- 인텔리제이
- 방통대 과제물
- 자전거
- TableView
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함