3.3 인자의 값으로 NULL을 절대 허용하지 마세요
코드 어딘가에 NULL이 존재하면 우리는 커다란 실수를 저지르고 있다?
- NULL을 사용하는 편이 더 편리하고 경제적인 접근방법 처럼 보일 수 있고, 논리적이라고 생각할 수 있겠지만 각각의 객체가 자신의 행동을 온전히 책임진다는 객체 패러다임과는 상반되는 아이디어라고 할 수 있다.
public Iterable<File> find(String mask) { if (mask == null) { // 모든 파일을 찾는다. } else { // 마스크를 사용해서 파일을 찾는다. } } // 개선코드 1 public Iterable<File> find(Mask mask) { if(mask.empty()) { // 모든 파일을 찾는다. } else { // 마스크를 사용해서 파일을 찾는다. } } // 개선코드 2 public Iterable<File> find(Mask mask) { Collection<File> files = new LinkedList<>(); for (File file : //모든파일) { if(mask.matches(file)) { file.add(file); } } return files; }
- Mask 객체를 우리가 존중했다면 Mask 객체 스스로 조건존재의 여부를 결정하게 했을 것이다.
- 겉모습만으로 객체를 판단해서는 안된다. “진짜"객체라면 대화에 응할 것이며 NULL이면 대응하지 않겠다는 식으로 객체와 의사소통해서는 안된다.
- 인자 값으로 NULL을 허용하면 mask==null과 같은 비교문을 사용할 수 밖에 없고, 객체와 협력할 때마다 객체의 “실체"를 확인하는 것 말고는 NULL인지 여부를 판단할 수 있는 방법이 없다.
- 또한 NULL 여부를 체크함으로써 객체가 맡아야 하는 상당량의 책임을 빼앗게 된다.
결론
- 이 부분은 단지 NULL을 체크해서 방지하지말고 NULL을 빨리 찾아서 대응해서 NULL참조가 없는 코드를 만들어 나가라는 말을 하는 것 같다.
3.4 충성스러우면서 불변이거나, 아니면 상수이거나
- 세상은 가변적 이지만 그렇다고 해서 불변객체로 세상을 모델링 할 수 없다는 것은 아니다.
- 상태와 개념에 대해 짚고 넘어갈 필요가 있다.
class WebPage { private final URI uri; WebPage(URI path) { this.uri = path; } public String content() { // HTTP GET 요청을 전송해서 // 웹 페이지의 컨텐츠를 읽은 후 // 읽혀진 컨텐츠를 UTF-8 문자열로 반환한다. } }
- 불변객체를 상수처럼 동작하는 것 처럼 보인다.
- 일단 클래스를 만들고 항상 같은 값만 반환하기 때문이다. 즉 예측이 가능함
- 하지만 이 그림은 불완전한 그림이다. 상수처럼 동작하는 객체는 단지 불변성의 특별한 경우이기 때문이다.
- 불변객체는 그 이상이다. 비록 content() 메서드의 결과를 예측할 수 없더라도 WebPage는 불변 객체에 속한다.
- WebPage 클래스는 실제 웹 페이지와 통신하기 때문에 우리는 이 객체가 무엇을 돌려줄지 알 지 못한다.
- 이런 측면에서 WebPage가 String 클래스와 다르지만 WebPage 역시 String처럼 불변이다.
- 객체의 행동을 예측할 수는 없지만 그래도 이 객체는 불변이다. 결과가 변하기 때문에 상수는 아니지만 객체가 대표하는 엔티티에 충성하기 때문이다.