체스 미션을 구현하던 중 pawn의 특이한 성질 때문에 체스판의 기물이 pawn인지 확인해야 하는 로직이 필요했다.
그때 고민이 생겼는데
- 추상 클래스인 Piece에 추상 메서드인 isPawn()을 만들어 pawn인지 확인한다.
- instanceof를 사용해 Piece구현체들이 pawn인지 확인한다.
처음에 두 방법이 생각났고 얼마전에 알게된 instanceof를 쓰는 방법이 클래스의 메서드도 줄일 수 있을 뿐더러 더 고급진 방법이라는 생각이 들었다.
그래서 2번 방법으로 구현해서 리뷰요청을 했다.
하지만 결과는.....
위와 같은 피드백을 받고 말았다...🥲👍🏻
추상화가 제대로 된건지 의심?? 추상화를 했으니까 instanceof를 쓸 수 있는거 아닌가요???
어쨋든 그래서 instanceof를 왜 지양해야 하는지에 대해 조사해보게 되었다.
: 부모를 상속해서 만들어진 자식 객체가 여러 타입인 경우에 특정 클래스가 맞는지 확인하기 위해 사용될 수 있다.
if (!(piece instanceof Nothing)) {
cells.put(position, piece);
}
다음과 같이 Piece를 상속하는 Piece 구현체들이 Nothing 인지 확인해서 분기처리 해줄 수 있다.
instanceof를 사용하지 않는다면
public abstract class Piece {
public abstract boolean isNothing();
}
public final class Pawn extends Piece {
@Override
public boolean isNothing() {
return false;
{
}
public final class Nothing extends Piece {
@Override
public boolean isNothing() {
return true;
{
}
이런식으로 다형성을 이용해 부모의 abstract 메서드를 오버라이딩해서 메서드를 정의해 줘야 한다.
다형성을 이용하면 class에 메서드가 하나 더 추가되는 꼴인데 그럼 왜 instanceof를 쓰지 말라는 건지 알아보자.
캡슐화란 외부에서 객체의 상태나 행위를 보지 못하도록 내부에서 객체의 상태와 속성을 숨기는 것을 뜻한다. 객체지향에서는 각 객체가 자신의 책임과 역할을 독립적으로 수행하는 것이 중요하기 때문에 캡슐화가 깨지는 건 큰 문제가 될 수 있다.
instanceof를 사용하면 외부의 객체에서 해당 객체가 어떤 타입인지를 알 수 있기 때문에 캡슐화가 깨지게 된다. instanceof를 지양해야 하는 첫번째 이유이다.
pawn과 관련된 로직을 체스판안에 있는 기물들을 관리하는 Board에서 책임지는게 맞을까? 아님 Piece에서 추상화하여 책임지는게 맞을까?
public final class Board {
...
public void checkPawn(final Piece piece) {
if(piece instanceof Pawn) {
...
{
{
...
}
public abstract class Piece {
public abstract void checkPawn(final Position source, final Position target, final Direction direction);
}
public final class Rook extends Piece {
@Override
public void checkPawn(final Position source, final Position target, final Direction direction) {
return;
{
}
public final class Pawn extends Piece {
@Override
public void checkPawn(final Position source, final Position target, final Direction direction) {
if (source.isTwoRankDifference(target)) {
throw new IllegalArgumentException("폰은 초기 위치일 때만 두 칸이동 가능합니다.");
}
...
{
}
1번 방법은 Piece에게 너 pawn이야? 그럼 이거 확인 해줘. 라고 물어보는 대신 그저 Piece들을 관리하는 Board 객체에서 적절하지 않은 책임을 담당하고 있다. 각 타입에게 책임을 부여하면 되는 일을 하나의 메서드에서 과도하게 담당하고 있다. 이는 Single Responsibility Principle(단일 책임원칙)에 위배된다고 볼 수 있다.
instanceof가 눈앞에 놓인 상황에서는 확실히 편하고 유용해 보일 수 있다. 하지만 객체지향의 관점에서 봤을 때, 유지보수와 기능의 확장면에서 instanceof의 사용은 지양되어야 함을 알게 되었다.
객체지향적인 설계를 위해 instanceof의 사용을 가급적 하지 말자!!