Home Articles Talks Links Contact Me ISA ThoughtWorks

Special Case


A subclass which provides special behavior for particular cases.

Null's make awkward things in object-oriented programs because they defeat polymorphism. Usually you can invoke foo freely on a variable reference of a given type without worrying about whether the item in the type is of the exact type or a subclass. With a strongly typed language you can even have the compiler check that the call is correct. However since a variable can contain null, you may run into a runtime error by invoking a message on null, which will get you a nice friendly stack trace.

If it's possible for a variable to be null, you have to remember to surround it with null test code so you remember do the right thing if a null is present. Often the right thing is same in lots of contexts, so you end up writing similar code in lots of places - which is the sin of duplicate code.

Nulls are a common problem of this kind, but others crop up regularly. In number systems you have to deal with infinity, which has special rules for things like addition that break the usual invariants of real numbers. One of my earliest examples in business software was a utility customer who wasn't fully known - referred to as "occupant". All of these imply altered behavior to the usual behavior of the type.

Instead of returning null, or some odd value, return a Special Case that has the same interface as what the caller expects.

How it Works

The basic idea is to create a subclass to handle the Special Case. So if you have a customer object and you want to avoid null checks, you make a null customer object. Take all of the methods for customer and override them in the Special Case to provide some harmless behavior. Then whenever you would have a null put in an instance of null customer instead.

There's usually not any reason to distinguish between different instances of null customer, so you can often implement a Special Case with a flyweight [Gang of Four]. This isn't true all the time. For a utility you can accumulate charges against an occupant customer even you can't do much of the billing, so it's important to keep your occupants separate.

People often use a null to mean different things. A null customer may mean there isn't a customer or it may mean that there is a customer but we don't know who it is. Rather than just use a null customer, consider having separate Special Cases for missing customer and unknown customer.

A common way for a Special Case to override methods is to return another Special Case. So if you ask an unknown customer for his last bill, you may well get an unknown bill.

When to Use it

Use Special Case whenever you have multiple places in the system that do the same behavior after a conditional check for a particular instance of this class, or same behavior after a null check.

Further Reading

I haven't seen Special Case written up as a pattern yet, but Null Object has been written up in [Woolf]. If you'll pardon the unresistable punnery I see Null Object as special case of Special Case.

Example: A Simple Null Object (C#)

Here's a simple example of using Special Case for its common use as a null object.

We have a regular employee.

class Employee... 
		public virtual String Name {
			get {return _name;}
			set {_name = value;}
		}
		private String _name;
		public virtual Decimal GrossToDate {
			get {return calculateGrossFromPeriod(0);}
		}
		public virtual Contract Contract {
			get {return _contract;}
		}
		private Contract _contract;

The features of the class could be overridden by a null employee

class NullEmployee : Employee, INull... 
		public override String Name {
			get {return "Null Employee";}
			set {}
		}
		public override Decimal GrossToDate {
			get {return 0m;}
		}
		public override Contract Contract {
			get {return Contract.NULL;}
		}

Notice how when you ask a null employee for its contract you get a null contract back.

The default values here avoid a lot of null tests if the null tests end up with the same null values. The repeated null values are handled by the null object by default. You can also test for nullness explicitly either by giving customer an isNull method or by using a type test for a marker interface.



© Copyright Martin Fowler, all rights reserved