Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

NULL Object Design Pattern

4.83/5 (30 votes)
23 Oct 2015CPOL8 min read 59.5K  
In this article we will explain about NULL Design Pattern in C#.

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

Introduction

Image 1

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 Problem

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.

Image 2

For example you can see in the below code “Order” class is talking / collaborating with the “Discount” interface to calculate discount.

Image 3

C#
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.

C#
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.

C#
class Order
{
// Code removed for simplification
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.

C#
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.

C#
Order NoDiscountOrder = new Order(null);
NoDiscountOrder.CalculateDiscount();

The order object would crash while collaborating with the discount object.

Image 4

So what is a NEAT and a CLEAN solution for the above problem?.

The NULL Design pattern

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.

C#
public class NullDiscount : IDiscount
{
public double CalculateDiscount(double productCost)
{
            return 0;
}
}

Now the below client call to order object would not crash.

C#
Order NoDiscountOrder = new Order(new NullDiscount());
NoDiscountOrder.CalculateDiscount();

IF NULL Check

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.

C#
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.
C#
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.

NULL check existence VS Default Behavior

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.

C#
Order order = GetOrderbyProduct("1001");
if (order == null)
{
// Order does not exist
}

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.

Mock testing, TDD and Agile

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.

C#
class Order
{
// Code removed for clarity
        public void Save()
        {
double Discount = _Discount.CalculateDiscount(ProductCost);
            if (ProductName.Length == 0)
            {
                throw new Exception("Product Name required");
            }
            // Call database code goes here
        }
}

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#.

Making it Singleton and Immutable

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#.

Summarizing about NULL design pattern

  • 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.

Proposal to change name

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.

Learn Design pattern with a project

For Further reading do watch the below interview preparation videos and step by step video series. 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)