AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1258&pId=-1
Null Object Refactoring
page
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 26650/ 43

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.



©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-23 6:06:52 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search