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

[엘레강트 오브젝트] 3.3 인자의 값으로 NULL을 절대 허용하지 마세요

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

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

  • NULL
  • NULL 처리 방법
  • 정리
  • 관련해서 참고해볼 내용

코드 어딘가에 NULL이 존재한다면 커다란 실수를 저지르고 있는 것이라고 말한다. 사실, 전달할 객체가 없으므로 값이 없는 것으로 간주하세요 라는 의사를 표현할 수 있도록 사용자에게 진짜 객체 대신 NULL을 전달하도록 허용하는 것은 일반적인 방법이다.

NULL

NULL을 허용하는 find() 메서드를 구현하기 위해서는 다음과 같이 분기를 처리할 필요가 있습니다.

public Iterable<File> find(String mask) {
    if (mask == NULL) {
        // 모든 파일을 찾는다
    } else {
        // 마스크를 사용해서 파일을 찾는다
    }
}

객체를 존중한다면 다음과 같이 변경해야 한다.

public Iterable<File> find(Mask mask {
    if (mask.empty()) {
        // 모든 파일을 찾는다
    } else {
        // 마스크를 사용해서 파일을 찾는다
    }
}

더 개선한 코드는 다음과 같다.

public Iterable<File> find(Mask mask) {
    Collection<File> files = new LinkedList<>();
    for (File file : /* 모든 파일 */) {
        if (mask.matches(file)) {
            files.add(file);
        }
    }
    return files;
}
  • mask 객체를 존중했다면 조건의 존재 여부를 객체 스스로 결정하게 해야 한다.

인자의 값으로 NULL을 허용하면 mask == NULL 과 같은 비교문을 사용할 수 밖에 없다.

  • 객체와 협력할 때마다 객체의 실체를 확인하는 것말고는 NULL 인지 여부를 판단할 수 있는 방법이 없다.
  • NULL 여부를 체크함으로써 객체가 맡아야 하는 상당량의 책임을 빼앗게 된다.

NULL 처리 방법

OOP에서 존재하지 않는 인자 문제는 널 객체(null object)를 이용해서 해결해야 한다.

  • 전달할 것이 없다면, 비어있는 것처럼 행동하는 객체를 전달하면 된다.
  • 전달한 인자가 객체인지 NULL인지를 확인하는 점을 메소드 구현자에게 떠넘겨서는 안된다.
  • 대신 항상 객체를 전달하되, 전달한 객체에게 무리한 요청을 한다면 응답을 거부하도록 객체를 구현해야 한다.

클라이언트가 여전히 NULL을 전달한다면 어떻게 해야 할까?

  1. 방어적인 방법으로 여기에서는 NULL을 체크한 후 예외를 던진다.
public Iterable<File> find(Mask mask) {
 if (mask == null) {
     throw new IllegalArgumentException(
         "Mask can't be NULL; please provide an object.");
 }
 // 마스크를 사용해서 파일을 찾아 반환한다
}
  1. 개인적으로 선호하는 방법으로 NULL을 무시하는 것이다.
  • 여기에서는 인자가 절대 NULL이 아니라고 가정하고 어떤 대비도 하지 않는다.
  • 메서드를 실행하는 도중에 인자에 접근하면 NullPointerException이 던져지고 메서드 호출자는 자신이 실수했다는 사실을 인지하게 될 것이다.

정리

  • 중요하지 않은 NULL 확인 로직으로 코드를 오염시켜서는 안된다.
  • 메서드 인자로 절대 NULL을 허용하지 말아라.

관련해서 참고해볼 내용

Optional 을 통해서 표현하는 방법 또한 알아놓으면 좋다.

반응형