프로젝트 중 클래스 상속 설계를 하며 자식 클래스 타입 확인 시 생길 수 있는 상황에 대해 고민하고 정리해 보았다.
@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/
댓글