The Null Object Pattern
Last time we considered several options of temporarily disabling logging in our system. All choices involved commenting out or otherwise removing logging statements—we are modifying existing code.
Ideally, we would achieve our objective by writing new code rather than changing existing code.
Sure, any decent logging library will have us covered—we can configure it to log only at certain severity levels or not at all.
However, as capable developers, we should know of a wide range of solutions to programming problems. I’m going to present one such solution, which will not be dependent on the logging library used.
Programming options that help us stay independent of frameworks or 3rd party libraries are generally more versatile. Tightly coupling our application code to frameworks means we will be beholden to the whims of programmers who have different goals from ours—they want to push their framework while we want to develop our software. If they introduce a change that breaks our system, it’s our problem, not theirs.
We ought to control our programs, not 3rd-party developers.
OK, let’s come back to the best (so far) solution we had come up with to stop logging. We modified ConsoleLogger
, an ILogger
implementation, by commenting out all writes to the console:
public class ConsoleLogger : ILogger { public void LogInfo(string message) { // Console.WriteLine(message); } // ... }
In the process, we also had created two problems:
ConsoleLogger
no longer logs to the console, and- we changed rather than wrote new code.
What now? Can we do better?
Yes, we can! Ironically, the answer lies in the modifications we have made to ConsoleLogger
– let’s use those but repackage them in a new class, NullLogger
:
public class NullLogger : ILogger { public void LogInfo(string message) { // Do nothing } // ... }
Just like the modified ConsoleLogger
, this class provides no behaviour. When we plug in a NullLogger
for our Logger
reference, no logging will take place. We’re binding ILogger
to a module that does nothing.
Doesn’t that seem plain wrong? It certainly did for me when I first came across a NullLogger
implementation many years ago.
It looks crazy until we realise that this is a pluggable way of completely turning off logging—of doing nothing.
Sure, the application code still calls the Logger
methods as before. And now they do nothing.
Of course, turning logging on again is as simple as replacing NullLogger
with ConsoleLogger
or another functioning logger implementation.
What we have here is the Null Object Pattern.
Are there other use cases for the Null Object Pattern?
Caching
How about a NullCache? Plugging in NullCache would run our system without caching; i.e. we always go to our persisted data store.
How would NullCache behave? Simple: Updating the cache would do nothing. Cache reads are always cache misses, returning nulls or empty collections. A NullCache could be helpful to test a system’s ability to operate even when the cache is unavailable.
Feature Flagging
Turning off (and on) new application behaviour with feature flags is another use case for the Null Object Pattern. By default, we can turn off a new feature with an AlwaysOffFeatureFlagger implementation of IFeatureFlagger. As its name implies, AlwaysOffFeatureFlagger calls would always return false. Analogously, we could also implement an AlwaysOnFeatureFlagger.
Authorisation
Calls to authorisation come out either true or false—either the user is allowed to perform the action or not. As a pluggable way of side-stepping authorisation, say, since there are no restrictions on who is allowed to invoke an action, we could have an AlwaysAllowAuthoriser. The counterpart, AlwaysDenyAuthoriser, will also work.
A word of caution: Authorisation and security are sensitive topics. Always allowing to bypass the authorisation step with the help of an AlwaysAllowAuthoriser can land you in hot water when permissions are expected to be more restricted. Having a suite of unit tests (or other mechanisms) to ensure that the appropriate authorisation levels are applied at all times is critical.
The Null Object Pattern is cool. I hope you get to use it.