Introduction
The Problem
The NULL Design pattern
IF NULL Check
NULL check existence VS Default Behavior
Mock testing, TDD and Agile
Making it Singleton and Immutable
Summarizing about NULL design pattern
Proposal to change name
Learn Design pattern with a project
Close your eyes take a deep breath…. Assume there is no earth, no universe, you have no relatives / friends, you are surrounded by NULL, you are NULL, what you lose is NULL, what you gain is NULL and you can still live your life happily ahead.☻☻☻☻
In the same way now assume there is no object, but yourcodecan still run. Welcome to NULL design pattern.
The best way to understand any design pattern is to understand what problem it solves. So let’s first try to understand what problem does NULL design pattern address.
It’s a very common practice in Object oriented programming world to see one object collaborating with other object to achieve some complex functionality.
For example you can see in the below code “Order” class is talking / collaborating with the “Discount” interface to calculate discount.
class Order
{
public string ProductName { get; set; }
public double ProductCost { get; set; }
private IDiscount _Discount = null;
public double CalculateDiscount()
{
return _Discount.CalculateDiscount(ProductCost);
}
}
The discount classes are of two types “PremiumDiscount” and “FestivalDiscount”.
In future there is a possibility we need to add more such discount calculation logics. So to make sure the order class does not get disturbed / changed when we add new discount classes we have put an interface in betweenthe order and the discount classes.
interface IDiscount
{
double CalculateDiscount(double productCost);
}
public class PremiumDiscount : IDiscount
{
public double CalculateDiscount(double productCost)
{
return (productCost*0.5);
}
}
public class FestivalDiscount : IDiscount
{
public double CalculateDiscount(double productCost)
{
return (productCost * 0.2);
}
}
In the “Order” class we have made appropriate provision in the constructor to inject any type of “Discount” object. In other words we have followed “Inversion of Control” principle for decoupling. In case you are new to IOC please read this article Inversion of Control ( IOC ) and in case you have confusion about the difference between DI VS IOC please read this article DI vs IOC.
class Order
{
private IDiscount _Discount = null;
public Order(IDiscount dis)
{
_Discount = dis;
}
}
Sonow because we have followed IOC we have great flexibility of creating permutation and combination of any “Order” and “Discount” types.
In the below code you can see we have created “PremiumOrder” object using the “PremiumDiscount” object and “FestivalOrder” object using the “FestivalDiscount” object.
Order PremiumOrder = new Order(new PremiumDiscount());
Order FestivalOrder = new Order(new FestivalDiscount());
Now let us take a scenario where we would like to create an order with no discount. Currently we do not have any “Discount” class which calculates no discount.
And why we do not have one?, because it does not make sense to have a “Discount” class with no “Discount” calculation.
Now if we try to solve this problem by passing NULL as shown below.
Order NoDiscountOrder = new Order(null);
NoDiscountOrder.CalculateDiscount();
The order object would crash while collaborating with the discount object.
So what is a NEAT and a CLEAN solution for the above problem?.
We can summarize the above problem and solution as below:-
“We want the order object to continue executing and not crash. For that we need the ABSENCE of the discount object to be filled with some DEFAULT behavior or NULL behavior”.
So in other words we need to create a discount class which does NO discount calculation as shown in the below code.
public class NullDiscount : IDiscount
{
public double CalculateDiscount(double productCost)
{
return 0;
}
}
Now the below client call to order object would not crash.
Order NoDiscountOrder = new Order(new NullDiscount());
NoDiscountOrder.CalculateDiscount();
One of the things which can come to your mind as a developer is why can’t we just put a simple “IF NULL” check as shown in the below code.
public double CalculateDiscount()
{
if (_Discount == null)
{
return 0;
}
return _Discount.CalculateDiscount(ProductCost);
}
In the above approach below are the problems:-
- Order class is now deciding how discounts are calculated. This is a clear violation of S of SOLID i.e. Single responsibility principle. The calculation of discount should be done by the discount classes and not order class. In case you are new to solid please read this article SOLID Principles in C#.
- NULL classes symbolize default behavior and default behavior can be any value and not compulsorily ZERO. NULL classes are domain specific and not technical specific. It’s very much possible that default discount can be 0.1 as shown in the below code. So creating a class makes more sense than just technically checking NULLs and returning zero value.
public class NullDiscount : IDiscount
{
public double CalculateDiscount(double productCost)
{
return 0.1;
}
}
- There are group of developers which also includes me who believe in the principle of “Tell what to and Don’t ask”. So rather than checking what is the state of “Discount” object and then acting , it makes sense to just ask the”Discount” object what to do. He has theexpertise in discount calculation and he will provide the best solution for the same.
One of the misconceptions about this pattern is that it is used to avoid NULL pointer exceptions. I personally feel NULLs are important to signal out errors and issues in application. Swallowing those errors would lead to unhidden defects which can cause serious silent problems later.
For example below is a simple code which calls a function to get order data. If the order data is not found it returns NULL.
In the client side we check for the existence of databy doing a NULL check.
Order order = GetOrderbyProduct("1001");
if (order == null)
{
}
Now in the above case we should not return a default NULL order object. Here we are checking for existence of an object and in this case NULL checks are absolutely fine.
Giving out a default order object out can lead to error going undetected and causing other side effects. NULL design pattern should be used when object works in collaboration and at some moment of time one object expects other object for a default behavior.
NULL design pattern is about giving a default implementation for filling absence of an object and it’s not about avoiding NULL pointer exceptions. If you see NULL design pattern implemented with NO object collaboration then there is something wrong in the way the pattern is implemented.
Yes, the side benefit of NULL design pattern is thatlot of NULL exceptions are prevented but that is not the intention of the pattern.
Another great use of this pattern is when we are doing UNIT testing and especially regression UNIT testing. For example below is a simple “Save” method in the order class which calls the discount calculation first and then does some validations and finally adds the order data to database.
class Order
{
public void Save()
{
double Discount = _Discount.CalculateDiscount(ProductCost);
if (ProductName.Length == 0)
{
throw new Exception("Product Name required");
}
}
}
For now assume that you are working in an Agile environment where we deliver code as per sprints. Assume for now that the “Discount” class will be coded in the next sprint. So you do not have any concrete class for discount.
But you still want to UNIT test the order class. So in this case again you can create a default implementation of “Discount” class, inject in the order class and execute your unit tests.
In this case we can also term the NULL classes as Mock classes because we are actually doing Mock testing. In case you are new to Mock testing please check this YouTube video Mock testing in C#.
In order to improve performance it makes sense to create just a single immutable instance of the NULL class. In case you are not aware how to make a C# class immutable follow these 3 steps given in this article. Because the NULL class just has default values and default behavior so caching it makes more sense. In this article I would not get in to Singleton pattern in case you want to read more you can read from this article Singleton Pattern in C#.
- NULL design pattern fills an ABSENCE of an object with a DEFAULT behavior and should be used only when one object collaborates with other.
- NULL design pattern is not meant to replace NULL exception handling. It’s one of the side benefits of NULL design pattern but the intention is to provide a default behavior.
- NULL checks should not be replaced with NULL design pattern objects as it can lead to silent defects in application.
- NULL design pattern is useful in Mock unit testing and Agile development.
- NULL classes are classic case for applying Singleton design pattern and making the classes immutable.
- This pattern is mostly seen in combination with decorator and factory pattern.
I am not a big person to suggest name changes. But after using this pattern in lot of projects I feel the word NULL is misinterpreted with the NULL data type. If you look at this pattern closely the proper name would be: - “Default Behavior object design pattern”. So in case you feel this name looks logical please tweet and blog about it so that we can avoid lot of confusion about this pattern.
For Further reading do watch the below interview preparation videos and step by step video series.