2

I've just learnt about the Null Object design pattern, which recommends that the service either return a default null object or throw an null related exception itself so that the client need not worry about checking for null or throwing a null exception.

The thing is, as far as I understand a client/service relationship should take place via an interface. How can the information that "the service handles null checking so the client not need have to" be conveyed via the interface? In C# the interface provides a method signature that can be expected, but the fact the service does null checking seems like an extra piece of arbitrary information that cannot be passed through the interface.

My concern is if the client assumes the service does null checking even though the interface has no way to specify such, that this may lead to "leaky abstraction" and the client is now taking for granted a specific implementation detail of the service, thus coupling itself to it.

What is the correct way for a client to be aware that the service is doing null checking so that it does not need to, without breaking the principles of encapsulation?

  • "[...] so that the client need not worry about checking for null or throwing a null exception." I think you may have misunderstood the null object pattern. It is not designed to do null checks. Your question is still interesting, but you may want to clarify whether you want to know what the null object design pattern is, or whether you want to know whose responsibility it is to ensure a reference is not null. – Vincent Savard Jan 20 at 14:23
  • Sorry @VincentSavard could you please recommend a good source to explain the pattern? My understanding was that the service would do a null check on an object and if it is null, to return some default implementation, or alternatively possibly throw an exception directly. If this is not the case it seems I've misunderstood it. – user4779 Jan 20 at 14:32
  • True, there is some confusion regarding Null Object and null checks, but the general idea is that a service will never return null. It will either return a null instance, that has no behavior, or will fail internally with exception. – Euphoric Jan 20 at 14:34
  • The Null Object design pattern is probably best left to die off. What C# really needed was non-nullable reference types AND union types (enums on steriods, if you aren't familiar). – Graham Jan 20 at 15:30
  • @Graham I must disagree with your statement, the null object design pattern is quite useful, and I'm not sure why you believe it should die off. Its name is slightly confusing because it's only loosely related to the value 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. – Vincent Savard Jan 20 at 15:51
4

Best way would be to have a type system that doesn't have "implicit null" for reference types. That means, that all references are non-nullable by default and can only contain null when explicitly defined.

This is possible in C# 8 with never version of a compiler using Nullable reference types. This way, the client knows if returned reference value can or cannot contain null and can handle it accordingly.

Something similar would be difficult in older versions of C#. Only option that I'm aware of is to set a convention that all references are non-nullable and only way to return null is through Option monad. And enforce this during code reviews or using static analysis tool.

3

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.

  • Thanks for this detailed explanation Filip it helps with my understanding. When you say to document the behavior, are we documenting the interface or the internal class that implements it? By only documenting the interface are we ensuring a separation of concerns from its implementation so that the client need only rely upon all the information contained in the interface? – user4779 Jan 21 at 2:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.