TheNullObjectPattern

From Pattern Repository Wiki
Jump to: navigation, search

Null Object

Contextual Forces

Motivation

When a behaviour may or may not happen, create an object that does nothing and make it subsituteable for one or more other objects that implement actual behavior. Essenitally, this redefines "do nothing" as one version of the behavior.

Encapsulation

Null Object encapsulates the fact that nothing will happen under some circumstances. It is a very simple version of a Mock Object

Procedural Analog

If a null pointer check is used to guard against a null pointer exception, this typically indicates that the pointer may or may not be pointing to a routine

// Non-optional behavior
if (ptr !null) {
     ptr->function;
}

The null object pattern solves the same problem by providing a non-operating version of the routine that the pointer can delegate to, and in object systems this is encapsulated in an object.

Non-Software Analog

I recently bought an MP3 player that uses an SD chip to store songs. An SD chip was not included, however, but in the slot was a plastic "mock up" of an SD Chip. It did not actually have any ability to store, it simply filled the slot during shipping to ensure that nothing (dust, etc...) got in there. It was a "null" SD Chip. MP3SD.JPG

Implementation Forces

Example

Abstract Example

NullObject 1.jpg

We are charging tax based on the state of residence. If the purchaser lives in Washington state, we charge one tax rate, if they live in California, another. However, regardless of where they live, if they are a reseller we do not charge any tax at all.

public interface TaxCalculator
{
    double CalcTax(double purchaseAmount);
}

class WATaxCalculator : TaxCalculator
{
    public double CalcTax(double purchaseAmount)
    {
        return purchaseAmount * WA_TAX_RATE;
    }
}

class CATaxCalculator : TaxCalculator
{
    // Null Object Pattern
    public double CalcTax(double purchaseAmount)
    {
        return purchaseAmount * CA_TAX_RATE;
    }
}

class ResellersTaxCalculator : TaxCalculator
{
    public double CalcTax(double ignore)
    {
        return 0.0;
    }
}

class Client 
{
    public void ProcessOrder(TaxCalculator myTaxCalculator)
    {
        // Other operations
        double taxvalue = myTaxCalculator.CalcTax(amountOfPurchase);
        // Other operations
    }
}

Note that the client contains no null pointer check, and yet it can be made to operate "tax free".

Questions, concerns, credibility checks

  • The presence of the Null Object pattern does not eliminate the possibility of a null pointer exception or (in unmanaged code) a memory leak. It only removes null pointer checks from the expected, correct flow of program logic.
  • Is the null case really the same in all other aspects except for the absence of an implemented algorithm? The Null Object Pattern implies that no action is taken, and that no other distinction is being made because of this.

Options in implementation

Typically the Null Object is one of many implementations of an interface or abstract type.

Optionally, the Null Object can be the base type that establishes the interface.

Example:

NullObject 2.jpg


This has the negative of creating less clarity and explicitness in the design, but it does eliminate an additional class.

Consequent Forces

Testing issues

The Null Object test would specify that the object does nothing (whatever that means in a given context... returning 0, null, an empty String, etc...).

However, it can also be used as a very simple Mock Object which makes other objects more testable.

Cost-Benefit (gain-loss)

  • Costs
    • The main cost of the Null Object is simply the addition of another class to your project.
    • Sometimes, an additional cost will be the time and effort it will take to convince others that a Null Object is not a poor design. Some have been taught "Objects must have a sufficient reason to exist", and have interpreted this to mean that objects must have sufficient behavior. This objection can sometimes[1] be overcome by pointing out the benefits of the object.
  • Benefits
    • A null pointer check may be eliminated in clients.
    • The use of the pointer will therefore be guaranteed. If you know that a pointer is always exercised at runtime, then this becomes a reliable integration point for new behavior (using, for example, The Proxy Pattern). The addition of new behavior to an existing system is often very commonplace in maintenance.
    • The testability of the client object may be enhanced, as the Null Object can server in some cases as a very simple Mock Object.


  1. ...and sometimes not. If you are faced with someone who is very theoretical in their thinking, pointing out these practical advantages may not be sufficient to overcome their objections. A theoretical objection may require a theoretical answer. Point out that in mathematics the most important number in the Arabic numbering system is arguably zero, as it makes algebra much more powerful (as well as trigonometry, calculus, etc...) even though it is "only" zero. It is one of the reasons that Arabic numbers came to supplant Roman numerals. The Null Object Pattern can be thought of an an enabler in this sense, that is is the zero in object-oriented design, that it "does" many things even though it has no behavior of its own.