이번에 체스미션을 배카라와 같이 구현했다. Piece 추상클래스를 상속받은 체스말들을 클래스로 만들어서 관리를 했는데 Pawn인지 확인하는 메서드를 구현하다가 고민이 생겼다.
public boolean isPawn() {
return getClass() == Pawn.class;
}
public abstract boolean isPawn();
getClass를 이용해서 Pawn과 같은 클래스인지 확인하면 상속받은 모든 기물에서 구현할 필요가 없지만, abstract method를 쓰게되면 모든 기물에서 구현해야한다. 객체에서 메세지를 전해준다는 의미에서는 아래 코드가 맞아보이지만 뭔가 비효율적이다는 생각을 해서 리뷰어에게 물어보고 공부하게 되었다.
객체의 특성 중 캡슐화는 상태나 행위를 외부에서 사용하지 못하도록 숨기는 것을 의미한다. 하지만 instanceOf()
또는 getClass()
를 사용하게 되면 캡슐화가 보장되지 않는다. 외부의 객체가 각 객체가 어떤 타이빈지 알 수 있기 때문이다. getClass()
혹은instanceOf()
를 지양해야하는 가장 우선적인 이유다.
객체는 확장에 열려있고, 변화에는 닫혀있어야 한다. Piece를 상속받는 Queen 객체를 새로 생성하게 되면 instanceOf()
혹은 getClass()
를 사용하던 메서드를 변경해야할 수도 있다.
public int getScore() {
if (getClass() == King.class) {
return 0;
}
if (getClass() == Pawn.class) {
return 1;
}
return -1;
}
점수 구하는 메서드를 Piece에서 구현하면, Queen 객체가 추가됐을 때 해당 메서드를 수정해야하므로 OCP에 위배된다.
객체는 하나의 책임만 가져야 한다. isPawn()
을instanceOf()
를 통해 구현하게되면 Piece의 메서드가 모든 객체의 책임을 가져가게된다. 따라서 Piece가 책임을 위임받는 격이 되어 단일책임원칙에 위배된다.
@Test
void instanceOf() {
Pawn pawn = new Pawn(Team.BLACK);
long start = 0;
long end = 0;
start = System.nanoTime();
assertThat(pawn instanceof Pawn);
end = System.nanoTime();
System.out.println("instanceOf = " + (end - start));
start = System.nanoTime();
assertThat(pawn.isPawn());
end = System.nanoTime();
System.out.println("isPawn = " + (end - start));
}
실제로 성능을 비교해봐도 다형성을 이용해 구현하는게 훨씬 빠르다.
instanceOf()
를 쓰지말자.
- 객체 지향적이지 않다.
- 성능도 좋지 않다.
instanceOf()
를 사용하면 중복된 메서드가 줄어서 마치 효율적으로 보이지만 실제로 성능도 좋지 못하고 객체지향적이지 못하기 때문에 유지보수에도 불리하다. 자바는 객체 지향 언어이기 때문에 그에 맞도록 개발하자!