독서이야기/엘레강트 오브젝트 - 새로운 관점에서 바라본 객체지향

[엘레강트 오브젝트] 3.7 인스토스펙션과 캐스팅을 피하세요

사랑꾼이야 2023. 3. 22. 23:42
반응형

이 내용은 엘레강트 오브젝트 를 읽으면서 정리한 내용을 포함하고 있습니다.

  • 들어가기
  • 타입 인트로스펙션(introspection)
  • 캐스팅(casting)
  • 정리
  • 관련해서 생각해볼 내용

들어가기

타입 인트로스펙션(introspection)과 캐스팅(casting)을 사용하고 싶은 유혹에 빠지더라도 절대로 사용해서는 안된다.

기술적으로 Java의 instanceof 연산자와 Class.cast() 메서드, 다른 언어에서 동일한 기능을 수행하는 연산자들이 모두 이 범주에 포함된다.

public <T> int size(Iterable<T> items) {
    if (items instanceof Collection) {
        return Collection.class.cast(items).size();
    }
    int size = 0;
    for (T item : items) {
        ++size;
    }
    return size;
}
  • 타입 인스로스펙션은 리플렉션이라는 더 포괄적인 용어로 불림

타입 인트로스펙션(introspection)

타입 인트로스펙션(introspection)을 사용하면 메소드, 명령어, 구문, 클래스, 객체, 타입 등을 변경할 수 있다.

  • CPU가 이 요소들에 접근하기 전에 쉽고 간단하게 코드를 수정할 수 있다.
  • 리플렉션은 매우 강력한 기법이지만 동시에 코드를 유지보수하기 어렵게 만드는 매우 너저분한 기법이다.
  • 코드가 런타임에 다른 코드에 의해 수정된다는 사실을 항상 기억해야 한다면, 코드를 읽기가 매우 어려울 것이다.
  • 런타임에 객체의 타입을 조사(introspect)하는 것은 클래스 사이의 결합도가 높아지기 떄문에 기술적인 관점에서도 좋지 않다.

더 나은 설계는 다음과 같다.

  • 메소드 오버로딩을 통하여 코드를 개선
public <T> int size(Collection<T> items) {
    return items.size();
}
public <T> int size(Iterable<T> items) {
    int size = 0;
    for (T item : items) {
        ++size;
    }
    return size;
}

캐스팅(casting)

return Collection.class.cast(items).size();

동일한 수행 코드를 다음과 같이 작성할 수 있다.

return ((Collection) items).size();

기본적으로 두 코드는 거의 동일하게 동작한다.

  • 최종 결과는 items 객체가 Collection인 사실이다.
  • 이것을 예로 들면, 배관공에게 저는 당신이 컴퓨터 전문가라고 가정하고 있습니다. 그러니 이 컴퓨터를 수리해주세요. 라고 이야기 하는 것과 흡사하다.
if (items instanceof Collection) {
    return ((Collection) itens).size();
}
  • 만약 당신이 컴퓨터 전문가이기도 하다면, 이 프린터를 고쳐 주세요. 라고 말하는 듯이 보인다.

다시 말해서, 방문한 객체에 대한 기대(expectation)를 문서에 명시적으로 기록하지 않은 채로 외부에 노출해버린 것이다. 어떤 클라이언트를 여러분이 기대하는 바를 학습한 후 더 적절한 객체를 제공하겠지만 어떤 클라이언트는 그럴 수 없을 것이다. 클라이언트와 객체 사이의 불명확하고, 은폐되고, 암시적인 관계를 유지보수성에 심각한 영향을 끼친다.

정리

  • instanceof 연산자를 사용하거나 클래스를 캐스팅하는 일은 안티 패턴이기 때문에 사용해서는 안된다.

관련해서 생각해볼 내용

반응형