Category Archives: Patterns

Null Object Pattern

Motivation

대부분의 객체지향 언어에서 기본적으로 제공되는 널(null)이란 개념은 여러 단점을 수반한다. 널은 참조 변수(포인터를 저장할 공간)의 값이 할당되지 않은 상태를 나타내기 위해, ‘값이 존재 하지 않는’ 상태를 나타내는 일종의 ‘값’으로써 기능하며, 대상 타입에 상관없이 모든 참조 변수에 할당될 수 있다. 여기서 발생하는 개념적 불일치성 때문에, 개발자는 메소드의 반환 값이나 참조 변수의 값이 널일 수 있다는 가능성을 별도로 처리해야만 한다. 그 결과, 코드는 필요 이상으로 복잡해지고 널 값을 제대로 처리하지 못한 코드는 런타임에 오류를 발생시킨다. Objective-C나 스칼라와 같은 언어는 이런 단점을 극복하기 위해 ‘널 객체’라는 개념을 도입해, 참조 변수에 항상 ‘객체’가 저장되도록 일관성을 유지해준다.

Structure

‘해당 타입의 객체가 없음’이라는 의미를 나타내기 위해 ‘널 객체’를 도입한다. 널 객체는 대상 타입의 서브클래싱을 통해 정의하며, 대상 타입의 참조 변수에 널 값의 할당이 필요한 경우를 대체한다. 서브클래싱을 바탕으로 대상 타입의 메소드는 재정의되며, 이 때 널 객체에 재정의된 메소드의 행위는 ‘대상 타입이 없음’를 의미하도록 구현한다(구현 내용을 비우는 것도 한 가지 옵션이 아닐까).

Example

그루비로 작성된 예제코드를 살펴보자.

class Job {
    def salary
}

class Person {
    def name
    def Job job
}

여기서 다음과 같이 Job을 초기화한 후에 biggestSalary를 호출하면, job의 값이 널이기 때문에 NullPointerException이 반환된다.

people << new Person(name:'Harry')
def biggestSalary = people.collect{ p -> p.job.salary }.max()
println biggestSalary

Person 타입에 해당하는 널 객체를 새롭게 정의하고 활용하면, 이런 문제 상황을 구조적으로 해결하며 널 값의 사용을 제거할 수 있다.

class NullJob extends Job { def salary = 0 }

people << new Person(name:'Harry', job:new NullJob())
biggestSalary = people.collect{ p -> p.job.salary }.max()
println biggestSalary

Reference

  • The Null Object Pattern
    • 널 객체 패턴을 정리한 문서다.
  • Null Object Pattern
    • 그루비 블로그에서 설명하고 있는 널 객체 패턴으로 이 글에서 참조한 예제를 설명하고 있다. 그루비의 오퍼래이터 중 하나인 ‘?’를 함께 설명하고 있어서 흥미롭다.