Many times functions accept object references that can be null, and we tend to add if statements to treat the special case where null is passed to a function, and either provide a default response or do nothing. In the following example our calculateSpeed function expects an object that has a getSpeed function. In Javascript we will have to do something like:
class Car {
getSpeed(){
return 100;
}
}
const calculateSpeed = function(vehicle){
if(vehicle && typeof vehicle.getSpeed === 'function'){
return vehicle.getSpeed();
} else {
return 50;
}
}
const car1 = new Car();
console.log(calculateSpeed(car1)); // 100
console.log(calculateSpeed()); // 50
But there is a better way to achieve that. Using the Null Object Pattern we can create a class that acts as vehicle, lets call it DefaultMovable
.
class DefaultMovable {
getSpeed(){
return 50;
}
}
Our DefaultMovable
class provides the default functionality (aka the else in our previous code snippet), that way we can avoid the if/else statement.
class Car {
getSpeed(){
return 100;
}
}
class DefaultMovable {
getSpeed(){
return 50;
}
}
const calculateSpeed = function(vehicle = new DefaultMovable()){
return vehicle.getSpeed();
}
const car1 = new Car();
console.log(calculateSpeed(car1)); // 100
console.log(calculateSpeed()); // 50
The UML diagram of this pattern will look like this:
The same example in Ruby would look like:
class Car
def get_speed
100
end
end
class DefaultMovable
def get_speed
50
end
end
def getSpeed(vehicle = DefaultMovable.new)
vehicle.get_speed();
end
This is just a pattern and as every pattern it has its pros and cons, apply it thoughtfully based on your use case. (The example is fictional for the shake of demonstrating the pattern)
Top comments (16)
This seems like bad design to me. Why wouldn't you establish the defaults in the Car class, set a private variable
speed
in the class and just add a constructor that accepts a speed. Creating a second, unrelated class, just seems to be messy and more prone to the introduction of hard-to-trace bugs. Perhaps this was not the best example to use for demonstrating a Null Object Pattern.The code above solves the problem without potentially introducing new ones. Obviously, the constructor could be cleaned up to ensure we've received a number for
speed
and so on.Yes, this solves the problem but doesn't really make it clear that we're dealing with null's in the constructor. I feel like the example in the post is not the best because it's just returning a value from the method. If the behaviour was more complex, then you might want to do something specific if the case where we have
null
. That's where thenull object pattern
really shines.The example is flawed. As presented, this is just one big hack. Why is the default value 50? Is it a mistake to call this without a valid argument (one that has the expected function)? If it is a mistake, do not paper over it by returning 50. If it is an expected condition, NullMovable is a horrible name, since it does not describe what concept/abstraction it implements. (Null abstraction is such that it is an error to perform any operation on it.)
Agree, changed to
DefaultMovable
. Thx for the feedbackA better example would be in order, since the one presented doesn't make sense - what is the context of calling
calculateSpeed
without a specific car? For that function, it should specifically enforce that a parameter be passed.If there is a place in your code where you are okay with using context-free default values, it usually means that it's unnecessary to call that code in the first place, and the real architectural issue lies higher up.
I can see this being useful in specific cases though, where you're dealing with undefined input, and the default object / values are logical to be useful further on.
Do we agree that the default parameter in JavaScript handles the
undefined
case but not thenull
one?Sure, that is just the name of the pattern in literature (unfortunately) en.wikipedia.org/wiki/Null_object_...
This is really null object.
To be honest.. it's dirty :/
It would have been nice to see what some of the pro's and con's are of using this pattern.
I've seen a few comments stating that it's bad because there is more code. More code does not mean that the code is cleaner. We could write nested if statements on one line using ternary, but I'm sure you understand why this is a bad idea.
We shouldn't be checking if an object is
nil
ornull
as this means we're concerned with types. Ruby in particular is somewhere you would probably see this pattern as it encourages duck typing and polymorphism. The null object pattern is really useful when you're passing objects around the stack and calling methods in multiple places. Everywhere you call a method on an object you would also firstly need to check if the object isnil
. Yes, we should never really havenull objects
, but we always will, as exceptional behaviour will always be present. Systems fail, software fails.Embrace composition of multiple simpler elements rather than null-patterns. You'll still have the boilerplate of your solution, but it'll be clearer (explicit) what the actual state is.
For things like CSV or other simplified flat input, null is just fine. The format is limited so you have to workaround and null makes more sense for some columns than ''.
You wouldn't want a browser having null elements. It'd be better to have a list of children which is either empty, or has N elements sharing an interface, which conform to a rigid specification laid out in the engine. Null isn't needed. The same applies for defaults that are stored, unless auditing is a requirement.
I wouldn't overload NULL. It already has a clear definition. It would be better to use Void or Nop (no operation) as name.
Use Maybe / Optional instead