Null object pattern: Difference between revisions

Content deleted Content added
C++Bro123 (talk | contribs)
m C++: Improve C++ examples
m Disambiguating links to Object-orientation (link changed to Object-oriented programming) using DisamAssist.
 
(44 intermediate revisions by 28 users not shown)
Line 1:
{{Short description|Object with no referenced value or with defined neutral ("null") behavior}}
{{Redirect|Null object|the concept in category theory|Initial object}}
In [[Object-oriented programming|object-oriented]] [[computer programming]], a '''null object''' is an [[Object (computer science)|object]] with no referenced value or with defined neutral ("''null"'') behavior. The null object [[design pattern]], which describes the uses of such objects and their behavior (or lack thereof). It, was first published inas the"Void [[Pattern Languages of Programs#Publications|''Pattern Languages of Program Design'' book series]].Value"<ref>{{Cite bookconference
| first = Thomas
| last = Kühne
| contribution = Void Value
| title = Proceedings of the First International Conference on Object-Oriented Technology, White Object-Oriented Nights 1996 (WOON'96), St.&nbsp;Petersburg, Russia
| year = 1996
}}</ref>
and later in the [[Pattern Languages of Programs#Publications|''Pattern Languages of Program Design'' book series]] as "Null Object".<ref>{{Cite book
| first = Bobby
| last = Woolf
Line 13 ⟶ 21:
| year = 1998
| publisher = Addison-Wesley
}}</ref>
| postscript = <!--None-->
}}</ref>
 
==Motivation==
In most object-oriented languages, such as [[Java (programming language)|Java]] or [[C Sharp (programming language)|C#]], [[Reference (computer science)|references]] may be [[Null pointer|null]]. These references need to be checked to ensure they are not null before invoking any [[Method (computer science)|methods]], because methods typically cannot be invoked on null references.
 
The [[Objective-C|Objective-C language]] takes another approach to this problem and does nothing when sending a message to <code>nil</code>; if a return value is expected, <code>nil</code> (for objects), 0 (for numeric values), <code>NO</code> (for <code>BOOL</code> values), or a struct (for struct types) with all its members initialised to <code>null</code>/0/<code>NO</code>/zero-initialised struct is returned.<ref>{{Cite web|url = https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW22|title = Working with Objects (Working with nil)|date = 2012-12-13|accessdateaccess-date = 2014-05-19|website = iOS Developer Library|publisher = Apple, Inc.}}</ref>
 
== Description ==
{{Unreferenced section|date=June 2023}}
 
Instead of using a [[null reference]] to convey the absence of an object (for instance, a non-existent customer), one uses an object which implements the expected [[Interface_(computing)#In_object-oriented_languages | interface]], but whose method body is empty. TheA advantagekey purpose of thisusing approacha overnull aobject workingis defaultto implementationavoid isconditionals thatof adifferent nullkinds, objectresulting in code that is verymore predictablefocused, and hasquicker noto sideread effects:and itfollow{{snd}} i.e. doesimproved ''nothing''readability.
One advantage of this approach over a working default implementation is that a null object is very predictable and has no side effects: it does ''nothing''.
 
For example, a function may retrieve a list of files in a folder and perform some action on each. In the case of an empty folder, one response may be to throw an exception or return a null reference rather than a list. Thus, the code which expectsexpecting a list must verify that it in fact has one before continuing, which can complicate the design.
 
By returning a null object (i.e., an empty list) instead, there is no need to verify that the return value is in fact a list. The calling function may simply iterate the list as normal, effectively doing nothing. It is, however, still possible to check whether the return value is a null object (an empty list) and react differently if desired.
 
The null object pattern can also be used to act as a stub for testing, if a certain feature such as a database is not available for testing.
Line 35 ⟶ 44:
 
'''class''' node {
node left
node right
}
 
Line 42 ⟶ 51:
 
'''function''' tree_size(node) {
return 1 + tree_size(node.left) + tree_size(node.right)
}
 
Line 48 ⟶ 57:
 
'''function''' tree_size(node) {
set sum = 1
'''if''' node.left exists {
sum = sum + tree_size(node.left)
}
'''if''' node.right exists {
sum = sum + tree_size(node.right)
}
return sum
}
 
This, however, makes the procedure more complicated by mixing boundary checks with normal logic, and it becomes harder to read. Using the null object pattern, one can create a special version of the procedure but only for null nodes:
 
'''function''' tree_size(node) {
return 1 + tree_size(node.left) + tree_size(node.right)
}
 
'''function''' tree_size(null_node) {
return 0
}
 
This separates normal logic from special case handling, and makes the code easier to understand.
 
==Relation to other patterns==
It can be regarded as a special case of the [[State pattern]] and the [[Strategy pattern]].
 
It is not a pattern from ''[[Design Patterns (book)|Design Patterns]]'', but is mentioned in [[Martin Fowler (software engineer)|Martin Fowler's]] ''Refactoring''<ref>{{cite book
| first = Martin
| last = Fowler
| authorlinkauthor-link = Martin Fowler (software engineer)
| year = 1999
| title = Refactoring. Improving the Design of Existing Code
| publisher = Addison-Wesley
| isbn = 0-201-48567-2
| url-access = registration
}}</ref> and Joshua Kerievsky's Refactoring To Patterns<ref>{{cite book
| url = https://archive.org/details/isbn_9780201485677
}}</ref> and Joshua Kerievsky's Refactoring To Patterns<ref>{{cite book
| first = Joshua
| last = Kerievsky
| authorlinkauthor-link = Joshua Kerievsky
| year = 2004
| title = Refactoring To Patterns
Line 94 ⟶ 105:
| first = Robert
| last = Martin
| authorlinkauthor-link = Robert Cecil Martin
| year = 2002
| title = Agile Software Development: Principles, Patterns and Practices
| publisher = Pearson Education
| isbn = 0-13-597444-5
| url-access = registration
}}</ref> is dedicated to the pattern.
| url = https://archive.org/details/agilesoftwaredev00robe
}}</ref> is dedicated to the pattern.
 
==Alternatives==
From C# 6.0 it is possible to use the "?." operator (aka [[Safe_navigation_operator#C.23|null-conditional operator]]), which will simply evaluate to null if its left operand is null.
<sourcesyntaxhighlight lang="csharp">
// compile as Console Application, requires C# 6.0 or higher
using System;
Line 121 ⟶ 134:
// The output will be:
// 4
</syntaxhighlight>
</source>
 
===Extension methods and Null coalescing===
{{Unreferenced section|date=June 2023}}
In some [[Microsoft .NET]] languages, [[Extension method]]s can be used to perform what is called 'null coalescing'. This is because extension methods can be called on null values as if it concerns an 'instance method invocation' while in fact extension methods are static. Extension methods can be made to check for null values, thereby freeing code that uses them from ever having to do so. Note that the example below uses the [[C Sharp (programming language)|C#]] [[Null coalescing operator]] to guarantee error free invocation, where it could also have used a more mundane if...then...else. The following example only works when you do not care the existence of null, or you treat null and empty string the same. The assumption may not hold in other applications.
 
<sourcesyntaxhighlight lang="csharp">
// compile as Console Application, requires C# 3.0 or higher
using System;
Line 148 ⟶ 162:
// The output will be:
// 18
</syntaxhighlight>
</source>
 
==In various languages==
Line 158 ⟶ 172:
A language with statically typed references to objects illustrates how the null object becomes a more complicated pattern:
 
<sourcesyntaxhighlight lang="cpp">
import std;
#include <iostream>
 
class Animal {
Line 168 ⟶ 182:
};
 
class Dog : public Animal {
public:
virtual void MakeSound() const override { std::cout << println("woof!" << std::endl); }
};
 
class NullAnimal : public Animal {
public:
virtual void MakeSound() const override {}
};
</syntaxhighlight>
</source>
 
Here, the idea is that there are situations where a pointer or reference to an <code>Animal</code> object is required, but there is no appropriate object available. A null reference is impossible in standard-conforming C++. A null <code>Animal*</code> pointer is possible, and could be useful as a place-holder, but may not be used for direct dispatch: <code>a->MakeSound()</code> is undefined behavior if <code>a</code> is a null pointer.
Line 185 ⟶ 199:
The special null class must be created for each class hierarchy that is to have a null object, since a <code>NullAnimal</code> is of no use when what is needed is a null object with regard to some <code>Widget</code> base class that is not related to the <code>Animal</code> hierarchy.
 
Note, that NOT having a null class at all is an important feature, in contrast to languages where "anything is a reference" (e.g., Java and C#). In C++, the design of a function or method may explicitly state whether null is allowed or not.
 
<sourcesyntaxhighlight lang="cpp">
// Function which requires an |Animal| instance, and will not accept null.
void DoSomething(const Animal& animal) {
Line 197 ⟶ 211:
// |animal| may be null.
}
</syntaxhighlight>
</source>
 
===C#===
Line 203 ⟶ 217:
C# is a language in which the null object pattern can be properly implemented. This example shows animal objects that display sounds and a NullAnimal instance used in place of the C# null keyword. The null object provides consistent behaviour and prevents a runtime null reference exception that would occur if the C# null keyword were used instead.
 
<sourcesyntaxhighlight lang="csharp">
/* Null object pattern implementation:
*/
Line 232 ⟶ 246:
 
// Dog is a real animal.
class Dog : IAnimalAnimal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
Line 257 ⟶ 271:
unknown.MakeSound(); // outputs nothing, but does not throw a runtime exception
}
}</sourcesyntaxhighlight>
 
===Smalltalk===
Line 263 ⟶ 277:
Following the Smalltalk principle, ''everything is an object'', the absence of an object is itself modeled by an object, called <code>nil</code>. In the GNU Smalltalk for example, the class of <code>nil</code> is <code>UndefinedObject</code>, a direct descendant of <code>Object</code>.
 
Any operation that fails to return a sensible object for its purpose may return <code>nil</code> instead, thus avoiding the special case of returning "no object" unsupported by Smalltalk designers. This method has the advantage of simplicity (no need for a special case) over the classical "null" or "no object" or "null reference" approach. Especially useful messages to be used with <code>nil</code> are <code>isNil</code>, <code>ifNil:</code> or <code>ifNilifNotNil:</code>, which make it practical and safe to deal with possible references to <code>nil</code> in Smalltalk programs.
 
===Common Lisp===
Line 277 ⟶ 291:
 
In Common Lisp, the object <code>nil</code> is the one and only instance of the special class <code>null</code>. What this means is that a method can be specialized to the <code>null</code> class, thereby implementing the null design pattern. Which is to say, it is essentially built into the object system:
<sourcesyntaxhighlight lang="lisp">
;; empty dog class
 
Line 291 ⟶ 305:
;; innocuous empty body: nil makes no sound.
(defmethod make-sound ((obj null)))
</syntaxhighlight>
</source>
The class <code>null</code> is a subclass of the <code>symbol</code> class, because <code>nil</code> is a symbol.
Since <code>nil</code> also represents the empty list, <code>null</code> is a subclass of the <code>list</code> class, too. Methods parameters specialized to <code>symbol</code> or <code>list</code> will thus take a <code>nil</code> argument. Of course, a <code>null</code> specialization can still be defined which is a more specific match for <code>nil</code>.
Line 299 ⟶ 313:
Unlike Common Lisp, and many dialects of Lisp, the Scheme dialect does not have a nil value which works this way; the functions <code>car</code> and <code>cdr</code> may not be applied to an empty list; Scheme application code therefore has to use the <code>empty?</code> or <code>pair?</code> predicate functions to sidestep this situation, even in situations where very similar Lisp would not need to distinguish the empty and non-empty cases thanks to the behavior of <code>nil</code>.
 
=== Ruby ===
 
In [[Duck typing|duck-typed]] languages like [[Ruby (programming language)|Ruby]], language inheritance is not necessary to provide expected behavior.
<sourcesyntaxhighlight lang="ruby">
class Dog
def sound
"bark"
end
end
class NilAnimal
def sound(*); end
end
 
def get_animal(animal=NilAnimal.new)
Line 321 ⟶ 335:
get_animal.sound
=> nil
</syntaxhighlight>
</source>
Attempts to directly [[Monkey patch|monkey-patch]] NilClass instead of providing explicit implementations give more unexpected side effects than benefits.
 
=== JavaScript ===
In [[Duck typing|duck-typed]] languages like [[JavaScript]], language inheritance is not necessary to provide expected behavior.
<sourcesyntaxhighlight lang="javascript">
class Dog {
sound() {
Line 345 ⟶ 359:
['dog', null].map((animal) => getAnimal(animal).sound());
// Returns ["bark", null]
</syntaxhighlight>
</source>
 
===Java===
 
<sourcesyntaxhighlight lang="java">
public interface Animal {
void makeSound() ;
}
 
Line 362 ⟶ 376:
public class NullAnimal implements Animal {
public void makeSound() {
// silence...
}
}
</syntaxhighlight>
</source>
 
This code illustrates a variation of the C++ example, above, using the Java language. As with C++, a null class can be instantiated in situations where a reference to an <code>Animal</code> object is required, but there is no appropriate object available. A null <code>Animal</code> object is possible (<code>Animal myAnimal = null;</code>) and could be useful as a place-holder, but may not be used for calling a method. In this example, <code>myAnimal.makeSound();</code> will throw a NullPointerException. Therefore, additional code may be necessary to test for null objects.
Line 372 ⟶ 386:
 
=== PHP ===
<sourcesyntaxhighlight lang="php">
interface Animal
{
public function makeSound();
}
 
class Dog implements Animal
{
public function makeSound() {
{
echo "Woof...\n";
}
}
 
class Cat implements Animal
{
public function makeSound() {
{
echo "Meowww...\n";
}
}
 
class NullAnimal implements Animal
{
public function makeSound() {
{
// silence...
}
Line 396 ⟶ 417:
 
$animalType = 'elephant';
 
switch($animalType) {
function makeAnimalFromAnimalType(string $animalType): Animal
case 'dog':
{
$animal = new Dog();
switch ($animalType) {
break;
case 'catdog':
$animal = return new CatDog();
break;case 'cat':
return new Cat();
default:
$animal = new NullAnimal();default:
break return new NullAnimal();
}
}
 
makeAnimalFromAnimalType($animalanimalType)->makeSound(); // ..the null animal makes no sound
 
function animalMakeSound(Animal $animal): void
{
$animal = new Dog->makeSound();
}
 
foreach ([
makeAnimalFromAnimalType('dog'),
makeAnimalFromAnimalType('NullAnimal'),
makeAnimalFromAnimalType('cat'),
] as $animal) {
// That's also reduce null handling code
animalMakeSound($animal);
}
</syntaxhighlight>
$animal->makeSound(); // ..the null animal makes no sound
</source>
 
=== Visual Basic .NET ===
The following null object pattern implementation demonstrates the concrete class providing its corresponding null object in a static field <code>Empty</code>. This approach is frequently used in the .NET Framework (<code>String.Empty</code>, <code>EventArgs.Empty</code>, <code>Guid.Empty</code>, etc.).
 
<sourcesyntaxhighlight lang="vbvbnet">
Public Class Animal
Public Shared ReadOnly Empty As Animal = New AnimalEmpty()
Line 429 ⟶ 466:
End Sub
End Class
</syntaxhighlight>
</source>
 
==Criticism==
This pattern should be used carefully, as it can make errors/bugs appear as normal program execution.<ref>Fowler, Martin (1999). Refactoring, ppp. &nbsp;216.</ref>
 
Care should be taken not to implement this pattern just to avoid null checks and make code more readable, since the harder -to -read code may just move to another place and be less standard – suchstandard—such as when different logic must execute in case the object provided is indeed the null object. The common pattern in most languages with [[reference typestype]]s is to compare a reference to a single value referred to as null or nil. Also, there is an additional need for testing that no code anywhere ever assigns null instead of the null object, because in most cases and languages with static typing, this is not a compiler error if the null object is of a reference type, although it would certainly lead to errors at run time in parts of the code where the pattern was used to avoid null checks. On top of that, in most languages and assuming there can be many null objects (i.e., the null object is a reference type but doesn't implement the [[singleton pattern]] in one or another way), checking for the null object instead of for the null or nil value introduces overhead, as does the singleton pattern likely itself upon obtaining the singleton reference.
 
==See also==
Line 444 ⟶ 481:
 
==External links==
*[http://www.cs.oberlin.edu/~jwalker/nullObjPattern/ JeffreyJeffery Walker's account of the Null Object Pattern]
*[http://martinfowler.com/eaaCatalog/specialCase.html Martin Fowler's description of Special Case, a slightly more general pattern]
*[http://www.owlnet.rice.edu/~comp212/00-spring/handouts/week06/null_object_revisited.htm Null Object Pattern Revisited]