본문 바로가기
JAVA

Instanceof 를 사용하지 않고 자식 클래스 타입 확인하기

by 민트맛녹차 2023. 7. 16.

프로젝트 중 클래스 상속 설계를 하며 자식 클래스 타입 확인 시 생길 수 있는 상황에 대해 고민하고 정리해 보았다.

 

@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Shelter {
	...
}

@DiscriminatorValue("a")
public class AShelter extends Shelter {
	...
}

@DiscriminatorValue("b")
public class BShelter extends Shelter {
	...
}

추상클래스 Shelter 와 클래스 AShelter, BShelter 가 있다고 하자.

JPA 를 사용하고 AShelter, BShelter 는 Shelter 를 상속한다

 

List<Shelter> shelters = shelterService.findAll();

for(Shelter s : shelters) {
    if (s instanceof AShelter) {
    	...
    } else if (s instanceof BShelter) {
    	...
    }
    ...
}

메서드를 통해 Shelter 의 리스트를 받아온 후, Shelter 의 자식 클래스들의 타입을 확인하기 위해서는 instanceof 를 사용해야 한다.

@DiscriminatorColumn 과 @DiscriminatorValue 에 의해 Shelter 의 DB table 에서는 "DTYPE" 을 통해 자식 테이블의 종류를 확인 할 수 있다. 하지만 해당 속성은 native query 를 사용하지 않고는 JAVA 내에서 사용할 수 없다.

 

위의 상황에서 instanceof 를 사용하는 것이 확장성면에서 좋지 않다고 생각해 구글링을 해 보았는데, instanceof 를 지양해야 한다는 글들이 있었고 그 이유는 다음과 같았다.

  • 캡슐화
  • OCP
  • SRP
  • 성능
  • 구현 및 리팩토링

자세한 이유는 Tecoble 에서 확인 가능하다. 그렇다면 instanceof 를 사용하지 않고 어떻게 구현해야 할까?

다형성을 사용해 문제를 해결할 수 있다고 한다. 추상클래스에 타입을 확인하는 추상메서드를 추가하는 것이다. 위의 예시에 적용하면 다음과 같다.

@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Shelter {
	...
    
    public abstract boolean isAShelter();
    public abstract boolean isBShelter();
}

@DiscriminatorValue("a")
public class AShelter extends Shelter {
	...
    
    @Override
    public boolean isAShelter() {
    	return true;
    }
    
    @Override
    public boolean isBShelter() {
    	return false;
    }
}

@DiscriminatorValue("b")
public class BShelter extends Shelter {
	...
    
    @Override
    public boolean isAShelter() {
    	return false;
    }
    
    @Override
    public boolean isBShelter() {
    	return true;
    }
}

 

위의 방법도 좋은 방법이지만, isXXX 같은 boolean 형 추상메서드를 사용한다면 자식 클래스가 하나 더 생길 때마다 너무 많은 수정사항이 생긴다. 예를 들어 CShelter 가 추가된다면, Shelter, AShelter, BShleter 에 isCShelter 메서드를 추가해야 한다. 

 

그래서 Enum 을 반환하는 추상메서드를 적용하기로 결정했다.

public enum ShelterType {
    A_SHELTER, B_SHELTER
}

Shelter 의 종류를 뜻하는 Enum 을 생성한 후

@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Shelter {
	...
    
    public abstract ShelterType getShelterType();
}

@DiscriminatorValue("a")
public class AShelter extends Shelter {
	...
    
    @Override
    public ShelterType getShelterType() {
    	return ShelterType.A_SHELTER;
    }
}

@DiscriminatorValue("b")
public class BShelter extends Shelter {
	...
    
    @Override
    public ShelterType getShelterType() {
    	return ShelterType.B_SHELTER;
    }
}

추상클래스 Shelter 에 ShelterType 을 반환하는 getShelterType 이라는 추상메서드를 선언하고, 자식 클래스들은 getShelterType 을 Enum 을 사용해 오버라이드한다. 이런 방식이면 새로운 CShelter 가 생겨도 기존 클래스들의 수정 없이 CShelter 만 getShelterType 메서드를 오버라이드하면 된다. 

 

 

참조
https://tecoble.techcourse.co.kr/post/2021-04-26-instanceof/
 

댓글