A null object, in the context of the Null Object Pattern, is simply an object that does nothing when some operation is invoked.1 The idea is to avoid dealing with special cases - this includes, but is not limited to, avoiding null checks; e.g., you may want to do nothing if a certain set of conditions is not met. The calling code (client code) is written against an interface implemented by both the null object and by other objects that do have behavior. Because of polymorphism, the one that it (client code) gets to work with can turn out to be either one of those.
For this to work, client code has to have no knowledge about how exactly the interface is implemented, and, specifically, it cannot know if the concrete object it calls is null object or not. So, generally speaking, you cannot convey the specific idea of a null object through the design of the interface.
It's not an interface-level / single-object abstraction, it's an abstraction that involves a combination of objects, and the null object is usually, from a certain point of view, meant to be an internal detail.
It's important to consider what exactly are you exposing to client code. If you are writing some code that exposes an interface that others can implement, then you cannot force them to use the Null Object pattern (and you shouldn't). If you provide some class (let's call it MyClass
) that uses this interface as a dependency, then you can do a couple of things. E.g., you could provide an optional null implementation for clients to use if they wish to do so, and note that down in the documentation of MyClass
. Or, you could decide that MyClass
requires its dependencies to not be null and to not throw exceptions, document that requirement, and mention the Null Object Pattern as an option, but leave the implementation to clients. Or you could do both. Or, you can check for null in the constructor and use a corresponding null object for that parameter (without providing any way for clients to retrieve this null object; it's just to provide certain default behavior, and it should be internal to your class).
If the interface is internal to your code, or if clients are not able to extend it (either directly or indirectly), and you have a class that, again, has this interface as a dependency, then you have full control over how it all works, and you can decide internally when it's appropriate to use a null object, based on some parameter (or lack thereof). Clients don't have to know about this at all; you'd just provide one or more ways to construct the object, and document what each of these constructors and factory methods does; you don't even have to mention that you are using the Null Object Pattern internally.
1 Also, just to clear a misconception, throwing a null reference exception
is not a part of this pattern. Instead of removing special cases, this would actually
introduce one, since client code would (ultimately) need to handle this exception.
null
. For example, the null object of a list would be an empty list. Zero would be the null object to an int in the case of addition.