타입 체크를 지양하자

14977 단어 JavaJava

체스 미션을 진행하면서 타입체크, 캐스팅 코드를 작성했습니다.
해당 코드를 쓰면서도 사실 확신이 없어서 리뷰를 받고 싶었는데, 리뷰어 분께서 타입 체크 및 캐스팅은 객체지향적이지 않다라는 리뷰를 주셔서, 왜 타입 체크와 캐스팅이 객체지향적이지 않은지에 대해서 알아보려고 합니다.


1. 과도한 책임

Piece : Board야 그거 내가 할 일 같은데 왜 니가해?
Board : 그니까 힘들어죽겄어;;

기물(Piece)에게 “너 프로모션 할 수 있어?”라고 물어보는 것이 아닌,
기물을 관리하는 객체가 타입 체크를 통해 기물의 종류를 확인하고 프로모션 가능 여부를 반환하고 있습니다.

기물이 지녀야 할 프로모션 가능 여부를, 기물들을 관리하는 Board가 지니기 때문에, Board가 과도한 책임을 지닙니다.

결국, 타입 체크의 목적은 타입 체크 결과를 통해 어떤 동작을 수행할지 결정하고 수행하기 위함입니다.
그렇다면, 타입 체크를 행하는 객체가 결과를 통해 어떤 동작을 수행하기 때문에,
해당 객체에 과도한 책임이 몰리고, 적절한 책임 분배가 되지 않는 것입니다.

2. 캡슐화 저해

private static final Map<Class<? extends Piece>, String> symbolsByPiece = Map.of(
        King.class, "k", Queen.class, "q",
        Bishop.class, "b", Knight.class, "n",
        Rook.class, "r", Pawn.class, "p"
);

private void printPieceSymbol(final Piece piece) {
	final String symbol = symbolsByPiece.get(piece.getClass());
	System.out.print(symbol);
}

기물에 따른 symbol을 각 기물들에서 관리하는 것이 아니라, 외부에서 관리하고 있습니다.

이보다는 아래 처럼, 각 기물들이 symbol을 관리하고
기물에게 메시지를 던지는 것이 캡슐화 측면에서 낫다고 생각합니다.

public class King {

	private static final String SYMBOL = "k";

	public String getSymbol() {
		return SYMBOL;
	}
}

public class Queen {

	private static final String SYMBOL = "q";

	public String getSymbol() {
		return SYMBOL;
	}
}
private void printPieceSymbol(final Piece piece) {
	final String symbol = piece.getSymbol();
	System.out.print(symbol);
}

3. 변경에 취약하다.

public abstract class Animal {
		
}

public class Tiger extends Animal {

}

public class Bear extends Animal {

}

public class Giraffe extends Animal {

}
public void getSpecies(final Animal animal) {
	if (animal instanceof Tiger) {
		return "tiger";
	} else if (animal instanceof Bear) {
		return "bear";
	}
	return "giraffe";
}

위와 같이 instanceof를 통해 로직이 구현되는 상황에서, 다른 동물들이 추가 되면 어떻게 될까요?

동물의 종 정보를 필요로 하는 모든 곳에 분기 문들이 추가되어야 합니다. 또한, 분기문들이 많아져서 코드의 복잡도 역시 증가하겠죠.

반면, instanceof를 사용하지 않고, 해당 기능을 Animal에 추상화하고 자식 객체들이 이를 구현하도록 하면
단순히, 새로운 동물 객체를 작성만 해주면 됩니다.

public abstract class Animal {
	String getSpecies();
}

public class Tiger extends Animal {

	public String getSpecies() {
		return "tiger"
	}
}

public class Bear extends Animal {

	public String getSpecies() {
		return "bear"
		
}

public class Giraffe extends Animal {

	public String getSpecies() {
		return "giraffe"
	}
}

//새로운 동물을 작성하기만 하면된다.
public class Giraffe extends Animal {

	public String getSpecies() {
		return "giraffe"
	}
}
public void getSpecies(final Animal animal) {
	final String species = animal.getSpecies();
}

사실 이 글을 작성하면서, 대체 내가 왜 instanceof를 사용했지 라는 생각이 들었습니다.

위와 같은 문제가 있으니, instanceof 사용을 지양하고 추상화하는 것이 좋다고 생각합니다.

끗.

좋은 웹페이지 즐겨찾기