Null Object Refactoring
 
Published: 14 May 2007
Abstract
This article will give an overview of the Introduce Null Object refactoring. The Null Object makes it easier to specify a null value in a business system, and this article will show you how.
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 26351/ 86

Introduction

Martin Fowler wrote a book called "Refactoring: Improving the Design of Existing Code," and in the book he discusses the "Introduce Null Object" refactoring. Instead of assigning a null value to a reference-type property, a nullable object can be used to represent a null value, instead of directly assigning a null value itself. This makes it easier to reference and you do not have to worry about getting a null exception when using the property. Here is how it works.

Review Status Example

Take, for example, the following class.

Listing 1

public class ReviewStatus
{
  private string _code = string.Empty;
  private string _name = string.Empty;
 
  #region " Properties "
 
  public string Code
  {
    get
    {
      return _code;
    }
    set
    {
      _name = value;
    }
  }
 
  public string Name
  {
    get
    {
      return _name;
    }
    set
    {
      _name = value;
    }
  }
 
  #endregion
 
  #region " Constructors "
 
  protected ReviewStatus(string code, string name)
  {
    _code = code;
    _name = name;
  }
 
  #endregion
  public static ReviewStatus Accepted()
  {
    return new ReviewStatus("A", "Accepted");
  }
 
  public static ReviewStatus Cancelled()
  {
    return new ReviewStatus("C", "Cancelled");
  }
 
  public static ReviewStatus Processed()
  {
    return new ReviewStatus("P", "Processed");
  }
 
  public override bool Equals(object obj)
  {
    if (obj == null || !(obj is ReviewStatus))
      return false;
 
    ReviewStatus status = (ReviewStatus)obj;
    return this.Code.Equals(status.Code);
  }
}

This class represents the status of a review in a business system.  It can be Accepted, Cancelled, or Processed, and has two properties that contain the codified and named values.  The review status, though, could have no status to report, especially if a new business object was created and not in the review phase.  To handle this through a Null object we can use the following object.

Listing 2

public class NullReviewStatus: ReviewStatus
{
  #region " Constructors "
  private NullReviewStatus(): base(string.Empty, string.Empty){}
  #endregion
 
  public static ReviewStatus Empty()
  {
    return new NullReviewStatus();
  }
}

Because the constructor on ReviewStatus is declared protected, the NullReviewStatus class can inherit from ReviewStatus to expose a new static method:  Empty().  Empty returns the equivalent of an empty status, meaning no status is assigned by assigning string.Empty values to the code and name parameters.  To note, string.Empty is returned so it does not cause null reference checks when using the properties, like using the Code.Equals() method in the ReviewStatus code sample.

To make null checking easier, Fowler recommends adding an IsNull method, which the ReviewStatus class returns a static value of false.  NullReviewStatus overrides the IsNull method and returns a value of true, noting that the object is null.  This makes it easier to process whether or not the object is actually null.

Listing 3

//ReviewStatus Implementation
public virtual bool IsNull()
{
      return false;
}
//NullReviewStatus Implementation
public override bool IsNull()
{
      return true;
}

An object that uses the ReviewStatus object can default the private variable of a business class to Empty.

Listing 4

private ReviewStatus _review = NullReviewStatus.Empty();

This makes the review status value equal to the nullable object and prevents any null exceptions from using a null reference variable.  Although there are varying preferences in regards to this, I prefer this approach in my business applications for certain object types.

A Factory Example

As another example I have a CityFactory class, which returns an array of City objects.  However, the business class will default to the NullCity implementation if no value has been provided yet.  So, the City definition has properties of ID and Name.

Listing 5

public class City
{
  private int _id;
  private string _name;
  public int ID
  {
    get
    {
      return _id;
    }
  }
  public string Name
  {
    get
    {
      return _name;
    }
  }
}

The NullCity defaults the ID to -1 and the Name to empty through the NullCity constructor (using the technique for nullable objects above, not because of the default values of City).  The following is a NUnit test that shows the flow in the application, as city defaults to a null value, and then gets populated from the factory later.

Listing 6

[Test]
public void TestCity()
{
  City city = NullCity.Empty();
  Assert.AreEqual( - 1, city.ID);
  Assert.AreEqual(string.Empty, city.Name);
  city = CityFactory.GetCities()[0];
  Assert.AreEqual(1, city.ID);
  Assert.AreEqual("Pittsburgh", city.Name);
}

The test runs successfully. The code is available with a unit test to illustrate the process and it can be downloaded from the URL given in the download section.

Downloads

Conclusion

Null object refactoring is a great technique to introduce nullable objects into a business system without actually using a null value.



User Comments

Title: good write-up   
Name: HoyaSaxa93
Date: 5/15/2007 3:26:44 PM
Comment:
good examples of factoring / backing into null objects






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2014 ASPAlliance.com  |  Page Processed at 4/24/2014 9:03:09 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search