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

코린이서현이·2025년 2월 9일
0

이펙티브자바

목록 보기
6/6
post-thumbnail

태그 달린 클래스🏷️가 뭔가요?

태그 달린 클래스는 어거지로 하나의 클래스가 구체적인 표현을 두가지 이상 가진 클래스를 뜻합니다.
태그가 클래스의 구체 동작을 결정하고, 태그는 final 필드로 표현됩니다.

사실 우리는 자바를 정석으로 배워왔고, 객체 지향 프로그래밍이 탄탄히 자리잡힌 시점에서는 이런 클래스 구조를 볼 일이 없을 것 같은데요.

이 개념을 머릿속에 담기보다는 문제점이 무엇인지 생각하면서 읽으면 재밌을 것 같습니다!
아래에서 4가지의 문제점을 설명드릴게요!!

태그(필드)가 있는 클래스

public class Figure {
    enum Shape {RECTANGLE, CIRCLE};

    // 태그 필드 -> 이 필드 값에 따라서 메서드 내부 동작이 결정된다. 
    final Shape shape;

    //사각형일 때 쓰이는 필드들
    double length;
    double width;

    //원일 때 쓰이는 필드들
    double radius;

    //원 생성자
    public FigureWithTag(double radius) {
        this.shape = Shape.CIRCLE;
        this.radius = radius;
    }

    //사각형 생성자
    public FigureWithTag(double length, double width) {
        this.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);
        }
    }
}

태그 달린 클래스의 문제점은 무엇일까?

  1. 오류가 쉽게 발생한다.
    switch 문을 이용해 동작을 결정하게 된다. 만약 개발자가 실수로 빼 먹을 경우에 의도치 않은 동작이 발생한다.
  1. 가독성이 낮아진다.
    구현을 결정하기 위한 코드가 많아지고, 구현이 섞여 있어 가독성이 나쁘다.
    또한 원으로 인스턴스를 만들었을 때, 이 타입이 원인지 사각형인지 알 기 어렵다.
  1. 확장이 어렵고, 유지보수가 어렵다.
    원으로 쓰고 싶은 경우에 원의 구현에만 집중하는 것이 아니기 때문에 수정시 유연하지 못하다.
  1. 컴파일러을 활용하기 어렵다.
    잘못된 필드를 초기화하더라도, 오류가 컴파일이 아닌 런타임에 발생하게된다.

추상 클래스와, 하위 클래스로 만들자

추상 클래스

  • 구체 타입에 따라 동작이 달라지는 메서드는 추상메서드로 만든다. (필드도 동일)
  • 구체 타입에 관계없이 동작이 정해져있는 메서드는 일반 메서드로 만든다.

하위 클래스

  • 추상 메서드로 정의했던 동작을 오버라이딩한다.
    이렇게 하면, 재정의되지 않은 메서드에 대해서는 컴파일러가 오류를 내 개발 단계에서 오류를 반날 수 있다.
  • 구체 타입에서 필요한 필드들을 추가한다.

이 구현의 장점은 무엇일까?

  1. 단일 책임 원칙(Single Responsibility Principle, SRP)을 지킬 수 있게 되었다.

    단일 책임 원칙(SRP) : 한 클래스는 하나의 책임만 가져야 한다

    이전에는 한 클래스가 사각형과 원이라는 여러 책임감을 가지고 있었지다.
    그러나 클래스 계층 구조로 변경하면, 한 하위 클래스가 그 타입에 대한 하나의 책임을 가져, 응집도가 높아진다.

  1. OCP(Open-Closed Principle) 원칙을 지킬 수 있게 된다.

    OCP : 확장에는 열려있고, 수정에는 닫혀있어야 한다

    이전에는 사각형 넓이 계산에 수정이 있거나 삼각형이라는 새로운 타입을 추가하고 싶을 떄 관련없는 코드도 수정되어야했다.
    새로운 확장에 기존 코드 수정이 필요했다.
    그러나 클래스 계층 구조로 변경 후에는, 새로운 타입이 추가되더라도 다른 하위 클래스에 영향을 주지 않는다.

이 밖에 객체 생성 시점이나 별도의 메서드없이는 인스턴스의 구체 타입을 알기 어려웠던 문제점, 낮은 가독성등에 대한 문제점을 해결할 수 있다.

컴파일 오류를 내자

글의 분량이 아쉬운 것 같아서, 위에서 언급한 컴파일 오류에 대해서 추가 포스팅을 해보려고 합니다!

컴파일 오류의 장점

  1. 런타임 이전 문제 발견 : 운영 환경에서 발생할 수 있는 오류를 방지
  2. 비용 절감 : 운영 환경이 아닌 개발에서 버그를 발견하면 비용이 적게 든다.

컴파일러가 잡을 수 있는 에러

  1. 타입 관련 오류
  2. 문법 오류
  3. 접근 제어 위반
  4. final 관련 오류

컴파일러 오류를 잘 내는 방법

1. final 키워드를 적극 활용한다!

클래스, 메서드, 변수에 final을 사용하면, 컴파일 단계에서 의도하지 않은 변경을 막을 수 있다.

2. 구체적인 타입을 사용하자

Object보다는 구체적인 타입을 사용해 컴파알 단계에서 오류를 만날 수 있다.

3. 제네릭 타입을 활용한다.

제네릭 타입을 사용하면 타입 안정성을 높일 수 있다.

    List numbers = new ArrayList(); //모든 타입이 들어가게 된다.
    List<Integer> numbers = new ArrayList<>(); //정수형 타입만 사용하게 강제된다. 
이렇게 아이템 23을 정리해봤는데요. 
사실 이제는 이런 구조의 클래스를 볼 일이 없지 않나 싶습니다.

더 안전한 코드, 믿을 수 있는 코드를 짜기 위해 노력해보겠습니다.!

해당 글은 카카오테크스터디 중에 작성한 글입니다 🙇‍♀️
제가 정리하지 않은 나머지 글은 고마운 팀원분들이 정리해주시고 계십니다 🫶

더 많은 글을 읽고 싶다면 아래 깃허브를 참고해주세요!

🔗 카카오테크 스터디의 이펙티브 자바

profile
24년도까지 프로젝트 두개를 마치고 25년에는 개발 팀장을 할 수 있는 실력이 되자!

0개의 댓글

관련 채용 정보