Null 객체 패턴

Null object pattern

객체 지향 컴퓨터 프로그래밍에서 null 객체는 참조 값이 없거나 정의된 중립(null) 동작을 가진 객체입니다.이러한 객체의 용도와 그 동작(또는 그 결여)을 나타내는 늘 오브젝트 디자인 패턴은 처음에 "Void [1]Value"로 출판되었고, 나중에 "Null Object"[2][year needed]로 프로그램 디자인 패턴 언어 북 시리즈에 발표되었습니다.

동기

Java 나 C#대부분의 객체 지향 언어에서는 참조늘일 수 있습니다.보통 늘 참조에서는 메서드를 호출할 수 없기 때문에 메서드를 호출하기 전에 이들 참조가 늘이 아닌지 확인해야 합니다.

Objective-C 언어는 이 문제에 대해 다른 접근방식을 취하며 메시지를 보낼 때 아무 작업도 하지 않습니다.nil; 반환값이 예상되는 경우,nil(객체의 경우), 0(숫자의 경우),NO(용)BOOL값) 또는 모든 부재가 초기화된 구조물(구조물 유형의 경우)null/0/NO/zero-initialized structure가 [3]반환됩니다.

묘사

오브젝트(존재하지 않는 고객 등)의 부재를 전달하기 위해 늘 참조를 사용하는 대신 예상되는 인터페이스를 구현하지만 메서드 본문이 비어 있는 오브젝트를 사용합니다.디폴트 실장에 비해 이 접근방식의 장점은 null 객체는 예측성이 뛰어나고 부작용이 없다는 것입니다.즉, 아무것도 하지 않습니다.

예를 들어 함수는 폴더의 파일 목록을 검색하여 각각에 대해 몇 가지 작업을 수행할 수 있습니다.폴더가 비어 있는 경우 예외가 발생하거나 목록이 아닌 늘 참조를 반환하는 응답이 있을 수 있습니다.따라서 목록을 기대하는 코드는 계속하기 전에 실제로 목록이 있는지 확인해야 하므로 설계가 복잡해질 수 있습니다.

대신 null 객체(즉, 빈 목록)를 반환함으로써 반환값이 실제로 목록인지 확인할 필요가 없습니다.호출 함수는 단순히 정상적으로 목록을 반복할 뿐 사실상 아무것도 하지 않습니다.단, 반환값이 늘 오브젝트(빈 리스트)인지 여부를 확인하고 필요에 따라 다르게 반응할 수 있습니다.

데이터베이스 등의 특정 기능을 테스트에 사용할 수 없는 경우 늘 오브젝트 패턴을 테스트용 스텁으로 사용할 수도 있습니다.

다음 노드 구조를 가진 이진 트리가 지정됩니다.

클래스 노드 {노드 왼쪽 노드 오른쪽}

트리 크기 절차는 재귀적으로 구현할 수 있습니다.

함수 tree_size(노드) {return 1 + tree_size(노드.왼쪽) + tree_size(노드.오른쪽)}

하위 노드가 존재하지 않을 수 있으므로 존재하지 않거나 늘 검사를 추가하여 절차를 변경해야 합니다.

함수 tree_size(node) { set sum = 1(node.left가 존재하는 경우) {sum = sum + tree_size(node.left)} node.right가 존재하는 경우 {sum = sum + tree_size(node.right)} 반환 합계 }

그러나 이 경우 경계체크를 일반 로직과 혼합하여 절차가 더욱 복잡해지고 읽기 어려워집니다.null 객체패턴을 사용하면 특수한 버전의 절차를 만들 수 있지만 null 노드에 대해서만 작성할 수 있습니다.

함수 tree_size(노드) {return 1 + tree_size(노드.왼쪽) + tree_size(노드.오른쪽)}
함수 tree_size(function_node) {return 0 }

이것에 의해, 통상의 로직과 특수한 케이스의 취급을 분리해, 코드를 이해하기 쉬워집니다.

다른 패턴과의 관계

이것은 상태 패턴과 전략 패턴의 특별한 경우로 간주할 수 있다.

는 디자인 패턴의 패턴이 아니라 Martin Fowler's Refactoring[4] 및 Joshua Kerievsky의 Refactoring To[5] Patterns에서 Insert Null Object Refactoring으로 언급되어 있습니다.

Robert Cecil Martin의 Agile Software Development: 원칙, 패턴 및[6] 프랙티스는 패턴에 전념하고 있습니다.

대체 수단

C# 6.0 에서는, 「」를 사용할 수 있습니다." 연산자(일명 null-conditional 연산자). 왼쪽 피연산자가 null일 경우 단순히 null로 평가됩니다.

// 콘솔 어플리케이션으로 컴파일(C# 6.0 이후 필요) 사용. 시스템.;  네임스페이스 Console Application 2 {     학급 프로그램.     {         정적인 무효 주된(스트링[] args)         {             스트링 스트레이트 = '테스트";              콘솔.기입선(스트레이트?.길이);             콘솔.읽기 키();         }     } } // 출력은 다음과 같습니다. // 4 

확장 메서드 및 Null 병합

일부 Microsoft 에서는.NET 언어, 확장 메서드를 사용하여 이른바 '늘 병합'을 수행할 수 있습니다.이는 확장 메서드가 '인스턴스 메서드 호출'에 관련된 것처럼 null 값으로 호출될 수 있지만 실제로는 확장 메서드가 정적이기 때문입니다.확장 메서드를 사용하여 늘 값을 체크할 수 있으므로 이를 사용하는 코드가 불필요해집니다.다음의 예에서는, 에러 없는 호출보증하기 위해서, C# Null 머지 연산자를 사용하고 있습니다.이 경우, 보다 일반적인 if...then...else 를 사용할 수도 있습니다.다음 예시는 null의 존재를 고려하지 않거나 null 문자열과 빈 문자열을 동일하게 취급하는 경우에만 작동합니다.다른 응용 프로그램에서는 이 가정이 적용되지 않을 수 있습니다.

// 콘솔 애플리케이션으로 컴파일(C# 3.0 이후 필요) 사용. 시스템.; 사용. System.Linq; 네임스페이스 마이 익스텐션예시 포함 {     일반의 정적인 학급 String Extensions(문자열 확장) {          일반의 정적인 인트 SafeGetLength(이것. 스트링 값 Or Null) {              돌아가다 (값 Or Null ?? 스트링.).길이;          }     }     일반의 정적인 학급 프로그램. {         // 일부 문자열 정의         정적인 읽기 전용 스트링[] 줄들 = 신규 [] { "X씨", '캐트리엔 덕', 무효, 'Q' };         // 배열에 포함된 모든 문자열의 총 길이를 기록합니다.         일반의 정적인 무효 주된(스트링[] args) {             변화하다 질문하다 = 부터 본문  줄들 선택한다. 본문.SafeGetLength(); // 여기서 체크할 필요가 없습니다.             콘솔.기입선(질문하다.());         }     } } // 출력은 다음과 같습니다. // 18 

다양한 언어로

C++

오브젝트에 대한 참조를 스태틱하게 입력한 언어는 늘 오브젝트가 어떻게 더 복잡한 패턴이 되는지 보여줍니다.

#실패하다 <iostream>  학급 동물 {  일반의:   가상 ~동물() = 체납;    가상 무효 사운드 만들기() 컨스턴트 = 0; };  학급  : 일반의 동물 {  일반의:   가상 무효 사운드 만들기() 컨스턴트 덮어쓰다 { 표준::외치다 << > "우우우우~" << > 표준::; } };  학급 특수 동물 : 일반의 동물 {  일반의:   가상 무효 사운드 만들기() 컨스턴트 덮어쓰다 {} }; 

여기서, 아이디어는 포인터 또는 참조가 있는 상황이 있다는 것입니다.Animal개체가 필요하지만 사용할 수 있는 적절한 개체가 없습니다.표준 적합 C++에서는 null 참조가 불가능합니다.무효Animal*포인터가 가능하며 플레이스 홀더로 유용하지만 직접 디스패치에는 사용할 수 없습니다.a->MakeSound()정의되지 않은 동작입니다.a는 늘 포인터입니다.

null 객체 패턴은 특별한 기능을 제공하여 이 문제를 해결합니다.NullAnimal인스턴스화할 수 있는 클래스Animal포인터 또는 참조.

null 객체를 갖는 클래스 계층마다 특별한 null 클래스를 생성해야 합니다.NullAnimal필요한 것이 일부에 관한 null 오브젝트일 경우 아무 소용이 없습니다.Widget에 관련되지 않은 베이스 클래스Animal계층.

"anything is reference"(Java 및 C# 등)가 있는 언어와는 대조적으로 늘 클래스를 전혀 갖지 않는 것이 중요합니다.C++에서는 함수 또는 메서드의 설계는 null이 허용되는지 여부를 명시적으로 나타낼 수 있습니다.

// 동물 인스턴스가 필요하며 null을 허용하지 않는 함수입니다. 무효 어떻게 좀 해봐.(컨스턴트 동물& 동물성) {   // 여기서 동물은 null이 아닐 수 있습니다. }  // Animal 인스턴스 또는 null을 받아들일 수 있는 함수입니다. 무효 어떻게 좀 해봐.(컨스턴트 동물* 동물성) {   // animal은 null일 수 있습니다. } 

C#

C#은 Null 객체 패턴을 적절히 구현할 수 있는 언어입니다.이 예에서는 사운드를 표시하는 동물 개체와 C# null 키워드 대신 사용되는 NullAnimal 인스턴스를 보여 줍니다.null 객체는 일관된 동작을 제공하며 대신 C# null 키워드를 사용한 경우 발생하는 런타임 늘 참조 예외를 방지합니다.

/* Null 객체 패턴 구현: */ 사용. 시스템.;  // 동물 인터페이스는 아래의 동물 구현에 대한 호환성의 핵심입니다. 인터페이스 IAnimal {  무효 사운드 만들기(); }  // 동물이 베이스 케이스입니다. 추상적인 학급 동물 : IAnimal {  // 비교에 사용할 수 있는 공유 인스턴스  일반의 정적인 읽기 전용 IAnimal 특수한 순서 = 신규 특수 동물();    // Null Case: C# null 키워드 대신 이 Null Animal 클래스를 사용해야 합니다.  사적인 학급 특수 동물 : 동물  {   일반의 덮어쓰다 무효 사운드 만들기()   {    // 의도적으로 동작을 제공하지 않습니다.   }  }  일반의 추상적인 무효 사운드 만들기(); }  // 개는 진짜 동물이다. 학급  : 동물 {  일반의 덮어쓰다 무효 사운드 만들기()  {   콘솔.기입선("우~");  } }  /* ========================= * 메인 엔트리 포인트의 간단한 사용 예. */ 정적인 학급 프로그램. {  정적인 무효 주된()  {   IAnimal  = 신규 ();   .사운드 만들기(); // 출력 "Woof!"    /* C# null 대신 Animal을 사용합니다.Null 인스턴스입니다. * 이 예는 단순하지만 동물의 경우라는 생각을 전달합니다.Null 인스턴스가 사용된 후 프로그램이 사용됩니다. *은(는) 발생하지 않습니다.NET 시스템실행 시 NullReferenceException(C#null을 사용한 경우와 달리 NullReferenceException). */   IAnimal 알 수 없는 = 동물.특수한 순서;  //<< 치환:IAnimal unknown = null;   알 수 없는.사운드 만들기(); // 아무것도 출력하지 않지만 런타임 예외를 발생시키지 않음  } } 

스몰토크

Smalltalk 원칙에 따르면, 모든 것은 객체이며, 객체의 부재는 그 자체로 객체라고 불리는 객체에 의해 모델링됩니다.nil예를 들어 GNU Smalltalk에서는nilUndefinedObject의 직계 후손인Object.

목적상 합리적인 객체를 반환하지 못한 작업은 반환될 수 있습니다.nil따라서 Smalltalk 디자이너가 지원하지 않는 "개체 없음"을 반환하는 특별한 경우를 피할 수 있습니다.이 방법은 기존의 "null" 또는 "no object" 또는 "null reference" 접근 방식보다 단순하다는 장점이 있습니다(특별한 경우가 필요 없음).에서 사용하는 특히 유용한 메시지nil이다isNil,ifNil:또는ifNotNil:이를 통해 에 대한 참조를 실용적이고 안전하게 처리할 수 있습니다.nil스몰토크 프로그램에서.

일반적인 리스프

Lisp에서는 함수가 특별한 오브젝트를 정상적으로 받아들일 수 있습니다.nil애플리케이션 코드의 특수 케이스 테스트의 양을 줄입니다.예를 들어, 하지만nilatom이며 필드가 없습니다.함수car그리고.cdr받아들이다nil반품하면 편리하고 코드도 짧아집니다.

부터nil 는 Lisp의 빈 리스트입니다.상기의 도입부에 기재되어 있는 상황은 존재하지 않습니다.반환되는 코드nil는, 실제로는 빈 리스트(및 리스트유형에 대한 늘 참조와 유사한 것은 없음)를 반환하고 있기 때문에, 발신자는 그 값에 리스트가 있는지 아닌지를 확인하기 위해서, 값을 테스트할 필요는 없습니다.

null 객체 패턴은 다중값 처리에서도 지원됩니다.프로그램이 값을 반환하지 않는 식에서 값을 추출하려고 하면 null 객체는nil치환됩니다.따라서(list (values))돌아온다(nil)(0을 포함한 1자리 리스트).(values)expression은 값을 전혀 반환하지 않지만 함수가 에 호출하기 때문에list는 인수식을 값으로 줄여야 합니다.null 객체는 자동으로 대체됩니다.

닫히다

Common Lisp에서 오브젝트는nil특수 클래스의 유일한 인스턴스입니다.null이것은, 방법을 특정할 수 있는 것을 의미합니다.nullnull 설계 패턴을 구현합니다.즉, 기본적으로 객체 시스템에 내장되어 있습니다.

;;; 빈개반  (디클래스  () ())  ;; 개 개체는 짖어 소리를 낸다: woof!는 표준 출력으로 출력된다. ;; (make-sound x)가 호출되었을 때 x가 dog 클래스의 인스턴스인 경우.  (디프로덕트 소리를 내다 ((obj ))   (포맷 t "우~~%"))  ;; (make-sound nero)가 null 클래스로의 전문화를 통해 동작하도록 합니다. ;; 무해한 빈 본체: 0은 아무 소리도 내지 않습니다. (디프로덕트 소리를 내다 ((obj 무효))) 

학급.null의 서브클래스입니다.symbolclass,nil는 기호입니다.부터nil또한 빈 목록을 나타냅니다.null의 서브클래스입니다.list수업도 있어요.Methods 파라미터symbol또는list따라서 을 필요로 하는 것은nil논쟁.물론입니다.null전문화는 여전히 정의될 수 있습니다.nil.

스킴

일반적인 리스프나 리스프의 많은 방언과는 달리 스킴 방언에는 이와 같이 동작하는 제로 값이 없습니다.car그리고.cdr빈 목록에는 적용할 수 없습니다.따라서 스킴어플리케이션 코드는empty?또는pair?술어 함수는 매우 유사한 Lisp가 빈 대소문자와 빈 대소문자를 구분할 필요가 없는 상황에서도 이 상황을 회피하기 위해 사용됩니다.nil.

루비

Ruby와 같은 오리형 언어에서는 예상되는 동작을 제공하기 위해 언어 상속이 필요하지 않습니다.

학급    방어하다 소리     "실패"   끝. 끝.   학급 닐애니멀   방어하다 소리(*); 끝. 끝.  방어하다 get_animal(동물)(동물성=닐애니멀.신규)   동물성 끝.  get_animal(동물)(.신규).소리  => "실패" get_animal(동물).소리  => 제로 

명시적 구현을 제공하는 대신 NilClass를 직접 monkey-patch하려고 하면 이점보다 예상치 못한 부작용이 더 많이 발생합니다.

자바스크립트

JavaScript와 같은 duck형 언어에서는 예상되는 동작을 제공하기 위해 언어를 상속할 필요가 없습니다.

학급  {   소리() {     돌아가다 '실패';   } }  학급 특수 동물 {   소리() {     돌아가다 무효;   } }  기능. get Animal (get Animal)(유형) {   돌아가다 유형 === '개' ? 신규 () : 신규 특수 동물(); }  ['개', 무효].지도((동물성) => get Animal (get Animal)(동물성).소리()); // ["bark", null]을 반환합니다. 

자바

일반의 인터페이스 동물 {  무효 소리를 내다() ; }  일반의 학급  용구 동물 {  일반의 무효 소리를 내다() {   시스템..나가..인쇄("우우우우~");  } }  일반의 학급 특수 동물 용구 동물 {  일반의 무효 소리를 내다() {                 // 침묵...  } } 

이 코드는 Java 언어를 사용한 위의 C++ 예제의 변형을 나타내고 있습니다.C++ 와 마찬가지로, null 클래스는, 에 대한 참조가 있는 상황에서 인스턴스화할 수 있습니다.Animal개체가 필요하지만 사용할 수 있는 적절한 개체가 없습니다.무효Animal오브젝트는 가능합니다(Animal myAnimal = null;플레이스 홀더로서 편리하지만 메서드 호출에는 사용할 수 없습니다.이 예에서는,myAnimal.makeSound();Null Pointer를 슬로우합니다.예외.따라서 null 객체를 테스트하려면 추가 코드가 필요할 수 있습니다.

null 객체 패턴은 특별한 기능을 제공하여 이 문제를 해결합니다.NullAnimal활자의 개체로 인스턴스화할 수 있는 클래스AnimalC++ 및 관련 언어와 마찬가지로 null 객체가 필요한 클래스 계층별로 특별한 null 클래스를 생성해야 합니다.NullAnimal필요한 것이 null 객체로 구현되지 않은 경우에는 아무 소용이 없습니다.Animal인터페이스입니다.

PHP

인터페이스 동물 {     일반의 기능. 소리를 내다(); }  학급  용구 동물 {     일반의 기능. 소리를 내다()     {         메아리치다 "우우우...\n";     } }  학급 고양이 용구 동물 {     일반의 기능. 소리를 내다()     {         메아리치다 "야옹...\n";     } }  학급 특수 동물 용구 동물 {     일반의 기능. 소리를 내다()     {         // 침묵...     } }  $Animal Type(동물형) = '실패';  기능. make Animal From Animal Type(동물유형)(스트링 $Animal Type(동물형)): 동물 {     전환하다($Animal Type(동물형)) {         사례. '개':             돌아가다 신규 ();         사례. '고양이:             돌아가다 신규 고양이();         체납:             돌아가다 신규 특수 동물();     } }  make Animal From Animal Type(동물유형)($Animal Type(동물형))->소리를 내다(); // ..귀한 동물은 아무 소리도 내지 않는다.  기능. 애니멀 Make Sound(동물 동물달러): 무효 {     동물달러->소리를 내다(); }  앞지르다 ([              make Animal From Animal Type(동물유형)('개'),              make Animal From Animal Type(동물유형)('늘애니멀'),              make Animal From Animal Type(동물유형)('고양이),          ] ~하듯이 동물달러) {     // 이렇게 하면 null 처리 코드도 줄어듭니다.     애니멀 Make Sound(동물달러); } 

Visual Basic.그물

다음 null 객체 패턴 구현은 정적 필드에서 대응하는 null 객체를 제공하는 구체적인 클래스를 보여줍니다.Empty이 접근방식은 에서 자주 사용됩니다.NET 프레임워크 (String.Empty,EventArgs.Empty,Guid.Empty등).

일반의 학급 동물     일반의 공유했습니다. 읽기 전용  ~하듯이 동물 = 신규 애니멀 엠프티()      일반의 덮어쓰기 가능 후보선수 사운드 만들기()         콘솔.기입선("우~")     끝. 후보선수 끝. 학급  친구. 상속 불가 학급 애니멀 엠프티     상속 동물      일반의 오버라이드 후보선수 사운드 만들기()         '     끝. 후보선수 끝. 학급 

비판

이 패턴은 에러/버그가 정상적인 프로그램 [7]실행으로 나타날 수 있으므로 신중하게 사용해야 합니다.

이 패턴을 구현하지 않도록 주의해야 합니다.이 패턴은 읽기 어려운 코드가 다른 곳으로 이동해 표준적이지 않을 수 있기 때문입니다.예를 들어 제공된 객체가 실제로 null 객체인 경우 다른 로직이 실행되어야 하는 경우 등입니다.참조 유형을 사용하는 대부분의 언어에서 일반적인 패턴은 null 또는 nero로 참조되는 단일 값에 대한 참조를 비교하는 것입니다.또한 null 오브젝트 대신 null 오브젝트를 할당하는 코드는 없는지 테스트할 필요가 있습니다.대부분의 경우나 정적 타입의 언어에서는 null 오브젝트가 참조 타입일 경우 이는 컴파일러 에러가 아닙니다.다만, 패턴이 avoi에 사용되었던 코드의 일부에서는 확실히 에러가 발생합니다.d 늘 체크게다가 대부분의 언어에서 다수의 null 객체가 존재할 수 있다고 가정할 때(즉, null 객체는 참조 유형이지만 어떤 식으로든 싱글톤 패턴을 구현하지 않음), null 값 또는 null 값 대신 null 객체를 체크하면 오버헤드가 발생합니다.싱글톤 패턴 자체가 싱글톤을 취득할 가능성이 높기 때문입니다.언급.

「 」를 참조해 주세요.

레퍼런스

  1. ^ Kühne, Thomas (1996). "Void Value". Proceedings of the First International Conference on Object-Oriented Technology, White Object-Oriented Nights 1996 (WOON'96), St. Petersburg, Russia.
  2. ^ Woolf, Bobby (1998). "Null Object". In Martin, Robert; Riehle, Dirk; Buschmann, Frank (eds.). Pattern Languages of Program Design 3. Addison-Wesley.
  3. ^ "Working with Objects (Working with nil)". iOS Developer Library. Apple, Inc. 2012-12-13. Retrieved 2014-05-19.
  4. ^ Fowler, Martin (1999). Refactoring. Improving the Design of Existing Code. Addison-Wesley. ISBN 0-201-48567-2.
  5. ^ Kerievsky, Joshua (2004). Refactoring To Patterns. Addison-Wesley. ISBN 0-321-21335-1.
  6. ^ Martin, Robert (2002). Agile Software Development: Principles, Patterns and Practices. Pearson Education. ISBN 0-13-597444-5.
  7. ^ 파울러, 마틴(1999년).리팩터링 페이지 216

외부 링크